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