Fix T38452: on OS X 10.9, now always use Lion style fullscreen.
[blender.git] / intern / ghost / intern / GHOST_WindowCocoa.mm
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  *
23  * Contributor(s):      Maarten Gribnau 05/2001
24                                         Damien Plisson 10/2009
25  *
26  * ***** END GPL LICENSE BLOCK *****
27  */
28
29 #include <Cocoa/Cocoa.h>
30
31 #if MAC_OS_X_VERSION_MIN_REQUIRED <= 1050
32 //Use of the SetSystemUIMode function (64bit compatible)
33 #include <Carbon/Carbon.h>
34 #endif
35
36 #include <OpenGL/gl.h>
37 #include <OpenGL/CGLRenderers.h>
38 /***** Multithreaded opengl code : uncomment for enabling
39 #include <OpenGL/OpenGL.h>
40 */
41
42  
43 #include "GHOST_WindowCocoa.h"
44 #include "GHOST_SystemCocoa.h"
45 #include "GHOST_Debug.h"
46
47 #if MAC_OS_X_VERSION_MAX_ALLOWED < 1070
48 /* Lion style fullscreen support when building with the 10.6 SDK */
49 enum {
50         NSWindowCollectionBehaviorFullScreenPrimary = 1 << 7,
51         NSFullScreenWindowMask = 1 << 14
52 };
53 #endif
54
55 #pragma mark Cocoa window delegate object
56
57 @interface CocoaWindowDelegate : NSObject
58 <NSWindowDelegate>
59 {
60         GHOST_SystemCocoa *systemCocoa;
61         GHOST_WindowCocoa *associatedWindow;
62 }
63
64 - (void)setSystemAndWindowCocoa:(GHOST_SystemCocoa *)sysCocoa windowCocoa:(GHOST_WindowCocoa *)winCocoa;
65 - (void)windowDidBecomeKey:(NSNotification *)notification;
66 - (void)windowDidResignKey:(NSNotification *)notification;
67 - (void)windowDidExpose:(NSNotification *)notification;
68 - (void)windowDidResize:(NSNotification *)notification;
69 - (void)windowDidMove:(NSNotification *)notification;
70 - (void)windowWillMove:(NSNotification *)notification;
71 - (BOOL)windowShouldClose:(id)sender;   
72 - (void)windowDidChangeBackingProperties:(NSNotification *)notification;
73 @end
74
75
76 @implementation CocoaWindowDelegate : NSObject
77 - (void)setSystemAndWindowCocoa:(GHOST_SystemCocoa *)sysCocoa windowCocoa:(GHOST_WindowCocoa *)winCocoa
78 {
79         systemCocoa = sysCocoa;
80         associatedWindow = winCocoa;
81 }
82
83 - (void)windowDidBecomeKey:(NSNotification *)notification
84 {
85         systemCocoa->handleWindowEvent(GHOST_kEventWindowActivate, associatedWindow);
86 }
87
88 - (void)windowDidResignKey:(NSNotification *)notification
89 {
90         systemCocoa->handleWindowEvent(GHOST_kEventWindowDeactivate, associatedWindow);
91 }
92
93 - (void)windowDidExpose:(NSNotification *)notification
94 {
95         systemCocoa->handleWindowEvent(GHOST_kEventWindowUpdate, associatedWindow);
96 }
97
98 - (void)windowDidMove:(NSNotification *)notification
99 {
100         systemCocoa->handleWindowEvent(GHOST_kEventWindowMove, associatedWindow);
101 }
102
103 - (void)windowWillMove:(NSNotification *)notification
104 {
105         systemCocoa->handleWindowEvent(GHOST_kEventWindowMove, associatedWindow);
106 }
107
108 - (void)windowDidResize:(NSNotification *)notification
109 {
110 #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
111         //if (![[notification object] inLiveResize]) {
112                 //Send event only once, at end of resize operation (when user has released mouse button)
113 #endif
114                 systemCocoa->handleWindowEvent(GHOST_kEventWindowSize, associatedWindow);
115 #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
116         //}
117 #endif
118         /* Live resize, send event, gets handled in wm_window.c. Needed because live resize runs in a modal loop, not letting main loop run */
119          if ([[notification object] inLiveResize]) {
120                 systemCocoa->dispatchEvents();
121         }
122 }
123
124 - (void)windowDidChangeBackingProperties:(NSNotification *)notification
125 {
126         systemCocoa->handleWindowEvent(GHOST_kEventNativeResolutionChange, associatedWindow);
127 }
128
129 - (BOOL)windowShouldClose:(id)sender;
130 {
131         //Let Blender close the window rather than closing immediately
132         systemCocoa->handleWindowEvent(GHOST_kEventWindowClose, associatedWindow);
133         return false;
134 }
135
136 @end
137
138 #pragma mark NSWindow subclass
139 //We need to subclass it to tell that even borderless (fullscreen), it can become key (receive user events)
140 @interface CocoaWindow: NSWindow
141 {
142         GHOST_SystemCocoa *systemCocoa;
143         GHOST_WindowCocoa *associatedWindow;
144         GHOST_TDragnDropTypes m_draggedObjectType;
145 }
146 - (void)setSystemAndWindowCocoa:(GHOST_SystemCocoa *)sysCocoa windowCocoa:(GHOST_WindowCocoa *)winCocoa;
147 - (GHOST_SystemCocoa*)systemCocoa;
148 @end
149 @implementation CocoaWindow
150 - (void)setSystemAndWindowCocoa:(GHOST_SystemCocoa *)sysCocoa windowCocoa:(GHOST_WindowCocoa *)winCocoa
151 {
152         systemCocoa = sysCocoa;
153         associatedWindow = winCocoa;
154 }
155 - (GHOST_SystemCocoa*)systemCocoa
156 {
157         return systemCocoa;
158 }
159
160 -(BOOL)canBecomeKeyWindow
161 {
162         return YES;
163 }
164
165 //The drag'n'drop dragging destination methods
166 - (NSDragOperation)draggingEntered:(id < NSDraggingInfo >)sender
167 {
168         NSPoint mouseLocation = [sender draggingLocation];
169         NSPasteboard *draggingPBoard = [sender draggingPasteboard];
170         
171         if ([[draggingPBoard types] containsObject:NSTIFFPboardType]) m_draggedObjectType = GHOST_kDragnDropTypeBitmap;
172         else if ([[draggingPBoard types] containsObject:NSFilenamesPboardType]) m_draggedObjectType = GHOST_kDragnDropTypeFilenames;
173         else if ([[draggingPBoard types] containsObject:NSStringPboardType]) m_draggedObjectType = GHOST_kDragnDropTypeString;
174         else return NSDragOperationNone;
175         
176         associatedWindow->setAcceptDragOperation(TRUE); //Drag operation is accepted by default
177         systemCocoa->handleDraggingEvent(GHOST_kEventDraggingEntered, m_draggedObjectType, associatedWindow, mouseLocation.x, mouseLocation.y, nil);
178         return NSDragOperationCopy;
179 }
180
181 - (BOOL)wantsPeriodicDraggingUpdates
182 {
183         return NO; //No need to overflow blender event queue. Events shall be sent only on changes
184 }
185
186 - (NSDragOperation)draggingUpdated:(id < NSDraggingInfo >)sender
187 {
188         NSPoint mouseLocation = [sender draggingLocation];
189         
190         systemCocoa->handleDraggingEvent(GHOST_kEventDraggingUpdated, m_draggedObjectType, associatedWindow, mouseLocation.x, mouseLocation.y, nil);
191         return associatedWindow->canAcceptDragOperation() ? NSDragOperationCopy : NSDragOperationNone;
192 }
193
194 - (void)draggingExited:(id < NSDraggingInfo >)sender
195 {
196         systemCocoa->handleDraggingEvent(GHOST_kEventDraggingExited, m_draggedObjectType, associatedWindow, 0, 0, nil);
197         m_draggedObjectType = GHOST_kDragnDropTypeUnknown;
198 }
199
200 - (BOOL)prepareForDragOperation:(id < NSDraggingInfo >)sender
201 {
202         if (associatedWindow->canAcceptDragOperation())
203                 return YES;
204         else
205                 return NO;
206 }
207
208 - (BOOL)performDragOperation:(id < NSDraggingInfo >)sender
209 {
210         NSPoint mouseLocation = [sender draggingLocation];
211         NSPasteboard *draggingPBoard = [sender draggingPasteboard];
212         NSImage *droppedImg;
213         id data;
214         
215         switch (m_draggedObjectType) {
216                 case GHOST_kDragnDropTypeBitmap:
217                         if ([NSImage canInitWithPasteboard:draggingPBoard]) {
218                                 droppedImg = [[NSImage alloc]initWithPasteboard:draggingPBoard];
219                                 data = droppedImg; //[draggingPBoard dataForType:NSTIFFPboardType];
220                         }
221                         else return NO;
222                         break;
223                 case GHOST_kDragnDropTypeFilenames:
224                         data = [draggingPBoard propertyListForType:NSFilenamesPboardType];
225                         break;
226                 case GHOST_kDragnDropTypeString:
227                         data = [draggingPBoard stringForType:NSStringPboardType];
228                         break;
229                 default:
230                         return NO;
231                         break;
232         }
233         systemCocoa->handleDraggingEvent(GHOST_kEventDraggingDropDone, m_draggedObjectType, associatedWindow, mouseLocation.x, mouseLocation.y, (void*)data);
234         return YES;
235 }
236
237 @end
238
239
240
241 #pragma mark NSOpenGLView subclass
242 //We need to subclass it in order to give Cocoa the feeling key events are trapped
243 @interface CocoaOpenGLView : NSOpenGLView <NSTextInput>
244 {
245         GHOST_SystemCocoa *systemCocoa;
246         GHOST_WindowCocoa *associatedWindow;
247
248         bool composing;
249         NSString *composing_text;
250 }
251 - (void)setSystemAndWindowCocoa:(GHOST_SystemCocoa *)sysCocoa windowCocoa:(GHOST_WindowCocoa *)winCocoa;
252 @end
253 @implementation CocoaOpenGLView
254
255 - (void)setSystemAndWindowCocoa:(GHOST_SystemCocoa *)sysCocoa windowCocoa:(GHOST_WindowCocoa *)winCocoa
256 {
257         systemCocoa = sysCocoa;
258         associatedWindow = winCocoa;
259
260         composing = false;
261         composing_text = nil;
262 }
263
264 - (BOOL)acceptsFirstResponder
265 {
266         return YES;
267 }
268
269 // The trick to prevent Cocoa from complaining (beeping)
270 - (void)keyDown:(NSEvent *)event
271 {
272         systemCocoa->handleKeyEvent(event);
273
274         /* Start or continue composing? */
275         if ([[event characters] length] == 0  ||
276             [[event charactersIgnoringModifiers] length] == 0 ||
277             composing)
278         {
279                 composing = YES;
280
281                 // interpret event to call insertText
282                 NSMutableArray *events;
283                 events = [[NSMutableArray alloc] initWithCapacity:1];
284                 [events addObject:event];
285                 [self interpretKeyEvents:events]; // calls insertText
286                 [events removeObject:event];
287                 [events release];
288                 return;
289         }
290 }
291
292 - (void)keyUp:(NSEvent *)event
293 {
294         systemCocoa->handleKeyEvent(event);
295 }
296
297 - (void)flagsChanged:(NSEvent *)event
298 {
299         systemCocoa->handleKeyEvent(event);
300 }
301
302 - (void)mouseDown:(NSEvent *)event
303 {
304         systemCocoa->handleMouseEvent(event);
305 }
306
307 - (void)mouseUp:(NSEvent *)event
308 {
309         systemCocoa->handleMouseEvent(event);
310 }
311
312 - (void)rightMouseDown:(NSEvent *)event
313 {
314         systemCocoa->handleMouseEvent(event);
315 }
316
317 - (void)rightMouseUp:(NSEvent *)event
318 {
319         systemCocoa->handleMouseEvent(event);
320 }
321
322 - (void)mouseMoved:(NSEvent *)event
323 {
324         systemCocoa->handleMouseEvent(event);
325 }
326
327 - (void)mouseDragged:(NSEvent *)event
328 {
329         systemCocoa->handleMouseEvent(event);
330 }
331
332 - (void)rightMouseDragged:(NSEvent *)event
333 {
334         systemCocoa->handleMouseEvent(event);
335 }
336
337 - (void)scrollWheel:(NSEvent *)event
338 {
339         systemCocoa->handleMouseEvent(event);
340 }
341
342 - (void)otherMouseDown:(NSEvent *)event
343 {
344         systemCocoa->handleMouseEvent(event);
345 }
346
347 - (void)otherMouseUp:(NSEvent *)event
348 {
349         systemCocoa->handleMouseEvent(event);
350 }
351
352 - (void)otherMouseDragged:(NSEvent *)event
353 {
354         systemCocoa->handleMouseEvent(event);
355 }
356
357 - (void)magnifyWithEvent:(NSEvent *)event
358 {
359         systemCocoa->handleMouseEvent(event);
360 }
361
362 - (void)rotateWithEvent:(NSEvent *)event
363 {
364         systemCocoa->handleMouseEvent(event);
365 }
366
367 - (void)beginGestureWithEvent:(NSEvent *)event
368 {
369         systemCocoa->handleMouseEvent(event);
370 }
371
372 - (void)endGestureWithEvent:(NSEvent *)event
373 {
374         systemCocoa->handleMouseEvent(event);
375 }
376
377 - (void)tabletPoint:(NSEvent *)event
378 {
379         systemCocoa->handleTabletEvent(event,[event type]);
380 }
381
382 - (void)tabletProximity:(NSEvent *)event
383 {
384         systemCocoa->handleTabletEvent(event,[event type]);
385 }
386
387 - (BOOL)isOpaque
388 {
389         return YES;
390 }
391
392 - (void) drawRect:(NSRect)rect
393 {
394         if ([self inLiveResize]) {
395                 /* Don't redraw while in live resize */
396         }
397         else {
398                 [super drawRect:rect];
399                 systemCocoa->handleWindowEvent(GHOST_kEventWindowUpdate, associatedWindow);
400         }
401 }
402
403 // Text input
404
405 - (void)composing_free
406 {
407         composing = NO;
408
409         if (composing_text) {
410                 [composing_text release];
411                 composing_text = nil;
412         }
413 }
414
415 - (void)insertText:(id)chars
416 {
417         [self composing_free];
418 }
419
420 - (void)setMarkedText:(id)chars selectedRange:(NSRange)range
421 {
422         [self composing_free];
423         if ([chars length] == 0)
424                 return;
425
426         // start composing
427         composing = YES;
428         composing_text = [chars copy];
429
430         // if empty, cancel
431         if ([composing_text length] == 0)
432                 [self composing_free];
433 }
434
435 - (void)unmarkText
436 {
437         [self composing_free];
438 }
439
440 - (BOOL)hasMarkedText
441 {
442         return (composing) ? YES : NO;
443 }
444
445 - (void)doCommandBySelector:(SEL)selector
446 {
447 }
448
449 - (BOOL)isComposing
450 {
451         return composing;
452 }
453
454 - (NSInteger)conversationIdentifier
455 {
456         return (NSInteger)self;
457 }
458
459 - (NSAttributedString *)attributedSubstringFromRange:(NSRange)range
460 {
461         return [NSAttributedString new]; // XXX does this leak?
462 }
463
464 - (NSRange)markedRange
465 {
466         unsigned int length = (composing_text) ? [composing_text length] : 0;
467
468         if (composing)
469                 return NSMakeRange(0, length);
470
471         return NSMakeRange(NSNotFound, 0);
472 }
473
474 - (NSRange)selectedRange
475 {
476         unsigned int length = (composing_text) ? [composing_text length] : 0;
477         return NSMakeRange(0, length);
478 }
479
480 - (NSRect)firstRectForCharacterRange:(NSRange)range
481 {
482         return NSZeroRect;
483 }
484
485 - (NSUInteger)characterIndexForPoint:(NSPoint)point
486 {
487         return NSNotFound;
488 }
489
490 - (NSArray*)validAttributesForMarkedText
491 {
492         return [NSArray array]; // XXX does this leak?
493 }
494
495 @end
496
497 #pragma mark initialization / finalization
498
499 #if MAC_OS_X_VERSION_MAX_ALLOWED < 1070
500 @interface NSView (NSOpenGLSurfaceResolution)
501 - (BOOL)wantsBestResolutionOpenGLSurface;
502 - (void)setWantsBestResolutionOpenGLSurface:(BOOL)flag;
503 - (NSRect)convertRectToBacking:(NSRect)bounds;
504 @end
505 #endif
506
507 NSOpenGLContext* GHOST_WindowCocoa::s_firstOpenGLcontext = nil;
508
509 GHOST_WindowCocoa::GHOST_WindowCocoa(
510         GHOST_SystemCocoa *systemCocoa,
511         const STR_String& title,
512         GHOST_TInt32 left,
513         GHOST_TInt32 bottom,
514         GHOST_TUns32 width,
515         GHOST_TUns32 height,
516         GHOST_TWindowState state,
517         GHOST_TDrawingContextType type,
518         const bool stereoVisual, const GHOST_TUns16 numOfAASamples
519 ) :
520         GHOST_Window(width, height, state, GHOST_kDrawingContextTypeNone, stereoVisual, false, numOfAASamples),
521         m_customCursor(0)
522 {
523         NSOpenGLPixelFormatAttribute pixelFormatAttrsWindow[40];
524         NSOpenGLPixelFormat *pixelFormat = nil;
525         int i;
526                 
527         m_systemCocoa = systemCocoa;
528         m_fullScreen = false;
529         
530         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
531         
532         //Creates the window
533         NSRect rect;
534         NSSize  minSize;
535         
536         rect.origin.x = left;
537         rect.origin.y = bottom;
538         rect.size.width = width;
539         rect.size.height = height;
540         
541         m_window = [[CocoaWindow alloc] initWithContentRect:rect
542                                                                                    styleMask:NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask | NSMiniaturizableWindowMask
543                                                                                          backing:NSBackingStoreBuffered defer:NO];
544         if (m_window == nil) {
545                 [pool drain];
546                 return;
547         }
548         
549         [m_window setSystemAndWindowCocoa:systemCocoa windowCocoa:this];
550         
551         //Forbid to resize the window below the blender defined minimum one
552         minSize.width = 320;
553         minSize.height = 240;
554         [m_window setContentMinSize:minSize];
555         
556         setTitle(title);
557         
558         
559         // Pixel Format Attributes for the windowed NSOpenGLContext
560         i=0;
561         pixelFormatAttrsWindow[i++] = NSOpenGLPFADoubleBuffer;
562         
563         // Guarantees the back buffer contents to be valid after a call to NSOpenGLContext object's flushBuffer
564         // needed for 'Draw Overlap' drawing method
565         pixelFormatAttrsWindow[i++] = NSOpenGLPFABackingStore; 
566         
567         // Force software OpenGL, for debugging
568         if (getenv("BLENDER_SOFTWAREGL")) {
569                 pixelFormatAttrsWindow[i++] = NSOpenGLPFARendererID;
570                 pixelFormatAttrsWindow[i++] = kCGLRendererGenericID;
571         }
572         else
573                 pixelFormatAttrsWindow[i++] = NSOpenGLPFAAccelerated;
574
575         //pixelFormatAttrsWindow[i++] = NSOpenGLPFAAllowOfflineRenderers,;   // Removed to allow 10.4 builds, and 2 GPUs rendering is not used anyway
576
577         pixelFormatAttrsWindow[i++] = NSOpenGLPFADepthSize;
578         pixelFormatAttrsWindow[i++] = (NSOpenGLPixelFormatAttribute) 32;
579         
580         pixelFormatAttrsWindow[i++] = NSOpenGLPFAAccumSize;
581         pixelFormatAttrsWindow[i++] = (NSOpenGLPixelFormatAttribute) 32;
582         
583         if (stereoVisual) pixelFormatAttrsWindow[i++] = NSOpenGLPFAStereo;
584         
585         if (numOfAASamples > 0) {
586                 // Multisample anti-aliasing
587                 pixelFormatAttrsWindow[i++] = NSOpenGLPFAMultisample;
588                 
589                 pixelFormatAttrsWindow[i++] = NSOpenGLPFASampleBuffers;
590                 pixelFormatAttrsWindow[i++] = (NSOpenGLPixelFormatAttribute) 1;
591                 
592                 pixelFormatAttrsWindow[i++] = NSOpenGLPFASamples;
593                 pixelFormatAttrsWindow[i++] = (NSOpenGLPixelFormatAttribute) numOfAASamples;
594                 
595                 pixelFormatAttrsWindow[i++] = NSOpenGLPFANoRecovery;
596         }
597         
598         pixelFormatAttrsWindow[i] = (NSOpenGLPixelFormatAttribute) 0;
599         
600         pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:pixelFormatAttrsWindow];
601         
602         
603         //Fall back to no multisampling if Antialiasing init failed
604         if (pixelFormat == nil) {
605                 i=0;
606                 pixelFormatAttrsWindow[i++] = NSOpenGLPFADoubleBuffer;
607                 
608                 // Guarantees the back buffer contents to be valid after a call to NSOpenGLContext object's flushBuffer
609                 // needed for 'Draw Overlap' drawing method
610                 pixelFormatAttrsWindow[i++] = NSOpenGLPFABackingStore;
611                 
612                 // Force software OpenGL, for debugging
613                 if (getenv("BLENDER_SOFTWAREGL")) {
614                         pixelFormatAttrsWindow[i++] = NSOpenGLPFARendererID;
615                         pixelFormatAttrsWindow[i++] = kCGLRendererGenericID;
616                 }
617                 else
618                         pixelFormatAttrsWindow[i++] = NSOpenGLPFAAccelerated;
619
620                 //pixelFormatAttrsWindow[i++] = NSOpenGLPFAAllowOfflineRenderers,;   // Removed to allow 10.4 builds, and 2 GPUs rendering is not used anyway
621                 
622                 pixelFormatAttrsWindow[i++] = NSOpenGLPFADepthSize;
623                 pixelFormatAttrsWindow[i++] = (NSOpenGLPixelFormatAttribute) 32;
624
625                 pixelFormatAttrsWindow[i++] = NSOpenGLPFAAccumSize;
626                 pixelFormatAttrsWindow[i++] = (NSOpenGLPixelFormatAttribute) 32;
627                 
628                 if (stereoVisual) pixelFormatAttrsWindow[i++] = NSOpenGLPFAStereo;
629                 
630                 pixelFormatAttrsWindow[i] = (NSOpenGLPixelFormatAttribute) 0;
631                 
632                 pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:pixelFormatAttrsWindow];
633                 
634         }
635         
636         if (numOfAASamples > 0) { //Set m_numOfAASamples to the actual value
637                 GLint gli;
638                 [pixelFormat getValues:&gli forAttribute:NSOpenGLPFASamples forVirtualScreen:0];
639                 if (m_numOfAASamples != (GHOST_TUns16)gli) {
640                         m_numOfAASamples = (GHOST_TUns16)gli;
641                         printf("GHOST_Window could be created with anti-aliasing of only %i samples\n",m_numOfAASamples);
642                 }
643         }
644                 
645         //Creates the OpenGL View inside the window
646         m_openGLView = [[CocoaOpenGLView alloc] initWithFrame:rect
647                                                                                                  pixelFormat:pixelFormat];
648
649         [m_openGLView setSystemAndWindowCocoa:systemCocoa windowCocoa:this];
650         
651         [pixelFormat release];
652         
653         m_openGLContext = [m_openGLView openGLContext]; //This context will be replaced by the proper one just after
654         
655         [m_window setContentView:m_openGLView];
656         [m_window setInitialFirstResponder:m_openGLView];
657         
658         [m_window makeKeyAndOrderFront:nil];
659         
660         setDrawingContextType(type);
661         updateDrawingContext();
662         activateDrawingContext();
663
664         if (m_systemCocoa->m_nativePixel) {
665                 if ([m_openGLView respondsToSelector:@selector(setWantsBestResolutionOpenGLSurface:)]) {
666                         [m_openGLView setWantsBestResolutionOpenGLSurface:YES];
667                 
668                         NSRect backingBounds = [m_openGLView convertRectToBacking:[m_openGLView bounds]];
669                         m_nativePixelSize = (float)backingBounds.size.width / (float)rect.size.width;
670                 }
671         }
672         
673         m_tablet.Active = GHOST_kTabletModeNone;
674         
675         CocoaWindowDelegate *windowDelegate = [[CocoaWindowDelegate alloc] init];
676         [windowDelegate setSystemAndWindowCocoa:systemCocoa windowCocoa:this];
677         [m_window setDelegate:windowDelegate];
678         
679         [m_window setAcceptsMouseMovedEvents:YES];
680         
681 #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
682         NSView *view = [m_window contentView];
683         [view setAcceptsTouchEvents:YES];
684 #endif
685         
686         [m_window registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType,
687                                                                                   NSStringPboardType, NSTIFFPboardType, nil]];
688         
689 #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
690         if (state != GHOST_kWindowStateFullScreen) {
691                 [m_window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
692         }
693 #endif
694         
695         if (state == GHOST_kWindowStateFullScreen)
696                 setState(GHOST_kWindowStateFullScreen);
697                 
698         [pool drain];
699 }
700
701
702 GHOST_WindowCocoa::~GHOST_WindowCocoa()
703 {
704         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
705
706         if (m_customCursor) {
707                 [m_customCursor release];
708                 m_customCursor = nil;
709         }
710
711         [m_openGLView release];
712         
713         if (m_window) {
714                 [m_window close];
715         }
716         
717         // Check for other blender opened windows and make the frontmost key
718         // Note: for some reason the closed window is still in the list
719         NSArray *windowsList = [NSApp orderedWindows];
720         for (int a = 0; a < [windowsList count]; a++) {
721                 if (m_window != (CocoaWindow *)[windowsList objectAtIndex:a]) {
722                         [[windowsList objectAtIndex:a] makeKeyWindow];
723                         break;
724                 }
725         }
726         m_window = nil;
727
728         [pool drain];
729 }
730
731 #pragma mark accessors
732
733 bool GHOST_WindowCocoa::getValid() const
734 {
735         return (m_window != 0);
736 }
737
738 void* GHOST_WindowCocoa::getOSWindow() const
739 {
740         return (void*)m_window;
741 }
742
743 void GHOST_WindowCocoa::setTitle(const STR_String& title)
744 {
745         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setTitle(): window invalid");
746         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
747
748         NSString *windowTitle = [[NSString alloc] initWithCString:title encoding:NSUTF8StringEncoding];
749         
750         //Set associated file if applicable
751         if (windowTitle && [windowTitle hasPrefix:@"Blender"]) {
752                 NSRange fileStrRange;
753                 NSString *associatedFileName;
754                 int len;
755                 
756                 fileStrRange.location = [windowTitle rangeOfString:@"["].location+1;
757                 len = [windowTitle rangeOfString:@"]"].location - fileStrRange.location;
758         
759                 if (len > 0) {
760                         fileStrRange.length = len;
761                         associatedFileName = [windowTitle substringWithRange:fileStrRange];
762                         [m_window setTitle:[associatedFileName lastPathComponent]];
763
764                         //Blender used file open/save functions converte file names into legal URL ones
765                         associatedFileName = [associatedFileName stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
766                         @try {
767                                 [m_window setRepresentedFilename:associatedFileName];
768                         }
769                         @catch (NSException * e) {
770                                 printf("\nInvalid file path given in window title");
771                         }
772                 }
773                 else {
774                         [m_window setTitle:windowTitle];
775                         [m_window setRepresentedFilename:@""];
776                 }
777
778         }
779         else {
780                 [m_window setTitle:windowTitle];
781                 [m_window setRepresentedFilename:@""];
782         }
783
784         
785         [windowTitle release];
786         [pool drain];
787 }
788
789
790 void GHOST_WindowCocoa::getTitle(STR_String& title) const
791 {
792         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getTitle(): window invalid");
793
794         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
795
796         NSString *windowTitle = [m_window title];
797
798         if (windowTitle != nil) {
799                 title = [windowTitle UTF8String];
800         }
801         
802         [pool drain];
803 }
804
805
806 void GHOST_WindowCocoa::getWindowBounds(GHOST_Rect& bounds) const
807 {
808         NSRect rect;
809         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getWindowBounds(): window invalid");
810
811         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
812         
813         NSRect screenSize = [[m_window screen] visibleFrame];
814
815         rect = [m_window frame];
816
817         bounds.m_b = screenSize.size.height - (rect.origin.y -screenSize.origin.y);
818         bounds.m_l = rect.origin.x -screenSize.origin.x;
819         bounds.m_r = rect.origin.x-screenSize.origin.x + rect.size.width;
820         bounds.m_t = screenSize.size.height - (rect.origin.y + rect.size.height -screenSize.origin.y);
821         
822         [pool drain];
823 }
824
825
826 void GHOST_WindowCocoa::getClientBounds(GHOST_Rect& bounds) const
827 {
828         NSRect rect;
829         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getClientBounds(): window invalid");
830         
831         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
832         
833         if (!m_fullScreen) {
834                 NSRect screenSize = [[m_window screen] visibleFrame];
835
836                 //Max window contents as screen size (excluding title bar...)
837                 NSRect contentRect = [CocoaWindow contentRectForFrameRect:screenSize
838                                                                                                          styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)];
839
840                 rect = [m_window contentRectForFrameRect:[m_window frame]];
841                 
842                 bounds.m_b = contentRect.size.height - (rect.origin.y -contentRect.origin.y);
843                 bounds.m_l = rect.origin.x -contentRect.origin.x;
844                 bounds.m_r = rect.origin.x-contentRect.origin.x + rect.size.width;
845                 bounds.m_t = contentRect.size.height - (rect.origin.y + rect.size.height -contentRect.origin.y);
846         }
847         else {
848                 NSRect screenSize = [[m_window screen] frame];
849                 
850                 bounds.m_b = screenSize.origin.y + screenSize.size.height;
851                 bounds.m_l = screenSize.origin.x;
852                 bounds.m_r = screenSize.origin.x + screenSize.size.width;
853                 bounds.m_t = screenSize.origin.y;
854         }
855         [pool drain];
856 }
857
858
859 GHOST_TSuccess GHOST_WindowCocoa::setClientWidth(GHOST_TUns32 width)
860 {
861         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setClientWidth(): window invalid");
862         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
863         GHOST_Rect cBnds, wBnds;
864         getClientBounds(cBnds);
865         if (((GHOST_TUns32)cBnds.getWidth()) != width) {
866                 NSSize size;
867                 size.width=width;
868                 size.height=cBnds.getHeight();
869                 [m_window setContentSize:size];
870         }
871         [pool drain];
872         return GHOST_kSuccess;
873 }
874
875
876 GHOST_TSuccess GHOST_WindowCocoa::setClientHeight(GHOST_TUns32 height)
877 {
878         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setClientHeight(): window invalid");
879         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
880         GHOST_Rect cBnds, wBnds;
881         getClientBounds(cBnds);
882         if (((GHOST_TUns32)cBnds.getHeight()) != height) {
883                 NSSize size;
884                 size.width=cBnds.getWidth();
885                 size.height=height;
886                 [m_window setContentSize:size];
887         }
888         [pool drain];
889         return GHOST_kSuccess;
890 }
891
892
893 GHOST_TSuccess GHOST_WindowCocoa::setClientSize(GHOST_TUns32 width, GHOST_TUns32 height)
894 {
895         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setClientSize(): window invalid");
896         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
897         GHOST_Rect cBnds, wBnds;
898         getClientBounds(cBnds);
899         if ((((GHOST_TUns32)cBnds.getWidth())  != width) ||
900             (((GHOST_TUns32)cBnds.getHeight()) != height))
901         {
902                 NSSize size;
903                 size.width=width;
904                 size.height=height;
905                 [m_window setContentSize:size];
906         }
907         [pool drain];
908         return GHOST_kSuccess;
909 }
910
911
912 GHOST_TWindowState GHOST_WindowCocoa::getState() const
913 {
914         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getState(): window invalid");
915         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
916         GHOST_TWindowState state;
917
918 #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
919         NSUInteger masks = [m_window styleMask];
920
921         if (masks & NSFullScreenWindowMask) {
922                 // Lion style fullscreen
923                 state = GHOST_kWindowStateFullScreen;
924         }
925         else
926 #endif
927         if (m_fullScreen) {
928                 state = GHOST_kWindowStateFullScreen;
929         } 
930         else if ([m_window isMiniaturized]) {
931                 state = GHOST_kWindowStateMinimized;
932         }
933         else if ([m_window isZoomed]) {
934                 state = GHOST_kWindowStateMaximized;
935         }
936         else {
937                 state = GHOST_kWindowStateNormal;
938         }
939         [pool drain];
940         return state;
941 }
942
943
944 void GHOST_WindowCocoa::screenToClient(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
945 {
946         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::screenToClient(): window invalid");
947
948         screenToClientIntern(inX, inY, outX, outY);
949
950         /* switch y to match ghost convention */
951         GHOST_Rect cBnds;
952         getClientBounds(cBnds);
953         outY = (cBnds.getHeight() - 1) - outY;
954 }
955
956
957 void GHOST_WindowCocoa::clientToScreen(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
958 {
959         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::clientToScreen(): window invalid");
960
961         /* switch y to match ghost convention */
962         GHOST_Rect cBnds;
963         getClientBounds(cBnds);
964         inY = (cBnds.getHeight() - 1) - inY;
965
966         clientToScreenIntern(inX, inY, outX, outY);
967 }
968
969 void GHOST_WindowCocoa::screenToClientIntern(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
970 {
971         NSPoint screenCoord;
972         NSPoint baseCoord;
973         
974         screenCoord.x = inX;
975         screenCoord.y = inY;
976         
977         baseCoord = [m_window convertScreenToBase:screenCoord];
978         
979         outX = baseCoord.x;
980         outY = baseCoord.y;
981 }
982
983 void GHOST_WindowCocoa::clientToScreenIntern(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
984 {
985         NSPoint screenCoord;
986         NSPoint baseCoord;
987         
988         baseCoord.x = inX;
989         baseCoord.y = inY;
990         
991         screenCoord = [m_window convertBaseToScreen:baseCoord];
992         
993         outX = screenCoord.x;
994         outY = screenCoord.y;
995 }
996
997
998 NSScreen* GHOST_WindowCocoa::getScreen()
999 {
1000         return [m_window screen];
1001 }
1002
1003 /* called for event, when window leaves monitor to another */
1004 void GHOST_WindowCocoa::setNativePixelSize(void)
1005 {
1006         /* make sure 10.6 keeps running */
1007         if ([m_openGLView respondsToSelector:@selector(setWantsBestResolutionOpenGLSurface:)]) {
1008                 NSRect backingBounds = [m_openGLView convertRectToBacking:[m_openGLView bounds]];
1009                 
1010                 GHOST_Rect rect;
1011                 getClientBounds(rect);
1012
1013                 m_nativePixelSize = (float)backingBounds.size.width / (float)rect.getWidth();
1014         }
1015 }
1016
1017 /**
1018  * \note Fullscreen switch is not actual fullscreen with display capture.
1019  * As this capture removes all OS X window manager features.
1020  *
1021  * Instead, the menu bar and the dock are hidden, and the window is made borderless and enlarged.
1022  * Thus, process switch, exposé, spaces, ... still work in fullscreen mode
1023  */
1024 GHOST_TSuccess GHOST_WindowCocoa::setState(GHOST_TWindowState state)
1025 {
1026         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setState(): window invalid");
1027         switch (state) {
1028                 case GHOST_kWindowStateMinimized:
1029                         [m_window miniaturize:nil];
1030                         break;
1031                 case GHOST_kWindowStateMaximized:
1032                         [m_window zoom:nil];
1033                         break;
1034                 
1035                 case GHOST_kWindowStateFullScreen:
1036                 {
1037 #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
1038                         NSUInteger masks = [m_window styleMask];
1039
1040                         if (!m_fullScreen && !(masks & NSFullScreenWindowMask)) {
1041                                 /* Starting with 10.9, we always use Lion fullscreen, since it
1042                                  * now has proper multi-monitor support for fullscreen */
1043                                 struct { SInt32 major, minor; } systemversion;
1044                                 Gestalt(gestaltSystemVersionMajor, &systemversion.major);
1045                                 Gestalt(gestaltSystemVersionMinor, &systemversion.minor);
1046
1047                                 if (systemversion.major > 10 || (systemversion.major == 10 && systemversion.minor >= 9)) {
1048                                         [m_window toggleFullScreen:nil];
1049                                         break;
1050                                 }
1051 #else
1052                         if (!m_fullScreen) {
1053 #endif
1054                                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1055                         
1056                                 /* This status change needs to be done before Cocoa call to enter fullscreen mode
1057                                  * to give window delegate hint not to forward its deactivation to ghost wm that
1058                                  * doesn't know view/window difference. */
1059                                 m_fullScreen = true;
1060
1061 #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
1062                                 /* Disable toggle for Lion style fullscreen */
1063                                 [m_window setCollectionBehavior:NSWindowCollectionBehaviorDefault];
1064 #endif
1065
1066 #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
1067                                 //10.6 provides Cocoa functions to autoshow menu bar, and to change a window style
1068                                 //Hide menu & dock if on primary screen. else only menu
1069                                 if ([[m_window screen] isEqual:[[NSScreen screens] objectAtIndex:0]]) {
1070                                         [NSApp setPresentationOptions:(NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar)];
1071                                 }
1072                                 //Make window borderless and enlarge it
1073                                 [m_window setStyleMask:NSBorderlessWindowMask];
1074                                 [m_window setFrame:[[m_window screen] frame] display:YES];
1075                                 [m_window makeFirstResponder:m_openGLView];
1076 #else
1077                                 //With 10.5, we need to create a new window to change its style to borderless
1078                                 //Hide menu & dock if needed
1079                                 if ([[m_window screen] isEqual:[[NSScreen screens] objectAtIndex:0]]) {
1080                                         //Cocoa function in 10.5 does not allow to set the menu bar in auto-show mode [NSMenu setMenuBarVisible:NO];
1081                                         //One of the very few 64bit compatible Carbon function
1082                                         SetSystemUIMode(kUIModeAllHidden,kUIOptionAutoShowMenuBar);
1083                                 }
1084                                 //Create a fullscreen borderless window
1085                                 CocoaWindow *tmpWindow = [[CocoaWindow alloc]
1086                                                                                   initWithContentRect:[[m_window screen] frame]
1087                                                                                   styleMask:NSBorderlessWindowMask
1088                                                                                   backing:NSBackingStoreBuffered
1089                                                                                   defer:YES];
1090                                 //Copy current window parameters
1091                                 [tmpWindow setTitle:[m_window title]];
1092                                 [tmpWindow setRepresentedFilename:[m_window representedFilename]];
1093                                 [tmpWindow setAcceptsMouseMovedEvents:YES];
1094                                 [tmpWindow setDelegate:[m_window delegate]];
1095                                 [tmpWindow setSystemAndWindowCocoa:[m_window systemCocoa] windowCocoa:this];
1096                                 [tmpWindow registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType,
1097                                                                     NSStringPboardType, NSTIFFPboardType, nil]];
1098                                 
1099                                 //Assign the openGL view to the new window
1100                                 [tmpWindow setContentView:m_openGLView];
1101                                 
1102                                 //Show the new window
1103                                 [tmpWindow makeKeyAndOrderFront:m_openGLView];
1104                                 //Close and release old window
1105                                 [m_window close];
1106                                 m_window = tmpWindow;
1107 #endif
1108                         
1109                                 //Tell WM of view new size
1110                                 m_systemCocoa->handleWindowEvent(GHOST_kEventWindowSize, this);
1111                                 
1112                                 [pool drain];
1113                         }
1114                         break;
1115                 }
1116                 case GHOST_kWindowStateNormal:
1117                 default:
1118                         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1119 #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
1120                         NSUInteger masks = [m_window styleMask];
1121
1122                         if (masks & NSFullScreenWindowMask) {
1123                                 // Lion style fullscreen
1124                                 [m_window toggleFullScreen:nil];
1125                         }
1126                         else
1127 #endif
1128                         if (m_fullScreen) {
1129                                 m_fullScreen = false;
1130
1131 #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
1132                                 /* Enable toggle for into Lion style fullscreen */
1133                                 [m_window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
1134 #endif
1135
1136                                 //Exit fullscreen
1137 #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
1138                                 //Show again menu & dock if needed
1139                                 if ([[m_window screen] isEqual:[NSScreen mainScreen]]) {
1140                                         [NSApp setPresentationOptions:NSApplicationPresentationDefault];
1141                                 }
1142                                 //Make window normal and resize it
1143                                 [m_window setStyleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)];
1144                                 [m_window setFrame:[[m_window screen] visibleFrame] display:YES];
1145                                 //TODO for 10.6 only : window title is forgotten after the style change
1146                                 [m_window makeFirstResponder:m_openGLView];
1147 #else
1148                                 //With 10.5, we need to create a new window to change its style to borderless
1149                                 //Show menu & dock if needed
1150                                 if ([[m_window screen] isEqual:[NSScreen mainScreen]]) {
1151                                         //Cocoa function in 10.5 does not allow to set the menu bar in auto-show mode [NSMenu setMenuBarVisible:YES];
1152                                         SetSystemUIMode(kUIModeNormal, 0); //One of the very few 64bit compatible Carbon function
1153                                 }
1154                                 //Create a fullscreen borderless window
1155                                 CocoaWindow *tmpWindow = [[CocoaWindow alloc]
1156                                                                                   initWithContentRect:[[m_window screen] frame]
1157                                                                                                         styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)
1158                                                                                                           backing:NSBackingStoreBuffered
1159                                                                                                                 defer:YES];
1160                                 //Copy current window parameters
1161                                 [tmpWindow setTitle:[m_window title]];
1162                                 [tmpWindow setRepresentedFilename:[m_window representedFilename]];
1163                                 [tmpWindow setAcceptsMouseMovedEvents:YES];
1164                                 [tmpWindow setDelegate:[m_window delegate]];
1165                                 [tmpWindow setSystemAndWindowCocoa:[m_window systemCocoa] windowCocoa:this];
1166                                 [tmpWindow registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType,
1167                                                                                                    NSStringPboardType, NSTIFFPboardType, nil]];
1168                                 //Forbid to resize the window below the blender defined minimum one
1169                                 [tmpWindow setContentMinSize:NSMakeSize(320, 240)];
1170                                 
1171                                 //Assign the openGL view to the new window
1172                                 [tmpWindow setContentView:m_openGLView];
1173                                 
1174                                 //Show the new window
1175                                 [tmpWindow makeKeyAndOrderFront:nil];
1176                                 //Close and release old window
1177                                 [m_window close];
1178                                 m_window = tmpWindow;
1179 #endif
1180                         
1181                                 //Tell WM of view new size
1182                                 m_systemCocoa->handleWindowEvent(GHOST_kEventWindowSize, this);
1183                         }
1184                         else if ([m_window isMiniaturized])
1185                                 [m_window deminiaturize:nil];
1186                         else if ([m_window isZoomed])
1187                                 [m_window zoom:nil];
1188                         [pool drain];
1189                         break;
1190         }
1191
1192         return GHOST_kSuccess;
1193 }
1194
1195 GHOST_TSuccess GHOST_WindowCocoa::setModifiedState(bool isUnsavedChanges)
1196 {
1197         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1198         
1199         [m_window setDocumentEdited:isUnsavedChanges];
1200         
1201         [pool drain];
1202         return GHOST_Window::setModifiedState(isUnsavedChanges);
1203 }
1204
1205
1206
1207 GHOST_TSuccess GHOST_WindowCocoa::setOrder(GHOST_TWindowOrder order)
1208 {
1209         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1210         
1211         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setOrder(): window invalid");
1212         if (order == GHOST_kWindowOrderTop) {
1213                 [m_window makeKeyAndOrderFront:nil];
1214         }
1215         else {
1216                 NSArray *windowsList;
1217                 
1218                 [m_window orderBack:nil];
1219                 
1220                 //Check for other blender opened windows and make the frontmost key
1221                 windowsList = [NSApp orderedWindows];
1222                 if ([windowsList count]) {
1223                         [[windowsList objectAtIndex:0] makeKeyAndOrderFront:nil];
1224                 }
1225         }
1226         
1227         [pool drain];
1228         return GHOST_kSuccess;
1229 }
1230
1231 #pragma mark Drawing context
1232
1233 /*#define  WAIT_FOR_VSYNC 1*/
1234
1235 GHOST_TSuccess GHOST_WindowCocoa::swapBuffers()
1236 {
1237         if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) {
1238                 if (m_openGLContext != nil) {
1239                         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1240                         [m_openGLContext flushBuffer];
1241                         [pool drain];
1242                         return GHOST_kSuccess;
1243                 }
1244         }
1245         return GHOST_kFailure;
1246 }
1247
1248 GHOST_TSuccess GHOST_WindowCocoa::updateDrawingContext()
1249 {
1250         if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) {
1251                 if (m_openGLContext != nil) {
1252                         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1253                         [m_openGLContext update];
1254                         [pool drain];
1255                         return GHOST_kSuccess;
1256                 }
1257         }
1258         return GHOST_kFailure;
1259 }
1260
1261 GHOST_TSuccess GHOST_WindowCocoa::activateDrawingContext()
1262 {
1263         if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) {
1264                 if (m_openGLContext != nil) {
1265                         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1266                         [m_openGLContext makeCurrentContext];
1267                         
1268                         // Disable AA by default
1269                         if (m_numOfAASamples > 0) glDisable(GL_MULTISAMPLE_ARB);
1270                         [pool drain];
1271                         return GHOST_kSuccess;
1272                 }
1273         }
1274         return GHOST_kFailure;
1275 }
1276
1277
1278 GHOST_TSuccess GHOST_WindowCocoa::installDrawingContext(GHOST_TDrawingContextType type)
1279 {
1280         GHOST_TSuccess success = GHOST_kFailure;
1281         
1282         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1283         
1284         NSOpenGLPixelFormat *pixelFormat;
1285         NSOpenGLContext *tmpOpenGLContext;
1286         
1287         /***** Multithreaded opengl code : uncomment for enabling
1288         CGLContextObj cglCtx;
1289         */
1290          
1291         switch (type) {
1292                 case GHOST_kDrawingContextTypeOpenGL:
1293                         if (!getValid()) break;
1294
1295                         pixelFormat = [m_openGLView pixelFormat];
1296                         tmpOpenGLContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat
1297                                                                                                                           shareContext:s_firstOpenGLcontext];
1298                         if (tmpOpenGLContext == nil) {
1299                                 success = GHOST_kFailure;
1300                                 break;
1301                         }
1302                         
1303                         //Switch openGL to multhreaded mode
1304                         /******* Multithreaded opengl code : uncomment for enabling
1305                         cglCtx = (CGLContextObj)[tmpOpenGLContext CGLContextObj];
1306                         if (CGLEnable(cglCtx, kCGLCEMPEngine) == kCGLNoError)
1307                                 printf("\nSwitched openGL to multithreaded mode\n");
1308                          */
1309                         
1310                         if (!s_firstOpenGLcontext) s_firstOpenGLcontext = tmpOpenGLContext;
1311 #ifdef WAIT_FOR_VSYNC
1312                         {
1313                                 GLint swapInt = 1;
1314                                 /* wait for vsync, to avoid tearing artifacts */
1315                                 [tmpOpenGLContext setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
1316                         }
1317 #endif
1318                         [m_openGLView setOpenGLContext:tmpOpenGLContext];
1319                         [tmpOpenGLContext setView:m_openGLView];
1320                         
1321                         m_openGLContext = tmpOpenGLContext;
1322                         break;
1323         
1324                 case GHOST_kDrawingContextTypeNone:
1325                         success = GHOST_kSuccess;
1326                         break;
1327                 
1328                 default:
1329                         break;
1330         }
1331         [pool drain];
1332         return success;
1333 }
1334
1335
1336 GHOST_TSuccess GHOST_WindowCocoa::removeDrawingContext()
1337 {
1338         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1339         switch (m_drawingContextType) {
1340                 case GHOST_kDrawingContextTypeOpenGL:
1341                         if (m_openGLContext) {
1342                                 [m_openGLView clearGLContext];
1343                                 if (s_firstOpenGLcontext == m_openGLContext) s_firstOpenGLcontext = nil;
1344                                 m_openGLContext = nil;
1345                         }
1346                         [pool drain];
1347                         return GHOST_kSuccess;
1348                 case GHOST_kDrawingContextTypeNone:
1349                         [pool drain];
1350                         return GHOST_kSuccess;
1351                         break;
1352                 default:
1353                         [pool drain];
1354                         return GHOST_kFailure;
1355         }
1356 }
1357
1358
1359 GHOST_TSuccess GHOST_WindowCocoa::invalidate()
1360 {
1361         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::invalidate(): window invalid");
1362         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1363         [m_openGLView setNeedsDisplay:YES];
1364         [pool drain];
1365         return GHOST_kSuccess;
1366 }
1367
1368 #pragma mark Progress bar
1369
1370 GHOST_TSuccess GHOST_WindowCocoa::setProgressBar(float progress)
1371 {
1372         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1373         
1374         if ((progress >=0.0) && (progress <=1.0)) {
1375                 NSImage* dockIcon = [[NSImage alloc] initWithSize:NSMakeSize(128,128)];
1376                 
1377                 [dockIcon lockFocus];
1378                 NSRect progressBox = {{4, 4}, {120, 16}};
1379
1380                 [[NSImage imageNamed:@"NSApplicationIcon"] drawAtPoint:NSZeroPoint fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0];
1381
1382                 // Track & Outline
1383                 [[NSColor blackColor] setFill];
1384                 NSRectFill(progressBox);
1385
1386                 [[NSColor whiteColor] set];
1387                 NSFrameRect(progressBox);
1388
1389                 // Progress fill
1390                 progressBox = NSInsetRect(progressBox, 1, 1);
1391
1392                 progressBox.size.width = progressBox.size.width * progress;
1393                 NSGradient *gradient = [[NSGradient alloc] initWithStartingColor:[NSColor darkGrayColor] endingColor:[NSColor lightGrayColor]];
1394                 [gradient drawInRect:progressBox angle:90];
1395                 [gradient release];
1396                 
1397                 [dockIcon unlockFocus];
1398                 
1399                 [NSApp setApplicationIconImage:dockIcon];
1400                 [dockIcon release];
1401                 
1402                 m_progressBarVisible = true;
1403         }
1404         
1405         [pool drain];
1406         return GHOST_kSuccess;
1407 }
1408
1409
1410 GHOST_TSuccess GHOST_WindowCocoa::endProgressBar()
1411 {
1412         if (!m_progressBarVisible) return GHOST_kFailure;
1413         m_progressBarVisible = false;
1414         
1415         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1416         
1417         NSImage* dockIcon = [[NSImage alloc] initWithSize:NSMakeSize(128,128)];
1418         [dockIcon lockFocus];
1419         [[NSImage imageNamed:@"NSApplicationIcon"] drawAtPoint:NSZeroPoint fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0];
1420         [dockIcon unlockFocus];
1421         [NSApp setApplicationIconImage:dockIcon];
1422         [dockIcon release];
1423         
1424         [pool drain];
1425         return GHOST_kSuccess;
1426 }
1427
1428
1429
1430 #pragma mark Cursor handling
1431
1432 void GHOST_WindowCocoa::loadCursor(bool visible, GHOST_TStandardCursor cursor) const
1433 {
1434         static bool systemCursorVisible = true;
1435         
1436         NSCursor *tmpCursor =nil;
1437         
1438         if (visible != systemCursorVisible) {
1439                 if (visible) {
1440                         [NSCursor unhide];
1441                         systemCursorVisible = true;
1442                 }
1443                 else {
1444                         [NSCursor hide];
1445                         systemCursorVisible = false;
1446                 }
1447         }
1448
1449         if (cursor == GHOST_kStandardCursorCustom && m_customCursor) {
1450                 tmpCursor = m_customCursor;
1451         }
1452         else {
1453                 switch (cursor) {
1454                         case GHOST_kStandardCursorDestroy:
1455                                 tmpCursor = [NSCursor disappearingItemCursor];
1456                                 break;
1457                         case GHOST_kStandardCursorText:
1458                                 tmpCursor = [NSCursor IBeamCursor];
1459                                 break;
1460                         case GHOST_kStandardCursorCrosshair:
1461                                 tmpCursor = [NSCursor crosshairCursor];
1462                                 break;
1463                         case GHOST_kStandardCursorUpDown:
1464                                 tmpCursor = [NSCursor resizeUpDownCursor];
1465                                 break;
1466                         case GHOST_kStandardCursorLeftRight:
1467                                 tmpCursor = [NSCursor resizeLeftRightCursor];
1468                                 break;
1469                         case GHOST_kStandardCursorTopSide:
1470                                 tmpCursor = [NSCursor resizeUpCursor];
1471                                 break;
1472                         case GHOST_kStandardCursorBottomSide:
1473                                 tmpCursor = [NSCursor resizeDownCursor];
1474                                 break;
1475                         case GHOST_kStandardCursorLeftSide:
1476                                 tmpCursor = [NSCursor resizeLeftCursor];
1477                                 break;
1478                         case GHOST_kStandardCursorRightSide:
1479                                 tmpCursor = [NSCursor resizeRightCursor];
1480                                 break;
1481                         case GHOST_kStandardCursorRightArrow:
1482                         case GHOST_kStandardCursorInfo:
1483                         case GHOST_kStandardCursorLeftArrow:
1484                         case GHOST_kStandardCursorHelp:
1485                         case GHOST_kStandardCursorCycle:
1486                         case GHOST_kStandardCursorSpray:
1487                         case GHOST_kStandardCursorWait:
1488                         case GHOST_kStandardCursorTopLeftCorner:
1489                         case GHOST_kStandardCursorTopRightCorner:
1490                         case GHOST_kStandardCursorBottomRightCorner:
1491                         case GHOST_kStandardCursorBottomLeftCorner:
1492                         case GHOST_kStandardCursorCopy:
1493                         case GHOST_kStandardCursorDefault:
1494                         default:
1495                                 tmpCursor = [NSCursor arrowCursor];
1496                                 break;
1497                 };
1498         }
1499         [tmpCursor set];
1500 }
1501
1502
1503
1504 GHOST_TSuccess GHOST_WindowCocoa::setWindowCursorVisibility(bool visible)
1505 {
1506         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
1507         
1508         if ([m_window isVisible]) {
1509                 loadCursor(visible, getCursorShape());
1510         }
1511         
1512         [pool drain];
1513         return GHOST_kSuccess;
1514 }
1515
1516
1517 GHOST_TSuccess GHOST_WindowCocoa::setWindowCursorGrab(GHOST_TGrabCursorMode mode)
1518 {
1519         GHOST_TSuccess err = GHOST_kSuccess;
1520         
1521         if (mode != GHOST_kGrabDisable) {
1522                 //No need to perform grab without warp as it is always on in OS X
1523                 if (mode != GHOST_kGrabNormal) {
1524                         GHOST_TInt32 x_old,y_old;
1525                         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1526
1527                         m_systemCocoa->getCursorPosition(x_old,y_old);
1528                         screenToClientIntern(x_old, y_old, m_cursorGrabInitPos[0], m_cursorGrabInitPos[1]);
1529                         //Warp position is stored in client (window base) coordinates
1530                         setCursorGrabAccum(0, 0);
1531                         
1532                         if (mode == GHOST_kGrabHide) {
1533                                 setWindowCursorVisibility(false);
1534                         }
1535                         
1536                         //Make window key if it wasn't to get the mouse move events
1537                         [m_window makeKeyWindow];
1538                         
1539                         //Dissociate cursor position even for warp mode, to allow mouse acceleration to work even when warping the cursor
1540                         err = CGAssociateMouseAndMouseCursorPosition(false) == kCGErrorSuccess ? GHOST_kSuccess : GHOST_kFailure;
1541                         
1542                         [pool drain];
1543                 }
1544         }
1545         else {
1546                 if (m_cursorGrab==GHOST_kGrabHide) {
1547                         //No need to set again cursor position, as it has not changed for Cocoa
1548                         setWindowCursorVisibility(true);
1549                 }
1550                 
1551                 err = CGAssociateMouseAndMouseCursorPosition(true) == kCGErrorSuccess ? GHOST_kSuccess : GHOST_kFailure;
1552                 /* Almost works without but important otherwise the mouse GHOST location can be incorrect on exit */
1553                 setCursorGrabAccum(0, 0);
1554                 m_cursorGrabBounds.m_l= m_cursorGrabBounds.m_r= -1; /* disable */
1555         }
1556         return err;
1557 }
1558         
1559 GHOST_TSuccess GHOST_WindowCocoa::setWindowCursorShape(GHOST_TStandardCursor shape)
1560 {
1561         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1562
1563         if (m_customCursor) {
1564                 [m_customCursor release];
1565                 m_customCursor = nil;
1566         }
1567
1568         if ([m_window isVisible]) {
1569                 loadCursor(getCursorVisibility(), shape);
1570         }
1571         
1572         [pool drain];
1573         return GHOST_kSuccess;
1574 }
1575
1576 /** Reverse the bits in a GHOST_TUns8
1577 static GHOST_TUns8 uns8ReverseBits(GHOST_TUns8 ch)
1578 {
1579         ch= ((ch >> 1) & 0x55) | ((ch << 1) & 0xAA);
1580         ch= ((ch >> 2) & 0x33) | ((ch << 2) & 0xCC);
1581         ch= ((ch >> 4) & 0x0F) | ((ch << 4) & 0xF0);
1582         return ch;
1583 }
1584 */
1585
1586
1587 /** Reverse the bits in a GHOST_TUns16 */
1588 static GHOST_TUns16 uns16ReverseBits(GHOST_TUns16 shrt)
1589 {
1590         shrt = ((shrt >> 1) & 0x5555) | ((shrt << 1) & 0xAAAA);
1591         shrt = ((shrt >> 2) & 0x3333) | ((shrt << 2) & 0xCCCC);
1592         shrt = ((shrt >> 4) & 0x0F0F) | ((shrt << 4) & 0xF0F0);
1593         shrt = ((shrt >> 8) & 0x00FF) | ((shrt << 8) & 0xFF00);
1594         return shrt;
1595 }
1596
1597 GHOST_TSuccess GHOST_WindowCocoa::setWindowCustomCursorShape(GHOST_TUns8 *bitmap, GHOST_TUns8 *mask,
1598                                                              int sizex, int sizey, int hotX, int hotY, int fg_color, int bg_color)
1599 {
1600         int y,nbUns16;
1601         NSPoint hotSpotPoint;
1602         NSBitmapImageRep *cursorImageRep;
1603         NSImage *cursorImage;
1604         NSSize imSize;
1605         GHOST_TUns16 *cursorBitmap;
1606         
1607         
1608         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1609         
1610         if (m_customCursor) {
1611                 [m_customCursor release];
1612                 m_customCursor = nil;
1613         }
1614         
1615
1616         cursorImageRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:nil
1617                                                                                                                          pixelsWide:sizex
1618                                                                                                                          pixelsHigh:sizey
1619                                                                                                                   bitsPerSample:1 
1620                                                                                                                 samplesPerPixel:2
1621                                                                                                                            hasAlpha:YES
1622                                                                                                                            isPlanar:YES
1623                                                                                                                  colorSpaceName:NSDeviceWhiteColorSpace
1624                                                                                                                         bytesPerRow:(sizex/8 + (sizex%8 >0 ?1:0))
1625                                                                                                                    bitsPerPixel:1];
1626         
1627         
1628         cursorBitmap = (GHOST_TUns16*)[cursorImageRep bitmapData];
1629         nbUns16 = [cursorImageRep bytesPerPlane]/2;
1630         
1631         for (y=0; y<nbUns16; y++) {
1632 #if !defined(__LITTLE_ENDIAN__)
1633                 cursorBitmap[y] = ~uns16ReverseBits((bitmap[2*y]<<0) | (bitmap[2*y+1]<<8));
1634                 cursorBitmap[nbUns16+y] = uns16ReverseBits((mask[2*y]<<0) | (mask[2*y+1]<<8));
1635 #else
1636                 cursorBitmap[y] = ~uns16ReverseBits((bitmap[2*y+1]<<0) | (bitmap[2*y]<<8));
1637                 cursorBitmap[nbUns16+y] = uns16ReverseBits((mask[2*y+1]<<0) | (mask[2*y]<<8));
1638 #endif
1639                 
1640         }
1641         
1642         
1643         imSize.width = sizex;
1644         imSize.height= sizey;
1645         cursorImage = [[NSImage alloc] initWithSize:imSize];
1646         [cursorImage addRepresentation:cursorImageRep];
1647         
1648         hotSpotPoint.x = hotX;
1649         hotSpotPoint.y = hotY;
1650         
1651         //foreground and background color parameter is not handled for now (10.6)
1652         m_customCursor = [[NSCursor alloc] initWithImage:cursorImage
1653                                                                                          hotSpot:hotSpotPoint];
1654         
1655         [cursorImageRep release];
1656         [cursorImage release];
1657         
1658         if ([m_window isVisible]) {
1659                 loadCursor(getCursorVisibility(), GHOST_kStandardCursorCustom);
1660         }
1661         [pool drain];
1662         return GHOST_kSuccess;
1663 }
1664
1665 GHOST_TSuccess GHOST_WindowCocoa::setWindowCustomCursorShape(GHOST_TUns8 bitmap[16][2], 
1666                                                              GHOST_TUns8 mask[16][2], int hotX, int hotY)
1667 {
1668         return setWindowCustomCursorShape((GHOST_TUns8*)bitmap, (GHOST_TUns8*) mask, 16, 16, hotX, hotY, 0, 1);
1669 }