|
1 /* $Id$ */ |
|
2 |
|
3 /****************************************************************************** |
|
4 * Cocoa video driver * |
|
5 * Known things left to do: * |
|
6 * List available resolutions. * |
|
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 /* Defined in stdbool.h */ |
|
28 #ifndef __cplusplus |
|
29 # ifndef __BEOS__ |
|
30 # undef bool |
|
31 # undef false |
|
32 # undef true |
|
33 # endif |
|
34 #endif |
|
35 |
|
36 |
|
37 #include "../../stdafx.h" |
|
38 #include "../../openttd.h" |
|
39 #include "../../debug.h" |
|
40 #include "../../macros.h" |
|
41 #include "../../os/macosx/splash.h" |
|
42 #include "../../variables.h" |
|
43 #include "../../gfx.h" |
|
44 #include "cocoa_v.h" |
|
45 #include "cocoa_keys.h" |
|
46 #include "../../blitter/factory.hpp" |
|
47 #include "../../fileio.h" |
|
48 |
|
49 #undef Point |
|
50 #undef Rect |
|
51 |
|
52 |
|
53 class WindowQuartzSubdriver; |
|
54 |
|
55 |
|
56 /* Subclass of NSWindow to fix genie effect and support resize events */ |
|
57 @interface OTTD_QuartzWindow : NSWindow { |
|
58 WindowQuartzSubdriver *driver; |
|
59 } |
|
60 |
|
61 - (void)setDriver:(WindowQuartzSubdriver*)drv; |
|
62 |
|
63 - (void)miniaturize:(id)sender; |
|
64 - (void)display; |
|
65 - (void)setFrame:(NSRect)frameRect display:(BOOL)flag; |
|
66 - (void)appDidHide:(NSNotification*)note; |
|
67 - (void)appWillUnhide:(NSNotification*)note; |
|
68 - (void)appDidUnhide:(NSNotification*)note; |
|
69 - (id)initWithContentRect:(NSRect)contentRect styleMask:(unsigned int)styleMask backing:(NSBackingStoreType)backingType defer:(BOOL)flag; |
|
70 @end |
|
71 |
|
72 /* Delegate for our NSWindow to send ask for quit on close */ |
|
73 @interface OTTD_QuartzWindowDelegate : NSObject{ |
|
74 WindowQuartzSubdriver *driver; |
|
75 } |
|
76 |
|
77 - (void)setDriver:(WindowQuartzSubdriver*)drv; |
|
78 |
|
79 - (BOOL)windowShouldClose:(id)sender; |
|
80 @end |
|
81 |
|
82 /* Subclass of NSView to fix Quartz rendering */ |
|
83 @interface OTTD_QuartzView : NSView { |
|
84 WindowQuartzSubdriver *driver; |
|
85 } |
|
86 |
|
87 - (void)setDriver:(WindowQuartzSubdriver*)drv; |
|
88 |
|
89 - (void)drawRect:(NSRect)rect; |
|
90 - (BOOL)isOpaque; |
|
91 @end |
|
92 |
|
93 class WindowQuartzSubdriver: public CocoaSubdriver { |
|
94 int device_width; |
|
95 int device_height; |
|
96 |
|
97 int window_width; |
|
98 int window_height; |
|
99 |
|
100 int buffer_depth; |
|
101 |
|
102 void* pixel_buffer; |
|
103 void* image_buffer; |
|
104 |
|
105 OTTD_QuartzWindow *window; |
|
106 |
|
107 #define MAX_DIRTY_RECTS 100 |
|
108 Rect dirty_rects[MAX_DIRTY_RECTS]; |
|
109 int num_dirty_rects; |
|
110 |
|
111 uint32 palette[256]; |
|
112 |
|
113 public: |
|
114 bool active; |
|
115 bool setup; |
|
116 |
|
117 OTTD_QuartzView* qzview; |
|
118 CGContextRef cgcontext; |
|
119 |
|
120 private: |
|
121 void GetDeviceInfo(); |
|
122 |
|
123 bool SetVideoMode(int width, int height); |
|
124 |
|
125 /** |
|
126 * This function copies 8bpp pixels from the screen buffer in 32bpp windowed mode. |
|
127 * |
|
128 * @param left The x coord for the left edge of the box to blit. |
|
129 * @param top The y coord for the top edge of the box to blit. |
|
130 * @param right The x coord for the right edge of the box to blit. |
|
131 * @param bottom The y coord for the bottom edge of the box to blit. |
|
132 */ |
|
133 void BlitIndexedToView32(int left, int top, int right, int bottom); |
|
134 |
|
135 public: |
|
136 WindowQuartzSubdriver(int bpp); |
|
137 virtual ~WindowQuartzSubdriver(); |
|
138 |
|
139 virtual void Draw(); |
|
140 virtual void MakeDirty(int left, int top, int width, int height); |
|
141 virtual void UpdatePalette(uint first_color, uint num_colors); |
|
142 |
|
143 virtual uint ListModes(OTTDPoint* modes, uint max_modes); |
|
144 |
|
145 virtual bool ChangeResolution(int w, int h); |
|
146 |
|
147 virtual bool IsFullscreen() { return false; } |
|
148 |
|
149 virtual int GetWidth() { return window_width; } |
|
150 virtual int GetHeight() { return window_height; } |
|
151 virtual void *GetPixelBuffer() { return buffer_depth == 8 ? pixel_buffer : image_buffer; } |
|
152 |
|
153 /* Convert local coordinate to window server (CoreGraphics) coordinate */ |
|
154 virtual CGPoint PrivateLocalToCG(NSPoint* p); |
|
155 |
|
156 virtual NSPoint GetMouseLocation(NSEvent *event); |
|
157 virtual bool MouseIsInsideView(NSPoint *pt); |
|
158 |
|
159 virtual bool IsActive() { return active; } |
|
160 |
|
161 |
|
162 void SetPortAlphaOpaque(); |
|
163 bool WindowResized(); |
|
164 }; |
|
165 |
|
166 |
|
167 static CGColorSpaceRef QZ_GetCorrectColorSpace() |
|
168 { |
|
169 static CGColorSpaceRef colorSpace = NULL; |
|
170 |
|
171 if (colorSpace == NULL) |
|
172 { |
|
173 CMProfileRef sysProfile; |
|
174 |
|
175 if (CMGetSystemProfile(&sysProfile) == noErr) |
|
176 { |
|
177 colorSpace = CGColorSpaceCreateWithPlatformColorSpace(sysProfile); |
|
178 CMCloseProfile(sysProfile); |
|
179 } |
|
180 |
|
181 assert(colorSpace != NULL); |
|
182 } |
|
183 |
|
184 return colorSpace; |
|
185 } |
|
186 |
|
187 |
|
188 @implementation OTTD_QuartzWindow |
|
189 |
|
190 - (void)setDriver:(WindowQuartzSubdriver*)drv |
|
191 { |
|
192 driver = drv; |
|
193 } |
|
194 |
|
195 |
|
196 /* we override these methods to fix the miniaturize animation/dock icon bug */ |
|
197 - (void)miniaturize:(id)sender |
|
198 { |
|
199 /* make the alpha channel opaque so anim won't have holes in it */ |
|
200 driver->SetPortAlphaOpaque (); |
|
201 |
|
202 /* window is hidden now */ |
|
203 driver->active = false; |
|
204 |
|
205 QZ_ShowMouse(); |
|
206 |
|
207 [ super miniaturize:sender ]; |
|
208 } |
|
209 |
|
210 - (void)display |
|
211 { |
|
212 /* This method fires just before the window deminaturizes from the Dock. |
|
213 * We'll save the current visible surface, let the window manager redraw any |
|
214 * UI elements, and restore the surface. This way, no expose event |
|
215 * is required, and the deminiaturize works perfectly. |
|
216 */ |
|
217 |
|
218 driver->SetPortAlphaOpaque(); |
|
219 |
|
220 /* save current visible surface */ |
|
221 [ self cacheImageInRect:[ driver->qzview frame ] ]; |
|
222 |
|
223 /* let the window manager redraw controls, border, etc */ |
|
224 [ super display ]; |
|
225 |
|
226 /* restore visible surface */ |
|
227 [ self restoreCachedImage ]; |
|
228 |
|
229 /* window is visible again */ |
|
230 driver->active = true; |
|
231 } |
|
232 |
|
233 - (void)setFrame:(NSRect)frameRect display:(BOOL)flag |
|
234 { |
|
235 [ super setFrame:frameRect display:flag ]; |
|
236 |
|
237 /* Don't do anything if the window is currently being created */ |
|
238 if (driver->setup) return; |
|
239 |
|
240 if (!driver->WindowResized()) |
|
241 error("Cocoa: Failed to resize window."); |
|
242 } |
|
243 |
|
244 - (void)appDidHide:(NSNotification*)note |
|
245 { |
|
246 driver->active = false; |
|
247 } |
|
248 |
|
249 |
|
250 - (void)appWillUnhide:(NSNotification*)note |
|
251 { |
|
252 driver->SetPortAlphaOpaque (); |
|
253 |
|
254 /* save current visible surface */ |
|
255 [ self cacheImageInRect:[ driver->qzview frame ] ]; |
|
256 } |
|
257 |
|
258 - (void)appDidUnhide:(NSNotification*)note |
|
259 { |
|
260 /* restore cached image, since it may not be current, post expose event too */ |
|
261 [ self restoreCachedImage ]; |
|
262 |
|
263 driver->active = true; |
|
264 } |
|
265 |
|
266 |
|
267 - (id)initWithContentRect:(NSRect)contentRect styleMask:(unsigned int)styleMask backing:(NSBackingStoreType)backingType defer:(BOOL)flag |
|
268 { |
|
269 /* Make our window subclass receive these application notifications */ |
|
270 [ [ NSNotificationCenter defaultCenter ] addObserver:self |
|
271 selector:@selector(appDidHide:) name:NSApplicationDidHideNotification object:NSApp ]; |
|
272 |
|
273 [ [ NSNotificationCenter defaultCenter ] addObserver:self |
|
274 selector:@selector(appDidUnhide:) name:NSApplicationDidUnhideNotification object:NSApp ]; |
|
275 |
|
276 [ [ NSNotificationCenter defaultCenter ] addObserver:self |
|
277 selector:@selector(appWillUnhide:) name:NSApplicationWillUnhideNotification object:NSApp ]; |
|
278 |
|
279 return [ super initWithContentRect:contentRect styleMask:styleMask backing:backingType defer:flag ]; |
|
280 } |
|
281 |
|
282 @end |
|
283 |
|
284 @implementation OTTD_QuartzWindowDelegate |
|
285 |
|
286 - (void)setDriver:(WindowQuartzSubdriver*)drv |
|
287 { |
|
288 driver = drv; |
|
289 } |
|
290 |
|
291 - (BOOL)windowShouldClose:(id)sender |
|
292 { |
|
293 HandleExitGameRequest(); |
|
294 |
|
295 return NO; |
|
296 } |
|
297 |
|
298 - (void)windowDidBecomeKey:(NSNotification*)aNotification |
|
299 { |
|
300 driver->active = true; |
|
301 } |
|
302 |
|
303 - (void)windowDidResignKey:(NSNotification*)aNotification |
|
304 { |
|
305 driver->active = false; |
|
306 } |
|
307 |
|
308 - (void)windowDidBecomeMain:(NSNotification*)aNotification |
|
309 { |
|
310 driver->active = true; |
|
311 } |
|
312 |
|
313 - (void)windowDidResignMain:(NSNotification*)aNotification |
|
314 { |
|
315 driver->active = false; |
|
316 } |
|
317 |
|
318 @end |
|
319 |
|
320 @implementation OTTD_QuartzView |
|
321 |
|
322 - (void)setDriver:(WindowQuartzSubdriver*)drv |
|
323 { |
|
324 driver = drv; |
|
325 } |
|
326 |
|
327 |
|
328 - (BOOL)isOpaque |
|
329 { |
|
330 return YES; |
|
331 } |
|
332 |
|
333 - (void)drawRect:(NSRect)invalidRect |
|
334 { |
|
335 CGImageRef fullImage; |
|
336 CGImageRef clippedImage; |
|
337 NSRect rect; |
|
338 const NSRect* dirtyRects; |
|
339 int dirtyRectCount; |
|
340 int n; |
|
341 CGRect clipRect; |
|
342 CGRect blitRect; |
|
343 uint32 blitArea = 0; |
|
344 NSRect frameRect = [ self frame ]; |
|
345 CGContextRef viewContext = (CGContextRef)[ [ NSGraphicsContext currentContext ] graphicsPort ]; |
|
346 |
|
347 if (driver->cgcontext == NULL) return; |
|
348 |
|
349 CGContextSetShouldAntialias(viewContext, FALSE); |
|
350 CGContextSetInterpolationQuality(viewContext, kCGInterpolationNone); |
|
351 |
|
352 /* The obtained 'rect' is actually a union of all dirty rects, let's ask for an explicit list of rects instead */ |
|
353 [ self getRectsBeingDrawn:&dirtyRects count:&dirtyRectCount ]; |
|
354 |
|
355 /* We need an Image in order to do blitting, but as we don't touch the context between this call and drawing no copying will actually be done here */ |
|
356 fullImage = CGBitmapContextCreateImage(driver->cgcontext); |
|
357 |
|
358 /* Calculate total area we are blitting */ |
|
359 for (n = 0; n < dirtyRectCount; n++) { |
|
360 blitArea += dirtyRects[n].size.width * dirtyRects[n].size.height; |
|
361 } |
|
362 |
|
363 /* |
|
364 * This might be completely stupid, but in my extremely subjective opinion it feels faster |
|
365 * The point is, if we're blitting less than 50% of the dirty rect union then it's still a good idea to blit each dirty |
|
366 * rect separately but if we blit more than that, it's just cheaper to blit the entire union in one pass. |
|
367 * Feel free to remove or find an even better value than 50% ... / blackis |
|
368 */ |
|
369 if (blitArea / (float)(invalidRect.size.width * invalidRect.size.height) > 0.5f) { |
|
370 rect = invalidRect; |
|
371 |
|
372 blitRect.origin.x = rect.origin.x; |
|
373 blitRect.origin.y = rect.origin.y; |
|
374 blitRect.size.width = rect.size.width; |
|
375 blitRect.size.height = rect.size.height; |
|
376 |
|
377 clipRect.origin.x = rect.origin.x; |
|
378 clipRect.origin.y = frameRect.size.height - rect.origin.y - rect.size.height; |
|
379 |
|
380 clipRect.size.width = rect.size.width; |
|
381 clipRect.size.height = rect.size.height; |
|
382 |
|
383 /* Blit dirty part of image */ |
|
384 clippedImage = CGImageCreateWithImageInRect(fullImage, clipRect); |
|
385 CGContextDrawImage(viewContext, blitRect, clippedImage); |
|
386 CGImageRelease(clippedImage); |
|
387 } else { |
|
388 for (n = 0; n < dirtyRectCount; n++) { |
|
389 rect = dirtyRects[n]; |
|
390 |
|
391 blitRect.origin.x = rect.origin.x; |
|
392 blitRect.origin.y = rect.origin.y; |
|
393 blitRect.size.width = rect.size.width; |
|
394 blitRect.size.height = rect.size.height; |
|
395 |
|
396 clipRect.origin.x = rect.origin.x; |
|
397 clipRect.origin.y = frameRect.size.height - rect.origin.y - rect.size.height; |
|
398 |
|
399 clipRect.size.width = rect.size.width; |
|
400 clipRect.size.height = rect.size.height; |
|
401 |
|
402 /* Blit dirty part of image */ |
|
403 clippedImage = CGImageCreateWithImageInRect(fullImage, clipRect); |
|
404 CGContextDrawImage(viewContext, blitRect, clippedImage); |
|
405 CGImageRelease(clippedImage); |
|
406 } |
|
407 } |
|
408 |
|
409 CGImageRelease(fullImage); |
|
410 } |
|
411 |
|
412 @end |
|
413 |
|
414 |
|
415 extern const char _openttd_revision[]; |
|
416 |
|
417 |
|
418 void WindowQuartzSubdriver::GetDeviceInfo() |
|
419 { |
|
420 CFDictionaryRef cur_mode; |
|
421 |
|
422 /* Initialize the video settings; this data persists between mode switches */ |
|
423 cur_mode = CGDisplayCurrentMode(kCGDirectMainDisplay); |
|
424 |
|
425 /* Gather some information that is useful to know about the display */ |
|
426 CFNumberGetValue( |
|
427 (const __CFNumber*)CFDictionaryGetValue(cur_mode, kCGDisplayWidth), |
|
428 kCFNumberSInt32Type, &device_width |
|
429 ); |
|
430 |
|
431 CFNumberGetValue( |
|
432 (const __CFNumber*)CFDictionaryGetValue(cur_mode, kCGDisplayHeight), |
|
433 kCFNumberSInt32Type, &device_height |
|
434 ); |
|
435 } |
|
436 |
|
437 bool WindowQuartzSubdriver::SetVideoMode(int width, int height) |
|
438 { |
|
439 char caption[50]; |
|
440 NSString *nsscaption; |
|
441 unsigned int style; |
|
442 NSRect contentRect; |
|
443 BOOL isCustom = NO; |
|
444 bool ret; |
|
445 |
|
446 setup = true; |
|
447 |
|
448 GetDeviceInfo(); |
|
449 |
|
450 if (width > device_width) |
|
451 width = device_width; |
|
452 if (height > device_height) |
|
453 height = device_height; |
|
454 |
|
455 contentRect = NSMakeRect(0, 0, width, height); |
|
456 |
|
457 /* Check if we should recreate the window */ |
|
458 if (window == nil) { |
|
459 OTTD_QuartzWindowDelegate *delegate; |
|
460 |
|
461 /* Set the window style */ |
|
462 style = NSTitledWindowMask; |
|
463 style |= (NSMiniaturizableWindowMask | NSClosableWindowMask); |
|
464 style |= NSResizableWindowMask; |
|
465 |
|
466 /* Manually create a window, avoids having a nib file resource */ |
|
467 window = [ [ OTTD_QuartzWindow alloc ] |
|
468 initWithContentRect: contentRect |
|
469 styleMask: style |
|
470 backing: NSBackingStoreBuffered |
|
471 defer: NO ]; |
|
472 |
|
473 if (window == nil) { |
|
474 DEBUG(driver, 0, "Could not create the Cocoa window."); |
|
475 setup = false; |
|
476 return false; |
|
477 } |
|
478 |
|
479 [ window setDriver:this ]; |
|
480 |
|
481 snprintf(caption, sizeof(caption), "OpenTTD %s", _openttd_revision); |
|
482 nsscaption = [ [ NSString alloc ] initWithCString:caption ]; |
|
483 [ window setTitle: nsscaption ]; |
|
484 [ window setMiniwindowTitle: nsscaption ]; |
|
485 [ nsscaption release ]; |
|
486 |
|
487 [ window setAcceptsMouseMovedEvents: YES ]; |
|
488 [ window setViewsNeedDisplay: NO ]; |
|
489 |
|
490 [ window useOptimizedDrawing: YES ]; |
|
491 |
|
492 delegate = [ [ OTTD_QuartzWindowDelegate alloc ] init ]; |
|
493 [ delegate setDriver: this ]; |
|
494 [ window setDelegate: [ delegate autorelease ] ]; |
|
495 } else { |
|
496 /* We already have a window, just change its size */ |
|
497 if (!isCustom) { |
|
498 [ window setContentSize: contentRect.size ]; |
|
499 |
|
500 // Ensure frame height - title bar height >= view height |
|
501 contentRect.size.height = Clamp(height, 0, [ window frame ].size.height - 22 /* 22 is the height of title bar of window*/); |
|
502 |
|
503 if (qzview != nil) { |
|
504 height = contentRect.size.height; |
|
505 [ qzview setFrameSize: contentRect.size ]; |
|
506 } |
|
507 } |
|
508 } |
|
509 |
|
510 window_width = width; |
|
511 window_height = height; |
|
512 |
|
513 [ window center ]; |
|
514 |
|
515 /* Only recreate the view if it doesn't already exist */ |
|
516 if (qzview == nil) { |
|
517 qzview = [ [ OTTD_QuartzView alloc ] initWithFrame: contentRect ]; |
|
518 if (qzview == nil) { |
|
519 DEBUG(driver, 0, "Could not create the Quickdraw view."); |
|
520 setup = false; |
|
521 return false; |
|
522 } |
|
523 |
|
524 [ qzview setDriver: this ]; |
|
525 |
|
526 [ qzview setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable ]; |
|
527 [ window setContentView: qzview ]; |
|
528 [ qzview release ]; |
|
529 [ window makeKeyAndOrderFront:nil ]; |
|
530 } |
|
531 |
|
532 ret = WindowResized(); |
|
533 |
|
534 UpdatePalette(0, 256); |
|
535 |
|
536 setup = false; |
|
537 |
|
538 return ret; |
|
539 } |
|
540 |
|
541 void WindowQuartzSubdriver::BlitIndexedToView32(int left, int top, int right, int bottom) |
|
542 { |
|
543 const uint32* pal = palette; |
|
544 const uint8* src = (uint8*)pixel_buffer; |
|
545 uint32* dst = (uint32*)image_buffer; |
|
546 uint width = window_width; |
|
547 uint pitch = window_width; |
|
548 int x; |
|
549 int y; |
|
550 |
|
551 for (y = top; y < bottom; y++) { |
|
552 for (x = left; x < right; x++) { |
|
553 dst[y * pitch + x] = pal[src[y * width + x]]; |
|
554 } |
|
555 } |
|
556 } |
|
557 |
|
558 |
|
559 WindowQuartzSubdriver::WindowQuartzSubdriver(int bpp) |
|
560 { |
|
561 window_width = 0; |
|
562 window_height = 0; |
|
563 buffer_depth = bpp; |
|
564 image_buffer = NULL; |
|
565 pixel_buffer = NULL; |
|
566 active = false; |
|
567 setup = false; |
|
568 |
|
569 window = nil; |
|
570 qzview = nil; |
|
571 |
|
572 cgcontext = NULL; |
|
573 |
|
574 num_dirty_rects = MAX_DIRTY_RECTS; |
|
575 } |
|
576 |
|
577 WindowQuartzSubdriver::~WindowQuartzSubdriver() |
|
578 { |
|
579 QZ_ShowMouse(); |
|
580 |
|
581 /* Release window mode resources */ |
|
582 if (window != nil) [ window close ]; |
|
583 |
|
584 CGContextRelease(cgcontext); |
|
585 |
|
586 free(image_buffer); |
|
587 free(pixel_buffer); |
|
588 } |
|
589 |
|
590 void WindowQuartzSubdriver::Draw() |
|
591 { |
|
592 int i; |
|
593 NSRect dirtyrect; |
|
594 |
|
595 /* Check if we need to do anything */ |
|
596 if (num_dirty_rects == 0 || |
|
597 [ window isMiniaturized ]) { |
|
598 return; |
|
599 } |
|
600 |
|
601 if (num_dirty_rects >= MAX_DIRTY_RECTS) { |
|
602 num_dirty_rects = 1; |
|
603 dirty_rects[0].left = 0; |
|
604 dirty_rects[0].top = 0; |
|
605 dirty_rects[0].right = window_width; |
|
606 dirty_rects[0].bottom = window_height; |
|
607 } |
|
608 |
|
609 /* Build the region of dirty rectangles */ |
|
610 for (i = 0; i < num_dirty_rects; i++) { |
|
611 /* We only need to blit in indexed mode since in 32bpp mode the game draws directly to the image. */ |
|
612 if(buffer_depth == 8) { |
|
613 BlitIndexedToView32( |
|
614 dirty_rects[i].left, |
|
615 dirty_rects[i].top, |
|
616 dirty_rects[i].right, |
|
617 dirty_rects[i].bottom |
|
618 ); |
|
619 } |
|
620 |
|
621 dirtyrect.origin.x = dirty_rects[i].left; |
|
622 dirtyrect.origin.y = window_height - dirty_rects[i].bottom; |
|
623 dirtyrect.size.width = dirty_rects[i].right - dirty_rects[i].left; |
|
624 dirtyrect.size.height = dirty_rects[i].bottom - dirty_rects[i].top; |
|
625 |
|
626 /* drawRect will be automatically called by Mac OS X during next update cycle, and then blitting will occur */ |
|
627 [ qzview setNeedsDisplayInRect: dirtyrect ]; |
|
628 } |
|
629 |
|
630 //DrawResizeIcon(); |
|
631 |
|
632 num_dirty_rects = 0; |
|
633 } |
|
634 |
|
635 void WindowQuartzSubdriver::MakeDirty(int left, int top, int width, int height) |
|
636 { |
|
637 if (num_dirty_rects < MAX_DIRTY_RECTS) { |
|
638 dirty_rects[num_dirty_rects].left = left; |
|
639 dirty_rects[num_dirty_rects].top = top; |
|
640 dirty_rects[num_dirty_rects].right = left + width; |
|
641 dirty_rects[num_dirty_rects].bottom = top + height; |
|
642 } |
|
643 num_dirty_rects++; |
|
644 } |
|
645 |
|
646 void WindowQuartzSubdriver::UpdatePalette(uint first_color, uint num_colors) |
|
647 { |
|
648 uint i; |
|
649 |
|
650 if (buffer_depth != 8) |
|
651 return; |
|
652 |
|
653 for (i = first_color; i < first_color + num_colors; i++) { |
|
654 uint32 clr = 0xff000000; |
|
655 clr |= (uint32)_cur_palette[i].r << 16; |
|
656 clr |= (uint32)_cur_palette[i].g << 8; |
|
657 clr |= (uint32)_cur_palette[i].b; |
|
658 palette[i] = clr; |
|
659 } |
|
660 |
|
661 num_dirty_rects = MAX_DIRTY_RECTS; |
|
662 } |
|
663 |
|
664 uint WindowQuartzSubdriver::ListModes(OTTDPoint* modes, uint max_modes) |
|
665 { |
|
666 if (max_modes == 0) return 0; |
|
667 |
|
668 modes[0].x = window_width; |
|
669 modes[0].y = window_height; |
|
670 |
|
671 return 1; |
|
672 } |
|
673 |
|
674 bool WindowQuartzSubdriver::ChangeResolution(int w, int h) |
|
675 { |
|
676 int old_width = window_width; |
|
677 int old_height = window_height; |
|
678 |
|
679 if (SetVideoMode(w, h)) |
|
680 return true; |
|
681 |
|
682 if (old_width != 0 && old_height != 0) |
|
683 SetVideoMode(old_width, old_height); |
|
684 |
|
685 return false; |
|
686 } |
|
687 |
|
688 /* Convert local coordinate to window server (CoreGraphics) coordinate */ |
|
689 CGPoint WindowQuartzSubdriver::PrivateLocalToCG(NSPoint* p) |
|
690 { |
|
691 CGPoint cgp; |
|
692 |
|
693 *p = [ qzview convertPoint:*p toView: nil ]; |
|
694 *p = [ window convertBaseToScreen:*p ]; |
|
695 p->y = window_height - p->y; |
|
696 |
|
697 cgp.x = p->x; |
|
698 cgp.y = p->y; |
|
699 |
|
700 return cgp; |
|
701 } |
|
702 |
|
703 NSPoint WindowQuartzSubdriver::GetMouseLocation(NSEvent *event) |
|
704 { |
|
705 NSPoint pt; |
|
706 |
|
707 pt = [ event locationInWindow ]; |
|
708 pt = [ qzview convertPoint:pt fromView:nil ]; |
|
709 |
|
710 pt.y = window_height - pt.y; |
|
711 |
|
712 return pt; |
|
713 } |
|
714 |
|
715 bool WindowQuartzSubdriver::MouseIsInsideView(NSPoint *pt) |
|
716 { |
|
717 return [ qzview mouse:*pt inRect:[ qzview bounds ] ]; |
|
718 } |
|
719 |
|
720 |
|
721 /* This function makes the *game region* of the window 100% opaque. |
|
722 * The genie effect uses the alpha component. Otherwise, |
|
723 * it doesn't seem to matter what value it has. |
|
724 */ |
|
725 void WindowQuartzSubdriver::SetPortAlphaOpaque() |
|
726 { |
|
727 uint32* pixels = (uint32*)image_buffer; |
|
728 uint32 pitch = window_width; |
|
729 int x, y; |
|
730 |
|
731 for (y = 0; y < window_height; y++) |
|
732 for (x = 0; x < window_width; x++) { |
|
733 pixels[y * pitch + x] |= 0xFF000000; |
|
734 } |
|
735 } |
|
736 |
|
737 bool WindowQuartzSubdriver::WindowResized() |
|
738 { |
|
739 if (window == nil || qzview == nil) return true; |
|
740 |
|
741 NSRect newframe = [ qzview frame ]; |
|
742 |
|
743 window_width = newframe.size.width; |
|
744 window_height = newframe.size.height; |
|
745 |
|
746 /* Create Core Graphics Context */ |
|
747 free(image_buffer); |
|
748 image_buffer = (uint32*)malloc(window_width * window_height * 4); |
|
749 |
|
750 CGContextRelease(cgcontext); |
|
751 cgcontext = CGBitmapContextCreate( |
|
752 image_buffer, // data |
|
753 window_width, // width |
|
754 window_height, // height |
|
755 8, // bits per component |
|
756 window_width * 4, // bytes per row |
|
757 QZ_GetCorrectColorSpace(), // color space |
|
758 kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host |
|
759 ); |
|
760 |
|
761 assert(cgcontext != NULL); |
|
762 CGContextSetShouldAntialias(cgcontext, FALSE); |
|
763 CGContextSetAllowsAntialiasing(cgcontext, FALSE); |
|
764 CGContextSetInterpolationQuality(cgcontext, kCGInterpolationNone); |
|
765 |
|
766 if (buffer_depth == 8) { |
|
767 free(pixel_buffer); |
|
768 pixel_buffer = malloc(window_width * window_height); |
|
769 if (pixel_buffer == NULL) { |
|
770 DEBUG(driver, 0, "Failed to allocate pixel buffer"); |
|
771 return false; |
|
772 } |
|
773 } |
|
774 |
|
775 QZ_GameSizeChanged(); |
|
776 |
|
777 /* Redraw screen */ |
|
778 num_dirty_rects = MAX_DIRTY_RECTS; |
|
779 |
|
780 return true; |
|
781 } |
|
782 |
|
783 |
|
784 CocoaSubdriver *QZ_CreateWindowQuartzSubdriver(int width, int height, int bpp) |
|
785 { |
|
786 WindowQuartzSubdriver *ret; |
|
787 |
|
788 if (bpp != 8 && bpp != 32) { |
|
789 DEBUG(driver, 0, "The cocoa quartz subdriver only supports 8 and 32 bpp."); |
|
790 return NULL; |
|
791 } |
|
792 |
|
793 ret = new WindowQuartzSubdriver(bpp); |
|
794 |
|
795 if (!ret->ChangeResolution(width, height)) { |
|
796 delete ret; |
|
797 return NULL; |
|
798 } |
|
799 |
|
800 return ret; |
|
801 } |
|
802 |
|
803 #endif /* WITH_COCOA */ |