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