e14743d1 |
1 | /* |
2 | SDL - Simple DirectMedia Layer |
3 | Copyright (C) 1997-2009 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 | |
27 | #ifdef __powerpc__ /* I'm gambling they fixed this by 10.4. --ryan. */ |
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 | static inline void QZ_SetFrame(NSScreen *nsscreen, NSRect frame) |
46 | { |
47 | [nsscreen setFrame:frame]; |
48 | } |
49 | #else |
50 | static inline void QZ_SetFrame(NSScreen *nsscreen, NSRect frame) |
51 | { |
52 | } |
53 | #endif |
54 | |
55 | @interface SDLTranslatorResponder : NSTextView |
56 | { |
57 | } |
58 | - (void) doCommandBySelector:(SEL)myselector; |
59 | @end |
60 | |
61 | @implementation SDLTranslatorResponder |
62 | - (void) doCommandBySelector:(SEL) myselector {} |
63 | @end |
64 | |
65 | /* absent in 10.3.9. */ |
66 | CG_EXTERN CGImageRef CGBitmapContextCreateImage (CGContextRef); |
67 | |
68 | /* Bootstrap functions */ |
69 | static int QZ_Available (); |
70 | static SDL_VideoDevice* QZ_CreateDevice (int device_index); |
71 | static void QZ_DeleteDevice (SDL_VideoDevice *device); |
72 | |
73 | /* Initialization, Query, Setup, and Redrawing functions */ |
74 | static int QZ_VideoInit (_THIS, SDL_PixelFormat *video_format); |
75 | |
76 | static SDL_Rect** QZ_ListModes (_THIS, SDL_PixelFormat *format, |
77 | Uint32 flags); |
78 | static void QZ_UnsetVideoMode (_THIS, BOOL to_desktop); |
79 | |
80 | static SDL_Surface* QZ_SetVideoMode (_THIS, SDL_Surface *current, |
81 | int width, int height, int bpp, |
82 | Uint32 flags); |
83 | static int QZ_ToggleFullScreen (_THIS, int on); |
84 | static int QZ_SetColors (_THIS, int first_color, |
85 | int num_colors, SDL_Color *colors); |
86 | |
87 | static int QZ_LockDoubleBuffer (_THIS, SDL_Surface *surface); |
88 | static void QZ_UnlockDoubleBuffer (_THIS, SDL_Surface *surface); |
89 | static int QZ_ThreadFlip (_THIS); |
90 | static int QZ_FlipDoubleBuffer (_THIS, SDL_Surface *surface); |
91 | static void QZ_DoubleBufferUpdate (_THIS, int num_rects, SDL_Rect *rects); |
92 | |
93 | static void QZ_DirectUpdate (_THIS, int num_rects, SDL_Rect *rects); |
94 | static void QZ_UpdateRects (_THIS, int num_rects, SDL_Rect *rects); |
95 | static void QZ_VideoQuit (_THIS); |
96 | |
97 | /* Hardware surface functions (for fullscreen mode only) */ |
98 | #if 0 /* Not used (apparently, it's really slow) */ |
99 | static int QZ_FillHWRect (_THIS, SDL_Surface *dst, SDL_Rect *rect, Uint32 color); |
100 | #endif |
101 | static int QZ_LockHWSurface(_THIS, SDL_Surface *surface); |
102 | static void QZ_UnlockHWSurface(_THIS, SDL_Surface *surface); |
103 | static int QZ_AllocHWSurface(_THIS, SDL_Surface *surface); |
104 | static void QZ_FreeHWSurface (_THIS, SDL_Surface *surface); |
105 | /* static int QZ_FlipHWSurface (_THIS, SDL_Surface *surface); */ |
106 | |
107 | /* Bootstrap binding, enables entry point into the driver */ |
108 | VideoBootStrap QZ_bootstrap = { |
109 | "Quartz", "Mac OS X CoreGraphics", QZ_Available, QZ_CreateDevice |
110 | }; |
111 | |
112 | |
113 | /* Bootstrap functions */ |
114 | static int QZ_Available () |
115 | { |
116 | return 1; |
117 | } |
118 | |
119 | static SDL_VideoDevice* QZ_CreateDevice (int device_index) |
120 | { |
121 | #pragma unused (device_index) |
122 | |
123 | SDL_VideoDevice *device; |
124 | SDL_PrivateVideoData *hidden; |
125 | |
126 | device = (SDL_VideoDevice*) SDL_malloc (sizeof (*device) ); |
127 | hidden = (SDL_PrivateVideoData*) SDL_malloc (sizeof (*hidden) ); |
128 | |
129 | if (device == NULL || hidden == NULL) |
130 | SDL_OutOfMemory (); |
131 | |
132 | SDL_memset (device, 0, sizeof (*device) ); |
133 | SDL_memset (hidden, 0, sizeof (*hidden) ); |
134 | |
135 | device->hidden = hidden; |
136 | |
137 | device->VideoInit = QZ_VideoInit; |
138 | device->ListModes = QZ_ListModes; |
139 | device->SetVideoMode = QZ_SetVideoMode; |
140 | device->ToggleFullScreen = QZ_ToggleFullScreen; |
141 | device->UpdateMouse = QZ_UpdateMouse; |
142 | device->SetColors = QZ_SetColors; |
143 | /* device->UpdateRects = QZ_UpdateRects; this is determined by SetVideoMode() */ |
144 | device->VideoQuit = QZ_VideoQuit; |
145 | |
146 | device->LockHWSurface = QZ_LockHWSurface; |
147 | device->UnlockHWSurface = QZ_UnlockHWSurface; |
148 | device->AllocHWSurface = QZ_AllocHWSurface; |
149 | device->FreeHWSurface = QZ_FreeHWSurface; |
150 | /* device->FlipHWSurface = QZ_FlipHWSurface */; |
151 | |
152 | device->SetGamma = QZ_SetGamma; |
153 | device->GetGamma = QZ_GetGamma; |
154 | device->SetGammaRamp = QZ_SetGammaRamp; |
155 | device->GetGammaRamp = QZ_GetGammaRamp; |
156 | |
157 | device->GL_GetProcAddress = QZ_GL_GetProcAddress; |
158 | device->GL_GetAttribute = QZ_GL_GetAttribute; |
159 | device->GL_MakeCurrent = QZ_GL_MakeCurrent; |
160 | device->GL_SwapBuffers = QZ_GL_SwapBuffers; |
161 | device->GL_LoadLibrary = QZ_GL_LoadLibrary; |
162 | |
163 | device->FreeWMCursor = QZ_FreeWMCursor; |
164 | device->CreateWMCursor = QZ_CreateWMCursor; |
165 | device->ShowWMCursor = QZ_ShowWMCursor; |
166 | device->WarpWMCursor = QZ_WarpWMCursor; |
167 | device->MoveWMCursor = QZ_MoveWMCursor; |
168 | device->CheckMouseMode = QZ_CheckMouseMode; |
169 | device->InitOSKeymap = QZ_InitOSKeymap; |
170 | device->PumpEvents = QZ_PumpEvents; |
171 | |
172 | device->SetCaption = QZ_SetCaption; |
173 | device->SetIcon = QZ_SetIcon; |
174 | device->IconifyWindow = QZ_IconifyWindow; |
175 | /*device->GetWMInfo = QZ_GetWMInfo;*/ |
176 | device->GrabInput = QZ_GrabInput; |
177 | |
178 | /* |
179 | * This is a big hassle, needing QuickDraw and QuickTime on older |
180 | * systems, and god knows what on 10.6, so we immediately fail here, |
181 | * which causes SDL to make an RGB surface and manage the YUV overlay |
182 | * in software. Sorry. Use SDL 1.3 if you want YUV rendering in a pixel |
183 | * shader. :) |
184 | */ |
185 | /*device->CreateYUVOverlay = QZ_CreateYUVOverlay;*/ |
186 | |
187 | device->free = QZ_DeleteDevice; |
188 | |
189 | return device; |
190 | } |
191 | |
192 | static void QZ_DeleteDevice (SDL_VideoDevice *device) |
193 | { |
194 | SDL_free (device->hidden); |
195 | SDL_free (device); |
196 | } |
197 | |
198 | static int QZ_VideoInit (_THIS, SDL_PixelFormat *video_format) |
199 | { |
200 | NSRect r = NSMakeRect(0.0, 0.0, 0.0, 0.0); |
201 | const char *env = NULL; |
202 | |
203 | /* Initialize the video settings; this data persists between mode switches */ |
204 | display_id = kCGDirectMainDisplay; |
205 | |
206 | #if 0 /* The mouse event code needs to take this into account... */ |
207 | env = getenv("SDL_VIDEO_FULLSCREEN_DISPLAY"); |
208 | if ( env ) { |
209 | int monitor = SDL_atoi(env); |
210 | CGDirectDisplayID activeDspys [3]; |
211 | CGDisplayCount dspyCnt; |
212 | CGGetActiveDisplayList (3, activeDspys, &dspyCnt); |
213 | if ( monitor >= 0 && monitor < dspyCnt ) { |
214 | display_id = activeDspys[monitor]; |
215 | } |
216 | } |
217 | #endif |
218 | |
219 | save_mode = CGDisplayCurrentMode (display_id); |
220 | mode_list = CGDisplayAvailableModes (display_id); |
221 | palette = CGPaletteCreateDefaultColorPalette (); |
222 | |
223 | /* Allow environment override of screensaver disable. */ |
224 | env = SDL_getenv("SDL_VIDEO_ALLOW_SCREENSAVER"); |
225 | if ( env ) { |
226 | allow_screensaver = SDL_atoi(env); |
227 | } else { |
228 | #ifdef SDL_VIDEO_DISABLE_SCREENSAVER |
229 | allow_screensaver = 0; |
230 | #else |
231 | allow_screensaver = 1; |
232 | #endif |
233 | } |
234 | |
235 | /* Gather some information that is useful to know about the display */ |
236 | CFNumberGetValue (CFDictionaryGetValue (save_mode, kCGDisplayBitsPerPixel), |
237 | kCFNumberSInt32Type, &device_bpp); |
238 | |
239 | CFNumberGetValue (CFDictionaryGetValue (save_mode, kCGDisplayWidth), |
240 | kCFNumberSInt32Type, &device_width); |
241 | |
242 | CFNumberGetValue (CFDictionaryGetValue (save_mode, kCGDisplayHeight), |
243 | kCFNumberSInt32Type, &device_height); |
244 | |
245 | /* Determine the current screen size */ |
246 | this->info.current_w = device_width; |
247 | this->info.current_h = device_height; |
248 | |
249 | /* Determine the default screen depth */ |
250 | video_format->BitsPerPixel = device_bpp; |
251 | |
252 | /* Set misc globals */ |
253 | 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 | if ( Gestalt(gestaltSystemVersion, &system_version) != noErr ) |
260 | system_version = 0; |
261 | |
262 | /* register for sleep notifications so wake from sleep generates SDL_VIDEOEXPOSE */ |
263 | QZ_RegisterForSleepNotifications (this); |
264 | |
265 | /* Fill in some window manager capabilities */ |
266 | this->info.wm_available = 1; |
267 | |
268 | return 0; |
269 | } |
270 | |
271 | static SDL_Rect** QZ_ListModes (_THIS, SDL_PixelFormat *format, Uint32 flags) |
272 | { |
273 | CFIndex num_modes; |
274 | CFIndex i; |
275 | |
276 | int list_size = 0; |
277 | |
278 | /* Any windowed mode is acceptable */ |
279 | if ( (flags & SDL_FULLSCREEN) == 0 ) |
280 | 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 | num_modes = CFArrayGetCount (mode_list); |
295 | |
296 | /* Build list of modes with the requested bpp */ |
297 | for (i = 0; i < num_modes; i++) { |
298 | |
299 | CFDictionaryRef onemode; |
300 | CFNumberRef number; |
301 | int bpp; |
302 | |
303 | onemode = CFArrayGetValueAtIndex (mode_list, i); |
304 | number = CFDictionaryGetValue (onemode, kCGDisplayBitsPerPixel); |
305 | CFNumberGetValue (number, kCFNumberSInt32Type, &bpp); |
306 | |
307 | if (bpp == format->BitsPerPixel) { |
308 | |
309 | int intvalue; |
310 | int hasMode; |
311 | int width, height; |
312 | |
313 | number = CFDictionaryGetValue (onemode, kCGDisplayWidth); |
314 | CFNumberGetValue (number, kCFNumberSInt32Type, &intvalue); |
315 | width = (Uint16) intvalue; |
316 | |
317 | number = CFDictionaryGetValue (onemode, kCGDisplayHeight); |
318 | CFNumberGetValue (number, kCFNumberSInt32Type, &intvalue); |
319 | height = (Uint16) intvalue; |
320 | |
321 | /* Check if mode is already in the list */ |
322 | { |
323 | int i; |
324 | hasMode = SDL_FALSE; |
325 | for (i = 0; i < list_size; i++) { |
326 | if (client_mode_list[i]->w == width && |
327 | client_mode_list[i]->h == height) { |
328 | hasMode = SDL_TRUE; |
329 | break; |
330 | } |
331 | } |
332 | } |
333 | |
334 | /* Grow the list and add mode to the list */ |
335 | if ( ! hasMode ) { |
336 | |
337 | SDL_Rect *rect; |
338 | |
339 | list_size++; |
340 | |
341 | if (client_mode_list == NULL) |
342 | 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 | SDL_OutOfMemory (); |
352 | return NULL; |
353 | } |
354 | |
355 | rect->x = rect->y = 0; |
356 | rect->w = width; |
357 | rect->h = height; |
358 | |
359 | client_mode_list[list_size-1] = rect; |
360 | client_mode_list[list_size] = NULL; |
361 | } |
362 | } |
363 | } |
364 | |
365 | /* Sort list largest to smallest (by area) */ |
366 | { |
367 | int i, j; |
368 | for (i = 0; i < list_size; i++) { |
369 | for (j = 0; j < list_size-1; j++) { |
370 | |
371 | int area1, area2; |
372 | area1 = client_mode_list[j]->w * client_mode_list[j]->h; |
373 | area2 = client_mode_list[j+1]->w * client_mode_list[j+1]->h; |
374 | |
375 | if (area1 < area2) { |
376 | SDL_Rect *tmp = client_mode_list[j]; |
377 | client_mode_list[j] = client_mode_list[j+1]; |
378 | client_mode_list[j+1] = tmp; |
379 | } |
380 | } |
381 | } |
382 | } |
383 | return client_mode_list; |
384 | } |
385 | |
386 | static SDL_bool QZ_WindowPosition(_THIS, int *x, int *y) |
387 | { |
388 | const char *window = getenv("SDL_VIDEO_WINDOW_POS"); |
389 | if ( window ) { |
390 | if ( sscanf(window, "%d,%d", x, y) == 2 ) { |
391 | return SDL_TRUE; |
392 | } |
393 | } |
394 | return SDL_FALSE; |
395 | } |
396 | |
397 | static void QZ_UnsetVideoMode (_THIS, BOOL to_desktop) |
398 | { |
399 | /* Reset values that may change between switches */ |
400 | this->info.blit_fill = 0; |
401 | this->FillHWRect = NULL; |
402 | this->UpdateRects = NULL; |
403 | this->LockHWSurface = NULL; |
404 | this->UnlockHWSurface = NULL; |
405 | |
406 | if (cg_context) { |
407 | CGContextFlush (cg_context); |
408 | CGContextRelease (cg_context); |
409 | cg_context = nil; |
410 | } |
411 | |
412 | /* Release fullscreen resources */ |
413 | if ( mode_flags & SDL_FULLSCREEN ) { |
414 | |
415 | NSRect screen_rect; |
416 | |
417 | /* Release double buffer stuff */ |
418 | if ( mode_flags & SDL_DOUBLEBUF) { |
419 | quit_thread = YES; |
420 | SDL_SemPost (sem1); |
421 | SDL_WaitThread (thread, NULL); |
422 | SDL_DestroySemaphore (sem1); |
423 | SDL_DestroySemaphore (sem2); |
424 | SDL_free (sw_buffers[0]); |
425 | } |
426 | |
427 | /* If we still have a valid window, close it. */ |
428 | if ( qz_window ) { |
429 | 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 */ |
430 | [ qz_window close ]; /* includes release because [qz_window isReleasedWhenClosed] */ |
431 | qz_window = nil; |
432 | window_view = nil; |
433 | } |
434 | /* |
435 | Release the OpenGL context |
436 | Do this first to avoid trash on the display before fade |
437 | */ |
438 | if ( mode_flags & SDL_OPENGL ) { |
439 | |
440 | QZ_TearDownOpenGL (this); |
441 | CGLSetFullScreen (NULL); |
442 | } |
443 | if (to_desktop) { |
444 | ShowMenuBar (); |
445 | /* Restore original screen resolution/bpp */ |
446 | CGDisplaySwitchToMode (display_id, save_mode); |
447 | CGReleaseAllDisplays (); |
448 | /* |
449 | Reset the main screen's rectangle |
450 | See comment in QZ_SetVideoFullscreen for why we do this |
451 | */ |
452 | 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 | id delegate = [ qz_window delegate ]; |
459 | [ qz_window close ]; /* includes release because [qz_window isReleasedWhenClosed] */ |
460 | if (delegate != nil) [ delegate release ]; |
461 | qz_window = nil; |
462 | window_view = nil; |
463 | |
464 | /* Release the OpenGL context */ |
465 | if ( mode_flags & SDL_OPENGL ) |
466 | QZ_TearDownOpenGL (this); |
467 | } |
468 | |
469 | /* Signal successful teardown */ |
470 | video_set = SDL_FALSE; |
471 | } |
472 | |
473 | static SDL_Surface* QZ_SetVideoFullScreen (_THIS, SDL_Surface *current, int width, |
474 | int height, int bpp, Uint32 flags) |
475 | { |
476 | boolean_t exact_match = 0; |
477 | NSRect screen_rect; |
478 | CGError error; |
479 | NSRect contentRect; |
480 | CGDisplayFadeReservationToken fade_token = kCGDisplayFadeReservationInvalidToken; |
481 | |
482 | /* Fade to black to hide resolution-switching flicker (and garbage |
483 | that is displayed by a destroyed OpenGL context, if applicable) */ |
484 | if ( CGAcquireDisplayFadeReservation (5, &fade_token) == kCGErrorSuccess ) { |
485 | CGDisplayFade (fade_token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, TRUE); |
486 | } |
487 | |
488 | /* Destroy any previous mode */ |
489 | if (video_set == SDL_TRUE) |
490 | QZ_UnsetVideoMode (this, FALSE); |
491 | |
492 | /* Sorry, QuickDraw was ripped out. */ |
493 | if (getenv("SDL_NSWindowPointer") || getenv("SDL_NSQuickDrawViewPointer")) { |
494 | SDL_SetError ("Embedded QuickDraw windows are no longer supported"); |
495 | goto ERR_NO_MATCH; |
496 | } |
497 | |
498 | /* See if requested mode exists */ |
499 | mode = CGDisplayBestModeForParameters (display_id, bpp, width, |
500 | height, &exact_match); |
501 | |
502 | /* Require an exact match to the requested mode */ |
503 | if ( ! exact_match ) { |
504 | SDL_SetError ("Failed to find display resolution: %dx%dx%d", width, height, bpp); |
505 | goto ERR_NO_MATCH; |
506 | } |
507 | |
508 | /* Put up the blanking window (a window above all other windows) */ |
509 | if (getenv ("SDL_SINGLEDISPLAY")) |
510 | error = CGDisplayCapture (display_id); |
511 | else |
512 | error = CGCaptureAllDisplays (); |
513 | |
514 | if ( CGDisplayNoErr != error ) { |
515 | SDL_SetError ("Failed capturing display"); |
516 | goto ERR_NO_CAPTURE; |
517 | } |
518 | |
519 | /* Do the physical switch */ |
520 | if ( CGDisplayNoErr != CGDisplaySwitchToMode (display_id, mode) ) { |
521 | SDL_SetError ("Failed switching display resolution"); |
522 | goto ERR_NO_SWITCH; |
523 | } |
524 | |
525 | current->pixels = (Uint32*) CGDisplayBaseAddress (display_id); |
526 | current->pitch = CGDisplayBytesPerRow (display_id); |
527 | |
528 | current->flags = 0; |
529 | current->w = width; |
530 | current->h = height; |
531 | current->flags |= SDL_FULLSCREEN; |
532 | current->flags |= SDL_HWSURFACE; |
533 | current->flags |= SDL_PREALLOC; |
534 | /* current->hwdata = (void *) CGDisplayGetDrawingContext (display_id); */ |
535 | |
536 | this->UpdateRects = QZ_DirectUpdate; |
537 | this->LockHWSurface = QZ_LockHWSurface; |
538 | this->UnlockHWSurface = QZ_UnlockHWSurface; |
539 | |
540 | /* Setup double-buffer emulation */ |
541 | if ( flags & SDL_DOUBLEBUF ) { |
542 | |
543 | /* |
544 | Setup a software backing store for reasonable results when |
545 | double buffering is requested (since a single-buffered hardware |
546 | surface looks hideous). |
547 | |
548 | The actual screen blit occurs in a separate thread to allow |
549 | other blitting while waiting on the VBL (and hence results in higher framerates). |
550 | */ |
551 | this->LockHWSurface = NULL; |
552 | this->UnlockHWSurface = NULL; |
553 | this->UpdateRects = NULL; |
554 | |
555 | current->flags |= (SDL_HWSURFACE|SDL_DOUBLEBUF); |
556 | this->UpdateRects = QZ_DoubleBufferUpdate; |
557 | this->LockHWSurface = QZ_LockDoubleBuffer; |
558 | this->UnlockHWSurface = QZ_UnlockDoubleBuffer; |
559 | this->FlipHWSurface = QZ_FlipDoubleBuffer; |
560 | |
561 | current->pixels = SDL_malloc (current->pitch * current->h * 2); |
562 | if (current->pixels == NULL) { |
563 | SDL_OutOfMemory (); |
564 | goto ERR_DOUBLEBUF; |
565 | } |
566 | |
567 | sw_buffers[0] = current->pixels; |
568 | sw_buffers[1] = (Uint8*)current->pixels + current->pitch * current->h; |
569 | |
570 | quit_thread = NO; |
571 | sem1 = SDL_CreateSemaphore (0); |
572 | sem2 = SDL_CreateSemaphore (1); |
573 | thread = SDL_CreateThread ((int (*)(void *))QZ_ThreadFlip, this); |
574 | } |
575 | |
576 | if ( CGDisplayCanSetPalette (display_id) ) |
577 | current->flags |= SDL_HWPALETTE; |
578 | |
579 | /* Check if we should recreate the window */ |
580 | if (qz_window == nil) { |
581 | /* Manually create a window, avoids having a nib file resource */ |
582 | qz_window = [ [ SDL_QuartzWindow alloc ] |
583 | initWithContentRect:contentRect |
584 | styleMask:0 |
585 | backing:NSBackingStoreBuffered |
586 | defer:NO ]; |
587 | |
588 | if (qz_window != nil) { |
589 | [ qz_window setAcceptsMouseMovedEvents:YES ]; |
590 | [ qz_window setViewsNeedDisplay:NO ]; |
591 | } |
592 | } |
593 | /* We already have a window, just change its size */ |
594 | else { |
595 | [ qz_window setContentSize:contentRect.size ]; |
596 | current->flags |= (SDL_NOFRAME|SDL_RESIZABLE) & mode_flags; |
597 | [ window_view setFrameSize:contentRect.size ]; |
598 | } |
599 | |
600 | /* Setup OpenGL for a fullscreen context */ |
601 | if (flags & SDL_OPENGL) { |
602 | |
603 | CGLError err; |
604 | CGLContextObj ctx; |
605 | |
606 | if ( ! QZ_SetupOpenGL (this, bpp, flags) ) { |
607 | goto ERR_NO_GL; |
608 | } |
609 | |
610 | /* Initialize the NSView and add it to our window. The presence of a valid window and |
611 | view allow the cursor to be changed whilst in fullscreen.*/ |
612 | window_view = [ [ NSView alloc ] initWithFrame:contentRect ]; |
613 | [ [ qz_window contentView ] addSubview:window_view ]; |
614 | [ window_view release ]; |
615 | |
616 | ctx = QZ_GetCGLContextObj (gl_context); |
617 | err = CGLSetFullScreen (ctx); |
618 | |
619 | if (err) { |
620 | SDL_SetError ("Error setting OpenGL fullscreen: %s", CGLErrorString(err)); |
621 | goto ERR_NO_GL; |
622 | } |
623 | |
624 | [ gl_context makeCurrentContext]; |
625 | |
626 | glClear (GL_COLOR_BUFFER_BIT); |
627 | |
628 | [ gl_context flushBuffer ]; |
629 | |
630 | current->flags |= SDL_OPENGL; |
631 | } |
632 | |
633 | /* If we don't hide menu bar, it will get events and interrupt the program */ |
634 | HideMenuBar (); |
635 | |
636 | /* Fade in again (asynchronously) */ |
637 | if ( fade_token != kCGDisplayFadeReservationInvalidToken ) { |
638 | CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE); |
639 | CGReleaseDisplayFadeReservation(fade_token); |
640 | } |
641 | |
642 | /* |
643 | There is a bug in Cocoa where NSScreen doesn't synchronize |
644 | with CGDirectDisplay, so the main screen's frame is wrong. |
645 | As a result, coordinate translation produces incorrect results. |
646 | We can hack around this bug by setting the screen rect |
647 | ourselves. This hack should be removed if/when the bug is fixed. |
648 | */ |
649 | screen_rect = NSMakeRect(0,0,width,height); |
650 | QZ_SetFrame([ NSScreen mainScreen ], screen_rect); |
651 | |
652 | /* Save the flags to ensure correct tear-down */ |
653 | mode_flags = current->flags; |
654 | |
655 | /* Set app state, hide cursor if necessary, ... */ |
656 | QZ_DoActivate(this); |
657 | |
658 | return current; |
659 | |
660 | /* Since the blanking window covers *all* windows (even force quit) correct recovery is crucial */ |
661 | ERR_NO_GL: |
662 | ERR_DOUBLEBUF: CGDisplaySwitchToMode (display_id, save_mode); |
663 | ERR_NO_SWITCH: CGReleaseAllDisplays (); |
664 | ERR_NO_CAPTURE: |
665 | ERR_NO_MATCH: if ( fade_token != kCGDisplayFadeReservationInvalidToken ) { |
666 | CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE); |
667 | CGReleaseDisplayFadeReservation (fade_token); |
668 | } |
669 | return NULL; |
670 | } |
671 | |
672 | static SDL_Surface* QZ_SetVideoWindowed (_THIS, SDL_Surface *current, int width, |
673 | int height, int *bpp, Uint32 flags) |
674 | { |
675 | unsigned int style; |
676 | NSRect contentRect; |
677 | int center_window = 1; |
678 | int origin_x, origin_y; |
679 | CGDisplayFadeReservationToken fade_token = kCGDisplayFadeReservationInvalidToken; |
680 | |
681 | current->flags = 0; |
682 | current->w = width; |
683 | current->h = height; |
684 | |
685 | contentRect = NSMakeRect (0, 0, width, height); |
686 | |
687 | /* |
688 | Check if we should completely destroy the previous mode |
689 | - If it is fullscreen |
690 | - If it has different noframe or resizable attribute |
691 | - If it is OpenGL (since gl attributes could be different) |
692 | - If new mode is OpenGL, but previous mode wasn't |
693 | */ |
694 | if (video_set == SDL_TRUE) { |
695 | if (mode_flags & SDL_FULLSCREEN) { |
696 | /* Fade to black to hide resolution-switching flicker (and garbage |
697 | that is displayed by a destroyed OpenGL context, if applicable) */ |
698 | if (CGAcquireDisplayFadeReservation (5, &fade_token) == kCGErrorSuccess) { |
699 | CGDisplayFade (fade_token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, TRUE); |
700 | } |
701 | QZ_UnsetVideoMode (this, TRUE); |
702 | } |
703 | else if ( ((mode_flags ^ flags) & (SDL_NOFRAME|SDL_RESIZABLE)) || |
704 | (mode_flags & SDL_OPENGL) || |
705 | (flags & SDL_OPENGL) ) { |
706 | QZ_UnsetVideoMode (this, TRUE); |
707 | } |
708 | } |
709 | |
710 | /* Sorry, QuickDraw was ripped out. */ |
711 | if (getenv("SDL_NSWindowPointer") || getenv("SDL_NSQuickDrawViewPointer")) { |
712 | SDL_SetError ("Embedded QuickDraw windows are no longer supported"); |
713 | if (fade_token != kCGDisplayFadeReservationInvalidToken) { |
714 | CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE); |
715 | CGReleaseDisplayFadeReservation (fade_token); |
716 | } |
717 | return NULL; |
718 | } |
719 | |
720 | /* Check if we should recreate the window */ |
721 | if (qz_window == nil) { |
722 | |
723 | /* Set the window style based on input flags */ |
724 | if ( flags & SDL_NOFRAME ) { |
725 | style = NSBorderlessWindowMask; |
726 | current->flags |= SDL_NOFRAME; |
727 | } else { |
728 | style = NSTitledWindowMask; |
729 | style |= (NSMiniaturizableWindowMask | NSClosableWindowMask); |
730 | if ( flags & SDL_RESIZABLE ) { |
731 | style |= NSResizableWindowMask; |
732 | current->flags |= SDL_RESIZABLE; |
733 | } |
734 | } |
735 | |
736 | /* Manually create a window, avoids having a nib file resource */ |
737 | qz_window = [ [ SDL_QuartzWindow alloc ] |
738 | initWithContentRect:contentRect |
739 | styleMask:style |
740 | backing:NSBackingStoreBuffered |
741 | defer:NO ]; |
742 | |
743 | if (qz_window == nil) { |
744 | SDL_SetError ("Could not create the Cocoa window"); |
745 | if (fade_token != kCGDisplayFadeReservationInvalidToken) { |
746 | CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE); |
747 | CGReleaseDisplayFadeReservation (fade_token); |
748 | } |
749 | return NULL; |
750 | } |
751 | |
752 | /*[ qz_window setReleasedWhenClosed:YES ];*/ /* no need to set this as it's the default for NSWindows */ |
753 | QZ_SetCaption(this, this->wm_title, this->wm_icon); |
754 | [ qz_window setAcceptsMouseMovedEvents:YES ]; |
755 | [ qz_window setViewsNeedDisplay:NO ]; |
756 | |
757 | if ( QZ_WindowPosition(this, &origin_x, &origin_y) ) { |
758 | /* have to flip the Y value (NSPoint is lower left corner origin) */ |
759 | [ qz_window setFrameTopLeftPoint:NSMakePoint((float) origin_x, (float) (this->info.current_h - origin_y))]; |
760 | center_window = 0; |
761 | } else if ( center_window ) { |
762 | [ qz_window center ]; |
763 | } |
764 | |
765 | [ qz_window setDelegate: |
766 | [ [ SDL_QuartzWindowDelegate alloc ] init ] ]; |
767 | [ qz_window setContentView: [ [ [ SDL_QuartzView alloc ] init ] autorelease ] ]; |
768 | } |
769 | /* We already have a window, just change its size */ |
770 | else { |
771 | [ qz_window setContentSize:contentRect.size ]; |
772 | current->flags |= (SDL_NOFRAME|SDL_RESIZABLE) & mode_flags; |
773 | [ window_view setFrameSize:contentRect.size ]; |
774 | } |
775 | |
776 | /* For OpenGL, we bind the context to a subview */ |
777 | if ( flags & SDL_OPENGL ) { |
778 | |
779 | if ( ! QZ_SetupOpenGL (this, *bpp, flags) ) { |
780 | if (fade_token != kCGDisplayFadeReservationInvalidToken) { |
781 | CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE); |
782 | CGReleaseDisplayFadeReservation (fade_token); |
783 | } |
784 | return NULL; |
785 | } |
786 | |
787 | window_view = [ [ NSView alloc ] initWithFrame:contentRect ]; |
788 | [ window_view setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable ]; |
789 | [ [ qz_window contentView ] addSubview:window_view ]; |
790 | [ gl_context setView: window_view ]; |
791 | [ window_view release ]; |
792 | [ gl_context makeCurrentContext]; |
793 | [ qz_window makeKeyAndOrderFront:nil ]; |
794 | current->flags |= SDL_OPENGL; |
795 | } |
796 | /* For 2D, we build a CGBitmapContext */ |
797 | else { |
798 | CGColorSpaceRef cgColorspace; |
799 | |
800 | /* Only recreate the view if it doesn't already exist */ |
801 | if (window_view == nil) { |
802 | |
803 | window_view = [ [ NSView alloc ] initWithFrame:contentRect ]; |
804 | [ window_view setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable ]; |
805 | [ [ qz_window contentView ] addSubview:window_view ]; |
806 | [ window_view release ]; |
807 | [ qz_window makeKeyAndOrderFront:nil ]; |
808 | } |
809 | |
810 | cgColorspace = CGColorSpaceCreateDeviceRGB(); |
811 | current->pitch = 4 * current->w; |
812 | current->pixels = SDL_malloc (current->h * current->pitch); |
813 | |
814 | cg_context = CGBitmapContextCreate (current->pixels, current->w, current->h, |
815 | 8, current->pitch, cgColorspace, |
816 | kCGImageAlphaNoneSkipFirst); |
817 | CGColorSpaceRelease (cgColorspace); |
818 | |
819 | current->flags |= SDL_SWSURFACE; |
820 | current->flags |= SDL_ASYNCBLIT; |
821 | current->hwdata = (void *) cg_context; |
822 | |
823 | this->UpdateRects = QZ_UpdateRects; |
824 | this->LockHWSurface = QZ_LockHWSurface; |
825 | this->UnlockHWSurface = QZ_UnlockHWSurface; |
826 | } |
827 | |
828 | /* Save flags to ensure correct teardown */ |
829 | mode_flags = current->flags; |
830 | |
831 | /* Fade in again (asynchronously) if we came from a fullscreen mode and faded to black */ |
832 | if (fade_token != kCGDisplayFadeReservationInvalidToken) { |
833 | CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE); |
834 | CGReleaseDisplayFadeReservation (fade_token); |
835 | } |
836 | |
837 | return current; |
838 | } |
839 | |
840 | static SDL_Surface* QZ_SetVideoMode (_THIS, SDL_Surface *current, int width, |
841 | int height, int bpp, Uint32 flags) |
842 | { |
843 | current->flags = 0; |
844 | current->pixels = NULL; |
845 | |
846 | /* Setup full screen video */ |
847 | if ( flags & SDL_FULLSCREEN ) { |
848 | current = QZ_SetVideoFullScreen (this, current, width, height, bpp, flags ); |
849 | if (current == NULL) |
850 | return NULL; |
851 | } |
852 | /* Setup windowed video */ |
853 | else { |
854 | /* Force bpp to 32 */ |
855 | bpp = 32; |
856 | current = QZ_SetVideoWindowed (this, current, width, height, &bpp, flags); |
857 | if (current == NULL) |
858 | return NULL; |
859 | } |
860 | |
861 | /* Setup the new pixel format */ |
862 | { |
863 | int amask = 0, |
864 | rmask = 0, |
865 | gmask = 0, |
866 | bmask = 0; |
867 | |
868 | switch (bpp) { |
869 | case 16: /* (1)-5-5-5 RGB */ |
870 | amask = 0; |
871 | rmask = 0x7C00; |
872 | gmask = 0x03E0; |
873 | bmask = 0x001F; |
874 | break; |
875 | case 24: |
876 | SDL_SetError ("24bpp is not available"); |
877 | return NULL; |
878 | case 32: /* (8)-8-8-8 ARGB */ |
879 | amask = 0x00000000; |
880 | if ( flags & SDL_FULLSCREEN ) |
881 | { |
882 | rmask = 0x00FF0000; |
883 | gmask = 0x0000FF00; |
884 | bmask = 0x000000FF; |
885 | } |
886 | else |
887 | { |
888 | #if SDL_BYTEORDER == SDL_LIL_ENDIAN |
889 | rmask = 0x0000FF00; |
890 | gmask = 0x00FF0000; |
891 | bmask = 0xFF000000; |
892 | #else |
893 | rmask = 0x00FF0000; |
894 | gmask = 0x0000FF00; |
895 | bmask = 0x000000FF; |
896 | #endif |
897 | } |
898 | break; |
899 | } |
900 | |
901 | if ( ! SDL_ReallocFormat (current, bpp, |
902 | rmask, gmask, bmask, amask ) ) { |
903 | SDL_SetError ("Couldn't reallocate pixel format"); |
904 | return NULL; |
905 | } |
906 | } |
907 | |
908 | /* Signal successful completion (used internally) */ |
909 | video_set = SDL_TRUE; |
910 | |
911 | return current; |
912 | } |
913 | |
914 | static int QZ_ToggleFullScreen (_THIS, int on) |
915 | { |
916 | return 0; |
917 | } |
918 | |
919 | static int QZ_SetColors (_THIS, int first_color, int num_colors, |
920 | SDL_Color *colors) |
921 | { |
922 | CGTableCount index; |
923 | CGDeviceColor color; |
924 | |
925 | for (index = first_color; index < first_color+num_colors; index++) { |
926 | |
927 | /* Clamp colors between 0.0 and 1.0 */ |
928 | color.red = colors->r / 255.0; |
929 | color.blue = colors->b / 255.0; |
930 | color.green = colors->g / 255.0; |
931 | |
932 | colors++; |
933 | |
934 | CGPaletteSetColorAtIndex (palette, color, index); |
935 | } |
936 | |
937 | if ( CGDisplayNoErr != CGDisplaySetPalette (display_id, palette) ) |
938 | return 0; |
939 | |
940 | return 1; |
941 | } |
942 | |
943 | static int QZ_LockDoubleBuffer (_THIS, SDL_Surface *surface) |
944 | { |
945 | return 1; |
946 | } |
947 | |
948 | static void QZ_UnlockDoubleBuffer (_THIS, SDL_Surface *surface) |
949 | { |
950 | } |
951 | |
952 | /* The VBL delay is based on code by Ian R Ollmann's RezLib <iano@cco.caltech.edu> */ |
953 | static AbsoluteTime QZ_SecondsToAbsolute ( double seconds ) |
954 | { |
955 | union |
956 | { |
957 | UInt64 i; |
958 | Nanoseconds ns; |
959 | } temp; |
960 | |
961 | temp.i = seconds * 1000000000.0; |
962 | |
963 | return NanosecondsToAbsolute ( temp.ns ); |
964 | } |
965 | |
966 | static int QZ_ThreadFlip (_THIS) |
967 | { |
968 | Uint8 *src, *dst; |
969 | int skip, len, h; |
970 | |
971 | /* |
972 | Give this thread the highest scheduling priority possible, |
973 | in the hopes that it will immediately run after the VBL delay |
974 | */ |
975 | { |
976 | pthread_t current_thread; |
977 | int policy; |
978 | struct sched_param param; |
979 | |
980 | current_thread = pthread_self (); |
981 | pthread_getschedparam (current_thread, &policy, ¶m); |
982 | policy = SCHED_RR; |
983 | param.sched_priority = sched_get_priority_max (policy); |
984 | pthread_setschedparam (current_thread, policy, ¶m); |
985 | } |
986 | |
987 | while (1) { |
988 | |
989 | SDL_SemWait (sem1); |
990 | if (quit_thread) |
991 | return 0; |
992 | |
993 | /* |
994 | * We have to add SDL_VideoSurface->offset here, since we might be a |
995 | * smaller surface in the center of the framebuffer (you asked for |
996 | * a fullscreen resolution smaller than the hardware could supply |
997 | * so SDL is centering it in a bigger resolution)... |
998 | */ |
999 | dst = (Uint8 *)CGDisplayBaseAddress (display_id) + SDL_VideoSurface->offset; |
1000 | src = current_buffer + SDL_VideoSurface->offset; |
1001 | len = SDL_VideoSurface->w * SDL_VideoSurface->format->BytesPerPixel; |
1002 | h = SDL_VideoSurface->h; |
1003 | skip = SDL_VideoSurface->pitch; |
1004 | |
1005 | /* Wait for the VBL to occur (estimated since we don't have a hardware interrupt) */ |
1006 | { |
1007 | |
1008 | /* The VBL delay is based on Ian Ollmann's RezLib <iano@cco.caltech.edu> */ |
1009 | double refreshRate; |
1010 | double linesPerSecond; |
1011 | double target; |
1012 | double position; |
1013 | double adjustment; |
1014 | AbsoluteTime nextTime; |
1015 | CFNumberRef refreshRateCFNumber; |
1016 | |
1017 | refreshRateCFNumber = CFDictionaryGetValue (mode, kCGDisplayRefreshRate); |
1018 | if ( NULL == refreshRateCFNumber ) { |
1019 | SDL_SetError ("Mode has no refresh rate"); |
1020 | goto ERROR; |
1021 | } |
1022 | |
1023 | if ( 0 == CFNumberGetValue (refreshRateCFNumber, kCFNumberDoubleType, &refreshRate) ) { |
1024 | SDL_SetError ("Error getting refresh rate"); |
1025 | goto ERROR; |
1026 | } |
1027 | |
1028 | if ( 0 == refreshRate ) { |
1029 | |
1030 | SDL_SetError ("Display has no refresh rate, using 60hz"); |
1031 | |
1032 | /* ok, for LCD's we'll emulate a 60hz refresh, which may or may not look right */ |
1033 | refreshRate = 60.0; |
1034 | } |
1035 | |
1036 | linesPerSecond = refreshRate * h; |
1037 | target = h; |
1038 | |
1039 | /* Figure out the first delay so we start off about right */ |
1040 | position = CGDisplayBeamPosition (display_id); |
1041 | if (position > target) |
1042 | position = 0; |
1043 | |
1044 | adjustment = (target - position) / linesPerSecond; |
1045 | |
1046 | nextTime = AddAbsoluteToAbsolute (UpTime (), QZ_SecondsToAbsolute (adjustment)); |
1047 | |
1048 | MPDelayUntil (&nextTime); |
1049 | } |
1050 | |
1051 | |
1052 | /* On error, skip VBL delay */ |
1053 | ERROR: |
1054 | |
1055 | /* TODO: use CGContextDrawImage here too! Create two CGContextRefs the same way we |
1056 | create two buffers, replace current_buffer with current_context and set it |
1057 | appropriately in QZ_FlipDoubleBuffer. */ |
1058 | while ( h-- ) { |
1059 | |
1060 | SDL_memcpy (dst, src, len); |
1061 | src += skip; |
1062 | dst += skip; |
1063 | } |
1064 | |
1065 | /* signal flip completion */ |
1066 | SDL_SemPost (sem2); |
1067 | } |
1068 | |
1069 | return 0; |
1070 | } |
1071 | |
1072 | static int QZ_FlipDoubleBuffer (_THIS, SDL_Surface *surface) |
1073 | { |
1074 | /* wait for previous flip to complete */ |
1075 | SDL_SemWait (sem2); |
1076 | |
1077 | current_buffer = surface->pixels; |
1078 | |
1079 | if (surface->pixels == sw_buffers[0]) |
1080 | surface->pixels = sw_buffers[1]; |
1081 | else |
1082 | surface->pixels = sw_buffers[0]; |
1083 | |
1084 | /* signal worker thread to do the flip */ |
1085 | SDL_SemPost (sem1); |
1086 | |
1087 | return 0; |
1088 | } |
1089 | |
1090 | static void QZ_DoubleBufferUpdate (_THIS, int num_rects, SDL_Rect *rects) |
1091 | { |
1092 | /* perform a flip if someone calls updaterects on a doublebuferred surface */ |
1093 | this->FlipHWSurface (this, SDL_VideoSurface); |
1094 | } |
1095 | |
1096 | static void QZ_DirectUpdate (_THIS, int num_rects, SDL_Rect *rects) |
1097 | { |
1098 | #pragma unused(this,num_rects,rects) |
1099 | } |
1100 | |
1101 | |
1102 | /* Resize icon, BMP format */ |
1103 | static const unsigned char QZ_ResizeIcon[] = { |
1104 | 0x42,0x4d,0x31,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x36,0x00,0x00,0x00,0x28,0x00, |
1105 | 0x00,0x00,0x0d,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x01,0x00,0x18,0x00,0x00,0x00, |
1106 | 0x00,0x00,0xfb,0x01,0x00,0x00,0x13,0x0b,0x00,0x00,0x13,0x0b,0x00,0x00,0x00,0x00, |
1107 | 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, |
1108 | 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, |
1109 | 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x0b,0xff,0xff, |
1110 | 0xff,0xda,0xda,0xda,0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xda,0xda,0xda, |
1111 | 0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xda,0xda,0xda,0x87,0x87,0x87,0xe8, |
1112 | 0xe8,0xe8,0xff,0xff,0xff,0x0b,0xff,0xff,0xff,0xff,0xff,0xff,0xda,0xda,0xda,0x87, |
1113 | 0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xda,0xda,0xda,0x87,0x87,0x87,0xe8,0xe8, |
1114 | 0xe8,0xff,0xff,0xff,0xda,0xda,0xda,0x87,0x87,0x87,0xff,0xff,0xff,0x0b,0xff,0xff, |
1115 | 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xd5,0xd5,0xd5,0x87,0x87,0x87,0xe8,0xe8,0xe8, |
1116 | 0xff,0xff,0xff,0xda,0xda,0xda,0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xda, |
1117 | 0xda,0xda,0xff,0xff,0xff,0x0b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, |
1118 | 0xff,0xff,0xd7,0xd7,0xd7,0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xda,0xda, |
1119 | 0xda,0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xff,0xff,0xff,0x0b,0xff,0xff, |
1120 | 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xd7,0xd7,0xd7, |
1121 | 0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xda,0xda,0xda,0x87,0x87,0x87,0xe8, |
1122 | 0xe8,0xe8,0xff,0xff,0xff,0x0b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, |
1123 | 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xd7,0xd7,0xd7,0x87,0x87,0x87,0xe8,0xe8, |
1124 | 0xe8,0xff,0xff,0xff,0xdc,0xdc,0xdc,0x87,0x87,0x87,0xff,0xff,0xff,0x0b,0xff,0xff, |
1125 | 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, |
1126 | 0xff,0xff,0xff,0xd9,0xd9,0xd9,0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xdc, |
1127 | 0xdc,0xdc,0xff,0xff,0xff,0x0b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, |
1128 | 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xdb,0xdb, |
1129 | 0xdb,0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xff,0xff,0xff,0x0b,0xff,0xff, |
1130 | 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, |
1131 | 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xdb,0xdb,0xdb,0x87,0x87,0x87,0xe8, |
1132 | 0xe8,0xe8,0xff,0xff,0xff,0x0b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, |
1133 | 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, |
1134 | 0xff,0xff,0xff,0xff,0xdc,0xdc,0xdc,0x87,0x87,0x87,0xff,0xff,0xff,0x0b,0xff,0xff, |
1135 | 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, |
1136 | 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xdc, |
1137 | 0xdc,0xdc,0xff,0xff,0xff,0x0b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, |
1138 | 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, |
1139 | 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x0b |
1140 | }; |
1141 | |
1142 | static void QZ_DrawResizeIcon (_THIS) |
1143 | { |
1144 | /* Check if we should draw the resize icon */ |
1145 | if (SDL_VideoSurface->flags & SDL_RESIZABLE) { |
1146 | |
1147 | SDL_Rect icon_rect; |
1148 | |
1149 | /* Create the icon image */ |
1150 | if (resize_icon == NULL) { |
1151 | |
1152 | SDL_RWops *rw; |
1153 | SDL_Surface *tmp; |
1154 | |
1155 | rw = SDL_RWFromConstMem (QZ_ResizeIcon, sizeof(QZ_ResizeIcon)); |
1156 | tmp = SDL_LoadBMP_RW (rw, SDL_TRUE); |
1157 | |
1158 | resize_icon = SDL_ConvertSurface (tmp, SDL_VideoSurface->format, SDL_SRCCOLORKEY); |
1159 | SDL_SetColorKey (resize_icon, SDL_SRCCOLORKEY, 0xFFFFFF); |
1160 | |
1161 | SDL_FreeSurface (tmp); |
1162 | } |
1163 | |
1164 | icon_rect.x = SDL_VideoSurface->w - 13; |
1165 | icon_rect.y = SDL_VideoSurface->h - 13; |
1166 | icon_rect.w = 13; |
1167 | icon_rect.h = 13; |
1168 | |
1169 | SDL_BlitSurface (resize_icon, NULL, SDL_VideoSurface, &icon_rect); |
1170 | } |
1171 | } |
1172 | |
1173 | static void QZ_UpdateRects (_THIS, int numRects, SDL_Rect *rects) |
1174 | { |
1175 | if (SDL_VideoSurface->flags & SDL_OPENGLBLIT) { |
1176 | QZ_GL_SwapBuffers (this); |
1177 | } |
1178 | else if ( [ qz_window isMiniaturized ] ) { |
1179 | |
1180 | /* Do nothing if miniaturized */ |
1181 | } |
1182 | |
1183 | else { |
1184 | CGContextRef cgc = (CGContextRef) |
1185 | [[NSGraphicsContext graphicsContextWithWindow: qz_window] |
1186 | graphicsPort]; |
1187 | QZ_DrawResizeIcon (this); |
1188 | CGContextFlush (cg_context); |
1189 | CGImageRef image = CGBitmapContextCreateImage (cg_context); |
1190 | CGRect rectangle = CGRectMake (0,0,[window_view frame].size.width,[window_view frame].size.height); |
1191 | |
1192 | CGContextDrawImage (cgc, rectangle, image); |
1193 | CGImageRelease(image); |
1194 | CGContextFlush (cgc); |
1195 | CGContextRelease (cgc); |
1196 | } |
1197 | } |
1198 | |
1199 | static void QZ_VideoQuit (_THIS) |
1200 | { |
1201 | CGDisplayFadeReservationToken fade_token = kCGDisplayFadeReservationInvalidToken; |
1202 | |
1203 | /* Restore gamma settings */ |
1204 | CGDisplayRestoreColorSyncSettings (); |
1205 | |
1206 | /* Ensure the cursor will be visible and working when we quit */ |
1207 | CGDisplayShowCursor (display_id); |
1208 | CGAssociateMouseAndMouseCursorPosition (1); |
1209 | |
1210 | if (mode_flags & SDL_FULLSCREEN) { |
1211 | /* Fade to black to hide resolution-switching flicker (and garbage |
1212 | that is displayed by a destroyed OpenGL context, if applicable) */ |
1213 | if (CGAcquireDisplayFadeReservation (5, &fade_token) == kCGErrorSuccess) { |
1214 | CGDisplayFade (fade_token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, TRUE); |
1215 | } |
1216 | QZ_UnsetVideoMode (this, TRUE); |
1217 | if (fade_token != kCGDisplayFadeReservationInvalidToken) { |
1218 | CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE); |
1219 | CGReleaseDisplayFadeReservation (fade_token); |
1220 | } |
1221 | } |
1222 | else |
1223 | QZ_UnsetVideoMode (this, TRUE); |
1224 | |
1225 | CGPaletteRelease (palette); |
1226 | |
1227 | if (opengl_library) { |
1228 | SDL_UnloadObject(opengl_library); |
1229 | opengl_library = NULL; |
1230 | } |
1231 | this->gl_config.driver_loaded = 0; |
1232 | |
1233 | if (field_edit) { |
1234 | [field_edit release]; |
1235 | field_edit = NULL; |
1236 | } |
1237 | } |
1238 | |
1239 | #if 0 /* Not used (apparently, it's really slow) */ |
1240 | static int QZ_FillHWRect (_THIS, SDL_Surface *dst, SDL_Rect *rect, Uint32 color) |
1241 | { |
1242 | CGSDisplayHWFill (display_id, rect->x, rect->y, rect->w, rect->h, color); |
1243 | |
1244 | return 0; |
1245 | } |
1246 | #endif |
1247 | |
1248 | static int QZ_LockHWSurface(_THIS, SDL_Surface *surface) |
1249 | { |
1250 | return 1; |
1251 | } |
1252 | |
1253 | static void QZ_UnlockHWSurface(_THIS, SDL_Surface *surface) |
1254 | { |
1255 | } |
1256 | |
1257 | static int QZ_AllocHWSurface(_THIS, SDL_Surface *surface) |
1258 | { |
1259 | return(-1); /* unallowed (no HWSURFACE support here). */ |
1260 | } |
1261 | |
1262 | static void QZ_FreeHWSurface (_THIS, SDL_Surface *surface) |
1263 | { |
1264 | } |
1265 | |
1266 | /* |
1267 | int QZ_FlipHWSurface (_THIS, SDL_Surface *surface) { |
1268 | return 0; |
1269 | } |
1270 | */ |
1271 | |
1272 | /* Gamma functions */ |
1273 | int QZ_SetGamma (_THIS, float red, float green, float blue) |
1274 | { |
1275 | const CGGammaValue min = 0.0, max = 1.0; |
1276 | |
1277 | if (red == 0.0) |
1278 | red = FLT_MAX; |
1279 | else |
1280 | red = 1.0 / red; |
1281 | |
1282 | if (green == 0.0) |
1283 | green = FLT_MAX; |
1284 | else |
1285 | green = 1.0 / green; |
1286 | |
1287 | if (blue == 0.0) |
1288 | blue = FLT_MAX; |
1289 | else |
1290 | blue = 1.0 / blue; |
1291 | |
1292 | if ( CGDisplayNoErr == CGSetDisplayTransferByFormula |
1293 | (display_id, min, max, red, min, max, green, min, max, blue) ) { |
1294 | |
1295 | return 0; |
1296 | } |
1297 | else { |
1298 | |
1299 | return -1; |
1300 | } |
1301 | } |
1302 | |
1303 | int QZ_GetGamma (_THIS, float *red, float *green, float *blue) |
1304 | { |
1305 | CGGammaValue dummy; |
1306 | if ( CGDisplayNoErr == CGGetDisplayTransferByFormula |
1307 | (display_id, &dummy, &dummy, red, |
1308 | &dummy, &dummy, green, &dummy, &dummy, blue) ) |
1309 | |
1310 | return 0; |
1311 | else |
1312 | return -1; |
1313 | } |
1314 | |
1315 | int QZ_SetGammaRamp (_THIS, Uint16 *ramp) |
1316 | { |
1317 | const CGTableCount tableSize = 255; |
1318 | CGGammaValue redTable[tableSize]; |
1319 | CGGammaValue greenTable[tableSize]; |
1320 | CGGammaValue blueTable[tableSize]; |
1321 | |
1322 | int i; |
1323 | |
1324 | /* Extract gamma values into separate tables, convert to floats between 0.0 and 1.0 */ |
1325 | for (i = 0; i < 256; i++) |
1326 | redTable[i % 256] = ramp[i] / 65535.0; |
1327 | |
1328 | for (i=256; i < 512; i++) |
1329 | greenTable[i % 256] = ramp[i] / 65535.0; |
1330 | |
1331 | for (i=512; i < 768; i++) |
1332 | blueTable[i % 256] = ramp[i] / 65535.0; |
1333 | |
1334 | if ( CGDisplayNoErr == CGSetDisplayTransferByTable |
1335 | (display_id, tableSize, redTable, greenTable, blueTable) ) |
1336 | return 0; |
1337 | else |
1338 | return -1; |
1339 | } |
1340 | |
1341 | int QZ_GetGammaRamp (_THIS, Uint16 *ramp) |
1342 | { |
1343 | const CGTableCount tableSize = 255; |
1344 | CGGammaValue redTable[tableSize]; |
1345 | CGGammaValue greenTable[tableSize]; |
1346 | CGGammaValue blueTable[tableSize]; |
1347 | CGTableCount actual; |
1348 | int i; |
1349 | |
1350 | if ( CGDisplayNoErr != CGGetDisplayTransferByTable |
1351 | (display_id, tableSize, redTable, greenTable, blueTable, &actual) || |
1352 | actual != tableSize) |
1353 | |
1354 | return -1; |
1355 | |
1356 | /* Pack tables into one array, with values from 0 to 65535 */ |
1357 | for (i = 0; i < 256; i++) |
1358 | ramp[i] = redTable[i % 256] * 65535.0; |
1359 | |
1360 | for (i=256; i < 512; i++) |
1361 | ramp[i] = greenTable[i % 256] * 65535.0; |
1362 | |
1363 | for (i=512; i < 768; i++) |
1364 | ramp[i] = blueTable[i % 256] * 65535.0; |
1365 | |
1366 | return 0; |
1367 | } |
1368 | |