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