|
1 /* $Id$ */ |
|
2 |
|
3 /****************************************************************************** |
|
4 * Cocoa video driver * |
|
5 * Known things left to do: * |
|
6 * Scale© the old pixel buffer to the new one when switching resolution. * |
|
7 ******************************************************************************/ |
|
8 |
|
9 #ifdef WITH_COCOA |
|
10 |
|
11 #include <AvailabilityMacros.h> |
|
12 |
|
13 #import <Cocoa/Cocoa.h> |
|
14 #import <sys/time.h> /* gettimeofday */ |
|
15 #import <sys/param.h> /* for MAXPATHLEN */ |
|
16 #import <unistd.h> |
|
17 |
|
18 /** |
|
19 * Important notice regarding all modifications!!!!!!! |
|
20 * There are certain limitations because the file is objective C++. |
|
21 * gdb has limitations. |
|
22 * C++ and objective C code can't be joined in all cases (classes stuff). |
|
23 * Read http://developer.apple.com/releasenotes/Cocoa/Objective-C++.html for more information. |
|
24 */ |
|
25 |
|
26 |
|
27 /* From Menus.h (according to Xcode Developer Documentation) */ |
|
28 extern "C" void ShowMenuBar(); |
|
29 extern "C" void HideMenuBar(); |
|
30 |
|
31 /* Defined in stdbool.h */ |
|
32 #ifndef __cplusplus |
|
33 # ifndef __BEOS__ |
|
34 # undef bool |
|
35 # undef false |
|
36 # undef true |
|
37 # endif |
|
38 #endif |
|
39 |
|
40 |
|
41 #include "../../stdafx.h" |
|
42 #include "../../openttd.h" |
|
43 #include "../../debug.h" |
|
44 #include "../../macros.h" |
|
45 #include "../../os/macosx/splash.h" |
|
46 #include "../../variables.h" |
|
47 #include "../../gfx.h" |
|
48 #include "cocoa_v.h" |
|
49 #include "cocoa_keys.h" |
|
50 #include "../../blitter/factory.hpp" |
|
51 #include "../../fileio.h" |
|
52 |
|
53 #undef Point |
|
54 #undef Rect |
|
55 |
|
56 |
|
57 /* Structure for rez switch gamma fades |
|
58 * We can hide the monitor flicker by setting the gamma tables to 0 |
|
59 */ |
|
60 #define QZ_GAMMA_TABLE_SIZE 256 |
|
61 |
|
62 struct OTTD_QuartzGammaTable { |
|
63 CGGammaValue red[QZ_GAMMA_TABLE_SIZE]; |
|
64 CGGammaValue green[QZ_GAMMA_TABLE_SIZE]; |
|
65 CGGammaValue blue[QZ_GAMMA_TABLE_SIZE]; |
|
66 }; |
|
67 |
|
68 /* Add methods to get at private members of NSScreen. |
|
69 * Since there is a bug in Apple's screen switching code that does not update |
|
70 * this variable when switching to fullscreen, we'll set it manually (but only |
|
71 * for the main screen). |
|
72 */ |
|
73 @interface NSScreen (NSScreenAccess) |
|
74 - (void) setFrame:(NSRect)frame; |
|
75 @end |
|
76 |
|
77 @implementation NSScreen (NSScreenAccess) |
|
78 - (void) setFrame:(NSRect)frame; |
|
79 { |
|
80 _frame = frame; |
|
81 } |
|
82 @end |
|
83 |
|
84 |
|
85 class FullscreenSubdriver: public CocoaSubdriver { |
|
86 int display_width; |
|
87 int display_height; |
|
88 int display_depth; |
|
89 int screen_pitch; |
|
90 void* screen_buffer; |
|
91 void* pixel_buffer; |
|
92 |
|
93 CGDirectDisplayID display_id; /* 0 == main display (only support single display) */ |
|
94 CFDictionaryRef cur_mode; /* current mode of the display */ |
|
95 CFDictionaryRef save_mode; /* original mode of the display */ |
|
96 CGDirectPaletteRef palette; /* palette of an 8-bit display */ |
|
97 |
|
98 #define MAX_DIRTY_RECTS 100 |
|
99 Rect dirty_rects[MAX_DIRTY_RECTS]; |
|
100 int num_dirty_rects; |
|
101 |
|
102 |
|
103 /* Gamma functions to try to hide the flash from a rez switch |
|
104 * Fade the display from normal to black |
|
105 * Save gamma tables for fade back to normal |
|
106 */ |
|
107 uint32 FadeGammaOut(OTTD_QuartzGammaTable* table) |
|
108 { |
|
109 CGGammaValue redTable[QZ_GAMMA_TABLE_SIZE]; |
|
110 CGGammaValue greenTable[QZ_GAMMA_TABLE_SIZE]; |
|
111 CGGammaValue blueTable[QZ_GAMMA_TABLE_SIZE]; |
|
112 float percent; |
|
113 int j; |
|
114 unsigned int actual; |
|
115 |
|
116 if (CGGetDisplayTransferByTable( |
|
117 display_id, QZ_GAMMA_TABLE_SIZE, |
|
118 table->red, table->green, table->blue, &actual |
|
119 ) != CGDisplayNoErr || |
|
120 actual != QZ_GAMMA_TABLE_SIZE) { |
|
121 return 1; |
|
122 } |
|
123 |
|
124 memcpy(redTable, table->red, sizeof(redTable)); |
|
125 memcpy(greenTable, table->green, sizeof(greenTable)); |
|
126 memcpy(blueTable, table->blue, sizeof(greenTable)); |
|
127 |
|
128 for (percent = 1.0; percent >= 0.0; percent -= 0.01) { |
|
129 for (j = 0; j < QZ_GAMMA_TABLE_SIZE; j++) { |
|
130 redTable[j] = redTable[j] * percent; |
|
131 greenTable[j] = greenTable[j] * percent; |
|
132 blueTable[j] = blueTable[j] * percent; |
|
133 } |
|
134 |
|
135 if (CGSetDisplayTransferByTable( |
|
136 display_id, QZ_GAMMA_TABLE_SIZE, |
|
137 redTable, greenTable, blueTable |
|
138 ) != CGDisplayNoErr) { |
|
139 CGDisplayRestoreColorSyncSettings(); |
|
140 return 1; |
|
141 } |
|
142 |
|
143 CSleep(10); |
|
144 } |
|
145 |
|
146 return 0; |
|
147 } |
|
148 |
|
149 /* Fade the display from black to normal |
|
150 * Restore previously saved gamma values |
|
151 */ |
|
152 uint32 FadeGammaIn(const OTTD_QuartzGammaTable* table) |
|
153 { |
|
154 CGGammaValue redTable[QZ_GAMMA_TABLE_SIZE]; |
|
155 CGGammaValue greenTable[QZ_GAMMA_TABLE_SIZE]; |
|
156 CGGammaValue blueTable[QZ_GAMMA_TABLE_SIZE]; |
|
157 float percent; |
|
158 int j; |
|
159 |
|
160 memset(redTable, 0, sizeof(redTable)); |
|
161 memset(greenTable, 0, sizeof(greenTable)); |
|
162 memset(blueTable, 0, sizeof(greenTable)); |
|
163 |
|
164 for (percent = 0.0; percent <= 1.0; percent += 0.01) { |
|
165 for (j = 0; j < QZ_GAMMA_TABLE_SIZE; j++) { |
|
166 redTable[j] = table->red[j] * percent; |
|
167 greenTable[j] = table->green[j] * percent; |
|
168 blueTable[j] = table->blue[j] * percent; |
|
169 } |
|
170 |
|
171 if (CGSetDisplayTransferByTable( |
|
172 display_id, QZ_GAMMA_TABLE_SIZE, |
|
173 redTable, greenTable, blueTable |
|
174 ) != CGDisplayNoErr) { |
|
175 CGDisplayRestoreColorSyncSettings(); |
|
176 return 1; |
|
177 } |
|
178 |
|
179 CSleep(10); |
|
180 } |
|
181 |
|
182 return 0; |
|
183 } |
|
184 |
|
185 /* Wait for the VBL to occur (estimated since we don't have a hardware interrupt) */ |
|
186 void WaitForVerticalBlank() |
|
187 { |
|
188 /* The VBL delay is based on Ian Ollmann's RezLib <iano@cco.caltech.edu> */ |
|
189 double refreshRate; |
|
190 double linesPerSecond; |
|
191 double target; |
|
192 double position; |
|
193 double adjustment; |
|
194 CFNumberRef refreshRateCFNumber; |
|
195 |
|
196 refreshRateCFNumber = (const __CFNumber*)CFDictionaryGetValue(cur_mode, kCGDisplayRefreshRate); |
|
197 if (refreshRateCFNumber == NULL) return; |
|
198 |
|
199 if (CFNumberGetValue(refreshRateCFNumber, kCFNumberDoubleType, &refreshRate) == 0) |
|
200 return; |
|
201 |
|
202 if (refreshRate == 0) return; |
|
203 |
|
204 linesPerSecond = refreshRate * display_height; |
|
205 target = display_height; |
|
206 |
|
207 /* Figure out the first delay so we start off about right */ |
|
208 position = CGDisplayBeamPosition(display_id); |
|
209 if (position > target) position = 0; |
|
210 |
|
211 adjustment = (target - position) / linesPerSecond; |
|
212 |
|
213 CSleep((uint32)(adjustment * 1000)); |
|
214 } |
|
215 |
|
216 |
|
217 bool SetVideoMode(int w, int h) |
|
218 { |
|
219 int exact_match; |
|
220 CFNumberRef number; |
|
221 int bpp; |
|
222 int gamma_error; |
|
223 OTTD_QuartzGammaTable gamma_table; |
|
224 NSRect screen_rect; |
|
225 CGError error; |
|
226 NSPoint pt; |
|
227 |
|
228 /* Destroy any previous mode */ |
|
229 if (pixel_buffer != NULL) { |
|
230 free(pixel_buffer); |
|
231 pixel_buffer = NULL; |
|
232 } |
|
233 |
|
234 /* See if requested mode exists */ |
|
235 cur_mode = CGDisplayBestModeForParameters(display_id, display_depth, w, h, &exact_match); |
|
236 |
|
237 /* If the mode wasn't an exact match, check if it has the right bpp, and update width and height */ |
|
238 if (!exact_match) { |
|
239 number = (const __CFNumber*) CFDictionaryGetValue(cur_mode, kCGDisplayBitsPerPixel); |
|
240 CFNumberGetValue(number, kCFNumberSInt32Type, &bpp); |
|
241 if (bpp != display_depth) { |
|
242 DEBUG(driver, 0, "Failed to find display resolution"); |
|
243 goto ERR_NO_MATCH; |
|
244 } |
|
245 |
|
246 number = (const __CFNumber*)CFDictionaryGetValue(cur_mode, kCGDisplayWidth); |
|
247 CFNumberGetValue(number, kCFNumberSInt32Type, &w); |
|
248 |
|
249 number = (const __CFNumber*)CFDictionaryGetValue(cur_mode, kCGDisplayHeight); |
|
250 CFNumberGetValue(number, kCFNumberSInt32Type, &h); |
|
251 } |
|
252 |
|
253 /* Fade display to zero gamma */ |
|
254 gamma_error = FadeGammaOut(&gamma_table); |
|
255 |
|
256 /* Put up the blanking window (a window above all other windows) */ |
|
257 error = CGDisplayCapture(display_id); |
|
258 |
|
259 if (CGDisplayNoErr != error) { |
|
260 DEBUG(driver, 0, "Failed capturing display"); |
|
261 goto ERR_NO_CAPTURE; |
|
262 } |
|
263 |
|
264 /* Do the physical switch */ |
|
265 if (CGDisplaySwitchToMode(display_id, cur_mode) != CGDisplayNoErr) { |
|
266 DEBUG(driver, 0, "Failed switching display resolution"); |
|
267 goto ERR_NO_SWITCH; |
|
268 } |
|
269 |
|
270 screen_buffer = CGDisplayBaseAddress(display_id); |
|
271 screen_pitch = CGDisplayBytesPerRow(display_id); |
|
272 |
|
273 display_width = CGDisplayPixelsWide(display_id); |
|
274 display_height = CGDisplayPixelsHigh(display_id); |
|
275 |
|
276 /* Setup double-buffer emulation */ |
|
277 pixel_buffer = malloc(display_width * display_height * display_depth / 8); |
|
278 if (pixel_buffer == NULL) { |
|
279 DEBUG(driver, 0, "Failed to allocate memory for double buffering"); |
|
280 goto ERR_DOUBLEBUF; |
|
281 } |
|
282 |
|
283 if (display_depth == 8 && !CGDisplayCanSetPalette(display_id)) { |
|
284 DEBUG(driver, 0, "Not an indexed display mode."); |
|
285 goto ERR_NOT_INDEXED; |
|
286 } |
|
287 |
|
288 /* If we don't hide menu bar, it will get events and interrupt the program */ |
|
289 HideMenuBar(); |
|
290 |
|
291 /* Fade the display to original gamma */ |
|
292 if (!gamma_error) FadeGammaIn(&gamma_table); |
|
293 |
|
294 /* There is a bug in Cocoa where NSScreen doesn't synchronize |
|
295 * with CGDirectDisplay, so the main screen's frame is wrong. |
|
296 * As a result, coordinate translation produces incorrect results. |
|
297 * We can hack around this bug by setting the screen rect ourselves. |
|
298 * This hack should be removed if/when the bug is fixed. |
|
299 */ |
|
300 screen_rect = NSMakeRect(0, 0, display_width, display_height); |
|
301 [ [ NSScreen mainScreen ] setFrame:screen_rect ]; |
|
302 |
|
303 |
|
304 pt = [ NSEvent mouseLocation ]; |
|
305 pt.y = display_height - pt.y; |
|
306 if (MouseIsInsideView(&pt)) QZ_HideMouse(); |
|
307 |
|
308 UpdatePalette(0, 256); |
|
309 |
|
310 return true; |
|
311 |
|
312 /* Since the blanking window covers *all* windows (even force quit) correct recovery is crucial */ |
|
313 ERR_NOT_INDEXED: |
|
314 free(pixel_buffer); |
|
315 pixel_buffer = NULL; |
|
316 ERR_DOUBLEBUF: |
|
317 CGDisplaySwitchToMode(display_id, save_mode); |
|
318 ERR_NO_SWITCH: |
|
319 CGReleaseAllDisplays(); |
|
320 ERR_NO_CAPTURE: |
|
321 if (!gamma_error) FadeGammaIn(&gamma_table); |
|
322 ERR_NO_MATCH: |
|
323 display_width = 0; |
|
324 display_height = 0; |
|
325 |
|
326 return false; |
|
327 } |
|
328 |
|
329 void RestoreVideoMode() |
|
330 { |
|
331 /* Release fullscreen resources */ |
|
332 OTTD_QuartzGammaTable gamma_table; |
|
333 int gamma_error; |
|
334 NSRect screen_rect; |
|
335 |
|
336 gamma_error = FadeGammaOut(&gamma_table); |
|
337 |
|
338 /* Restore original screen resolution/bpp */ |
|
339 CGDisplaySwitchToMode(display_id, save_mode); |
|
340 CGReleaseAllDisplays(); |
|
341 ShowMenuBar(); |
|
342 /* Reset the main screen's rectangle |
|
343 * See comment in SetVideoMode for why we do this |
|
344 */ |
|
345 screen_rect = NSMakeRect(0, 0, CGDisplayPixelsWide(display_id), CGDisplayPixelsHigh(display_id)); |
|
346 [ [ NSScreen mainScreen ] setFrame:screen_rect ]; |
|
347 |
|
348 QZ_ShowMouse(); |
|
349 |
|
350 /* Destroy the pixel buffer */ |
|
351 if (pixel_buffer != NULL) { |
|
352 free(pixel_buffer); |
|
353 pixel_buffer = NULL; |
|
354 } |
|
355 |
|
356 if (!gamma_error) FadeGammaIn(&gamma_table); |
|
357 |
|
358 display_width = 0; |
|
359 display_height = 0; |
|
360 } |
|
361 |
|
362 public: |
|
363 FullscreenSubdriver(int bpp) |
|
364 { |
|
365 if (bpp != 8 && bpp != 32) { |
|
366 error("Cocoa: This video driver only supports 8 and 32 bpp blitters."); |
|
367 } |
|
368 |
|
369 /* Initialize the video settings; this data persists between mode switches */ |
|
370 display_id = kCGDirectMainDisplay; |
|
371 save_mode = CGDisplayCurrentMode(display_id); |
|
372 |
|
373 if (bpp == 8) palette = CGPaletteCreateDefaultColorPalette(); |
|
374 |
|
375 display_width = 0; |
|
376 display_height = 0; |
|
377 display_depth = bpp; |
|
378 pixel_buffer = NULL; |
|
379 |
|
380 num_dirty_rects = MAX_DIRTY_RECTS; |
|
381 } |
|
382 |
|
383 virtual ~FullscreenSubdriver() |
|
384 { |
|
385 RestoreVideoMode(); |
|
386 } |
|
387 |
|
388 virtual void Draw() |
|
389 { |
|
390 const uint8* src = (uint8*) pixel_buffer; |
|
391 uint8* dst = (uint8*) screen_buffer; |
|
392 uint pitch = screen_pitch; |
|
393 uint width = display_width; |
|
394 uint num_dirty = num_dirty_rects; |
|
395 uint bytesperpixel = display_depth / 8; |
|
396 uint i; |
|
397 |
|
398 /* Check if we need to do anything */ |
|
399 if (num_dirty == 0) return; |
|
400 |
|
401 if (num_dirty >= MAX_DIRTY_RECTS) { |
|
402 num_dirty = 1; |
|
403 dirty_rects[0].left = 0; |
|
404 dirty_rects[0].top = 0; |
|
405 dirty_rects[0].right = display_width; |
|
406 dirty_rects[0].bottom = display_height; |
|
407 } |
|
408 |
|
409 WaitForVerticalBlank(); |
|
410 /* Build the region of dirty rectangles */ |
|
411 for (i = 0; i < num_dirty; i++) { |
|
412 uint y = dirty_rects[i].top; |
|
413 uint left = dirty_rects[i].left; |
|
414 uint length = dirty_rects[i].right - left; |
|
415 uint bottom = dirty_rects[i].bottom; |
|
416 |
|
417 for (; y < bottom; y++) { |
|
418 memcpy(dst + y * pitch + left * bytesperpixel, src + y * width * bytesperpixel + left * bytesperpixel, length * bytesperpixel); |
|
419 } |
|
420 } |
|
421 |
|
422 num_dirty_rects = 0; |
|
423 } |
|
424 |
|
425 virtual void MakeDirty(int left, int top, int width, int height) |
|
426 { |
|
427 if (num_dirty_rects < MAX_DIRTY_RECTS) { |
|
428 dirty_rects[num_dirty_rects].left = left; |
|
429 dirty_rects[num_dirty_rects].top = top; |
|
430 dirty_rects[num_dirty_rects].right = left + width; |
|
431 dirty_rects[num_dirty_rects].bottom = top + height; |
|
432 } |
|
433 num_dirty_rects++; |
|
434 } |
|
435 |
|
436 virtual void UpdatePalette(uint first_color, uint num_colors) |
|
437 { |
|
438 CGTableCount index; |
|
439 CGDeviceColor color; |
|
440 |
|
441 if (display_depth != 8) |
|
442 return; |
|
443 |
|
444 for (index = first_color; index < first_color+num_colors; index++) { |
|
445 /* Clamp colors between 0.0 and 1.0 */ |
|
446 color.red = _cur_palette[index].r / 255.0; |
|
447 color.blue = _cur_palette[index].b / 255.0; |
|
448 color.green = _cur_palette[index].g / 255.0; |
|
449 |
|
450 CGPaletteSetColorAtIndex(palette, color, index); |
|
451 } |
|
452 |
|
453 CGDisplaySetPalette(display_id, palette); |
|
454 } |
|
455 |
|
456 virtual uint ListModes(OTTDPoint* modes, uint max_modes) |
|
457 { |
|
458 CFArrayRef mode_list; |
|
459 CFIndex num_modes; |
|
460 CFIndex i; |
|
461 uint count = 0; |
|
462 |
|
463 mode_list = CGDisplayAvailableModes(display_id); |
|
464 num_modes = CFArrayGetCount(mode_list); |
|
465 |
|
466 /* Build list of modes with the requested bpp */ |
|
467 for (i = 0; i < num_modes && count < max_modes; i++) { |
|
468 CFDictionaryRef onemode; |
|
469 CFNumberRef number; |
|
470 int bpp; |
|
471 int intvalue; |
|
472 bool hasMode; |
|
473 uint16 width, height; |
|
474 |
|
475 onemode = (const __CFDictionary*)CFArrayGetValueAtIndex(mode_list, i); |
|
476 number = (const __CFNumber*)CFDictionaryGetValue(onemode, kCGDisplayBitsPerPixel); |
|
477 CFNumberGetValue (number, kCFNumberSInt32Type, &bpp); |
|
478 |
|
479 if (bpp != display_depth) continue; |
|
480 |
|
481 number = (const __CFNumber*)CFDictionaryGetValue(onemode, kCGDisplayWidth); |
|
482 CFNumberGetValue(number, kCFNumberSInt32Type, &intvalue); |
|
483 width = (uint16)intvalue; |
|
484 |
|
485 number = (const __CFNumber*)CFDictionaryGetValue(onemode, kCGDisplayHeight); |
|
486 CFNumberGetValue(number, kCFNumberSInt32Type, &intvalue); |
|
487 height = (uint16)intvalue; |
|
488 |
|
489 /* Check if mode is already in the list */ |
|
490 { |
|
491 uint i; |
|
492 hasMode = false; |
|
493 for (i = 0; i < count; i++) { |
|
494 if (modes[i].x == width && modes[i].y == height) { |
|
495 hasMode = true; |
|
496 break; |
|
497 } |
|
498 } |
|
499 } |
|
500 |
|
501 if (hasMode) continue; |
|
502 |
|
503 /* Add mode to the list */ |
|
504 modes[count].x = width; |
|
505 modes[count].y = height; |
|
506 count++; |
|
507 } |
|
508 |
|
509 /* Sort list smallest to largest */ |
|
510 { |
|
511 uint i, j; |
|
512 for (i = 0; i < count; i++) { |
|
513 for (j = 0; j < count-1; j++) { |
|
514 if (modes[j].x > modes[j + 1].x || ( |
|
515 modes[j].x == modes[j + 1].x && |
|
516 modes[j].y > modes[j + 1].y |
|
517 )) { |
|
518 uint tmpw = modes[j].x; |
|
519 uint tmph = modes[j].y; |
|
520 |
|
521 modes[j].x = modes[j + 1].x; |
|
522 modes[j].y = modes[j + 1].y; |
|
523 |
|
524 modes[j + 1].x = tmpw; |
|
525 modes[j + 1].y = tmph; |
|
526 } |
|
527 } |
|
528 } |
|
529 } |
|
530 |
|
531 return count; |
|
532 } |
|
533 |
|
534 virtual bool ChangeResolution(int w, int h) |
|
535 { |
|
536 int old_width = display_width; |
|
537 int old_height = display_height; |
|
538 |
|
539 if (SetVideoMode(w, h)) |
|
540 return true; |
|
541 |
|
542 if (old_width != 0 && old_height != 0) |
|
543 SetVideoMode(old_width, old_height); |
|
544 |
|
545 return false; |
|
546 } |
|
547 |
|
548 virtual bool IsFullscreen() |
|
549 { |
|
550 return true; |
|
551 } |
|
552 |
|
553 virtual int GetWidth() |
|
554 { |
|
555 return display_width; |
|
556 } |
|
557 |
|
558 virtual int GetHeight() |
|
559 { |
|
560 return display_height; |
|
561 } |
|
562 |
|
563 virtual void *GetPixelBuffer() |
|
564 { |
|
565 return pixel_buffer; |
|
566 } |
|
567 |
|
568 /* |
|
569 Convert local coordinate to window server (CoreGraphics) coordinate. |
|
570 In fullscreen mode this just means copying the coords. |
|
571 */ |
|
572 virtual CGPoint PrivateLocalToCG(NSPoint* p) |
|
573 { |
|
574 CGPoint cgp; |
|
575 |
|
576 cgp.x = p->x; |
|
577 cgp.y = p->y; |
|
578 |
|
579 return cgp; |
|
580 } |
|
581 |
|
582 virtual NSPoint GetMouseLocation(NSEvent *event) |
|
583 { |
|
584 NSPoint pt; |
|
585 |
|
586 pt = [ NSEvent mouseLocation ]; |
|
587 pt.y = display_height - pt.y; |
|
588 |
|
589 return pt; |
|
590 } |
|
591 |
|
592 virtual bool MouseIsInsideView(NSPoint *pt) |
|
593 { |
|
594 return pt->x >= 0 && pt->y >= 0 && pt->x < display_width && pt->y < display_height; |
|
595 } |
|
596 |
|
597 virtual bool IsActive() |
|
598 { |
|
599 return true; |
|
600 } |
|
601 }; |
|
602 |
|
603 CocoaSubdriver *QZ_CreateFullscreenSubdriver(int width, int height, int bpp) |
|
604 { |
|
605 FullscreenSubdriver *ret; |
|
606 |
|
607 ret = new FullscreenSubdriver(bpp); |
|
608 |
|
609 if (!ret->ChangeResolution(width, height)) { |
|
610 delete ret; |
|
611 return NULL; |
|
612 } |
|
613 |
|
614 return ret; |
|
615 } |
|
616 |
|
617 #endif /* WITH_COCOA */ |