macOS: officially upgrade to 10.9 libraries from lib/darwin.
[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         [m_openGLView setSystemAndWindowCocoa:systemCocoa windowCocoa:this];
556         
557         [m_window setContentView:m_openGLView];
558         [m_window setInitialFirstResponder:m_openGLView];
559         
560         [m_window makeKeyAndOrderFront:nil];
561         
562         setDrawingContextType(type);
563         updateDrawingContext();
564         activateDrawingContext();
565
566         // XXX jwilkins: This seems like it belongs in GHOST_ContextCGL, but probably not GHOST_ContextEGL
567         if (m_systemCocoa->m_nativePixel) {
568                 if ([m_openGLView respondsToSelector:@selector(setWantsBestResolutionOpenGLSurface:)]) {
569                         [m_openGLView setWantsBestResolutionOpenGLSurface:YES];
570                 
571                         NSRect backingBounds = [m_openGLView convertRectToBacking:[m_openGLView bounds]];
572                         m_nativePixelSize = (float)backingBounds.size.width / (float)rect.size.width;
573                 }
574         }
575         
576         setTitle(title);
577         
578         m_tablet.Active = GHOST_kTabletModeNone;
579         
580         CocoaWindowDelegate *windowDelegate = [[CocoaWindowDelegate alloc] init];
581         [windowDelegate setSystemAndWindowCocoa:systemCocoa windowCocoa:this];
582         [m_window setDelegate:windowDelegate];
583         
584         [m_window setAcceptsMouseMovedEvents:YES];
585         
586         NSView *view = [m_window contentView];
587         [view setAcceptsTouchEvents:YES];
588         
589         [m_window registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType,
590                                            NSStringPboardType, NSTIFFPboardType, nil]];
591         
592         if (state != GHOST_kWindowStateFullScreen) {
593                 [m_window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
594         }
595         
596         if (state == GHOST_kWindowStateFullScreen)
597                 setState(GHOST_kWindowStateFullScreen);
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         NSPoint screenCoord;
869         NSPoint baseCoord;
870         
871         screenCoord.x = inX;
872         screenCoord.y = inY;
873         
874         baseCoord = [m_window convertScreenToBase:screenCoord];
875         
876         outX = baseCoord.x;
877         outY = baseCoord.y;
878 }
879
880 void GHOST_WindowCocoa::clientToScreenIntern(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
881 {
882         NSPoint screenCoord;
883         NSPoint baseCoord;
884         
885         baseCoord.x = inX;
886         baseCoord.y = inY;
887         
888         screenCoord = [m_window convertBaseToScreen:baseCoord];
889         
890         outX = screenCoord.x;
891         outY = screenCoord.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 #if !defined(WITH_GL_EGL)
1000
1001 #if defined(WITH_GL_PROFILE_CORE)
1002                 GHOST_Context *context = new GHOST_ContextCGL(
1003                         m_wantStereoVisual,
1004                         m_wantNumOfAASamples,
1005                         m_window,
1006                         m_openGLView,
1007                         GL_CONTEXT_CORE_PROFILE_BIT,
1008                         3, 2,
1009                         GHOST_OPENGL_CGL_CONTEXT_FLAGS,
1010                         GHOST_OPENGL_CGL_RESET_NOTIFICATION_STRATEGY);
1011 #elif defined(WITH_GL_PROFILE_ES20)
1012                 GHOST_Context *context = new GHOST_ContextCGL(
1013                         m_wantStereoVisual,
1014                         m_wantNumOfAASamples,
1015                         m_window,
1016                         m_openGLView,
1017                         CGL_CONTEXT_ES2_PROFILE_BIT_EXT,
1018                         2, 0,
1019                         GHOST_OPENGL_CGL_CONTEXT_FLAGS,
1020                         GHOST_OPENGL_CGL_RESET_NOTIFICATION_STRATEGY);
1021 #elif defined(WITH_GL_PROFILE_COMPAT)
1022                 GHOST_Context *context = new GHOST_ContextCGL(
1023                         m_wantStereoVisual,
1024                         m_wantNumOfAASamples,
1025                         m_window,
1026                         m_openGLView,
1027                         0, // profile bit
1028                         0, 0,
1029                         m_debug_context,
1030                         GHOST_OPENGL_CGL_RESET_NOTIFICATION_STRATEGY);
1031 #else
1032 #  error
1033 #endif
1034
1035 #else
1036
1037 #if defined(WITH_GL_PROFILE_CORE)
1038                 GHOST_Context *context = new GHOST_ContextEGL(
1039                         m_wantStereoVisual,
1040                         m_wantNumOfAASamples,
1041                         m_window,
1042                         m_openGLView,
1043                         EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,
1044                         3, 2,
1045                         GHOST_OPENGL_EGL_CONTEXT_FLAGS,
1046                         GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY,
1047                         EGL_OPENGL_API);
1048 #elif defined(WITH_GL_PROFILE_ES20)
1049                 GHOST_Context *context = new GHOST_ContextEGL(
1050                         m_wantStereoVisual,
1051                         m_wantNumOfAASamples,
1052                         m_window,
1053                         m_openGLView,
1054                         0, // profile bit
1055                         2, 0,
1056                         GHOST_OPENGL_EGL_CONTEXT_FLAGS,
1057                         GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY,
1058                         EGL_OPENGL_ES_API);
1059 #elif defined(WITH_GL_PROFILE_COMPAT)
1060                 GHOST_Context *context = new GHOST_ContextEGL(
1061                         m_wantStereoVisual,
1062                         m_wantNumOfAASamples,
1063                         m_window,
1064                         m_openGLView,
1065                         0, // profile bit
1066                         0, 0,
1067                         GHOST_OPENGL_EGL_CONTEXT_FLAGS,
1068                         GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY,
1069                         EGL_OPENGL_API);
1070 #else
1071 #  error
1072 #endif
1073
1074 #endif
1075                 if (context->initializeDrawingContext())
1076                         return context;
1077                 else
1078                         delete context;
1079         }
1080
1081         return NULL;
1082 }
1083
1084 #pragma mark invalidate
1085
1086 GHOST_TSuccess GHOST_WindowCocoa::invalidate()
1087 {
1088         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::invalidate(): window invalid");
1089         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1090         [m_openGLView setNeedsDisplay:YES];
1091         [pool drain];
1092         return GHOST_kSuccess;
1093 }
1094
1095 #pragma mark Progress bar
1096
1097 GHOST_TSuccess GHOST_WindowCocoa::setProgressBar(float progress)
1098 {
1099         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1100         
1101         if ((progress >=0.0) && (progress <=1.0)) {
1102                 NSImage* dockIcon = [[NSImage alloc] initWithSize:NSMakeSize(128,128)];
1103                 
1104                 [dockIcon lockFocus];
1105                 NSRect progressBox = {{4, 4}, {120, 16}};
1106
1107                 [[NSImage imageNamed:@"NSApplicationIcon"] drawAtPoint:NSZeroPoint fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0];
1108
1109                 // Track & Outline
1110                 [[NSColor blackColor] setFill];
1111                 NSRectFill(progressBox);
1112
1113                 [[NSColor whiteColor] set];
1114                 NSFrameRect(progressBox);
1115
1116                 // Progress fill
1117                 progressBox = NSInsetRect(progressBox, 1, 1);
1118
1119                 progressBox.size.width = progressBox.size.width * progress;
1120                 NSGradient *gradient = [[NSGradient alloc] initWithStartingColor:[NSColor darkGrayColor] endingColor:[NSColor lightGrayColor]];
1121                 [gradient drawInRect:progressBox angle:90];
1122                 [gradient release];
1123                 
1124                 [dockIcon unlockFocus];
1125                 
1126                 [NSApp setApplicationIconImage:dockIcon];
1127                 [dockIcon release];
1128                 
1129                 m_progressBarVisible = true;
1130         }
1131         
1132         [pool drain];
1133         return GHOST_kSuccess;
1134 }
1135
1136 static void postNotification()
1137 {
1138         NSUserNotification *notification = [[NSUserNotification alloc] init];
1139         notification.title = @"Blender progress notification";
1140         notification.informativeText = @"Calculation is finished";
1141         notification.soundName = NSUserNotificationDefaultSoundName;
1142         [[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:notification];
1143         [notification release];
1144 }
1145
1146 GHOST_TSuccess GHOST_WindowCocoa::endProgressBar()
1147 {
1148         if (!m_progressBarVisible) return GHOST_kFailure;
1149         m_progressBarVisible = false;
1150         
1151         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1152         
1153         NSImage* dockIcon = [[NSImage alloc] initWithSize:NSMakeSize(128,128)];
1154         [dockIcon lockFocus];
1155         [[NSImage imageNamed:@"NSApplicationIcon"] drawAtPoint:NSZeroPoint fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0];
1156         [dockIcon unlockFocus];
1157         [NSApp setApplicationIconImage:dockIcon];
1158         
1159         // We use notifications to inform the user when the progress reached 100%
1160         // Atm. just fire this when the progressbar ends, the behavior is controlled
1161         // in the NotificationCenter If Blender is not frontmost window, a message
1162         // pops up with sound, in any case an entry in notifications
1163         if ([NSUserNotificationCenter respondsToSelector:@selector(defaultUserNotificationCenter)]) {
1164                 postNotification();
1165         }
1166
1167         [dockIcon release];
1168         
1169         [pool drain];
1170         return GHOST_kSuccess;
1171 }
1172
1173 #pragma mark Cursor handling
1174
1175 void GHOST_WindowCocoa::loadCursor(bool visible, GHOST_TStandardCursor cursor) const
1176 {
1177         static bool systemCursorVisible = true;
1178         
1179         NSCursor *tmpCursor =nil;
1180         
1181         if (visible != systemCursorVisible) {
1182                 if (visible) {
1183                         [NSCursor unhide];
1184                         systemCursorVisible = true;
1185                 }
1186                 else {
1187                         [NSCursor hide];
1188                         systemCursorVisible = false;
1189                 }
1190         }
1191
1192         if (cursor == GHOST_kStandardCursorCustom && m_customCursor) {
1193                 tmpCursor = m_customCursor;
1194         }
1195         else {
1196                 switch (cursor) {
1197                         case GHOST_kStandardCursorDestroy:
1198                                 tmpCursor = [NSCursor disappearingItemCursor];
1199                                 break;
1200                         case GHOST_kStandardCursorText:
1201                                 tmpCursor = [NSCursor IBeamCursor];
1202                                 break;
1203                         case GHOST_kStandardCursorCrosshair:
1204                                 tmpCursor = [NSCursor crosshairCursor];
1205                                 break;
1206                         case GHOST_kStandardCursorUpDown:
1207                                 tmpCursor = [NSCursor resizeUpDownCursor];
1208                                 break;
1209                         case GHOST_kStandardCursorLeftRight:
1210                                 tmpCursor = [NSCursor resizeLeftRightCursor];
1211                                 break;
1212                         case GHOST_kStandardCursorTopSide:
1213                                 tmpCursor = [NSCursor resizeUpCursor];
1214                                 break;
1215                         case GHOST_kStandardCursorBottomSide:
1216                                 tmpCursor = [NSCursor resizeDownCursor];
1217                                 break;
1218                         case GHOST_kStandardCursorLeftSide:
1219                                 tmpCursor = [NSCursor resizeLeftCursor];
1220                                 break;
1221                         case GHOST_kStandardCursorRightSide:
1222                                 tmpCursor = [NSCursor resizeRightCursor];
1223                                 break;
1224                         case GHOST_kStandardCursorRightArrow:
1225                         case GHOST_kStandardCursorInfo:
1226                         case GHOST_kStandardCursorLeftArrow:
1227                         case GHOST_kStandardCursorHelp:
1228                         case GHOST_kStandardCursorCycle:
1229                         case GHOST_kStandardCursorSpray:
1230                         case GHOST_kStandardCursorWait:
1231                         case GHOST_kStandardCursorTopLeftCorner:
1232                         case GHOST_kStandardCursorTopRightCorner:
1233                         case GHOST_kStandardCursorBottomRightCorner:
1234                         case GHOST_kStandardCursorBottomLeftCorner:
1235                         case GHOST_kStandardCursorCopy:
1236                         case GHOST_kStandardCursorDefault:
1237                         default:
1238                                 tmpCursor = [NSCursor arrowCursor];
1239                                 break;
1240                 };
1241         }
1242         [tmpCursor set];
1243 }
1244
1245
1246
1247 GHOST_TSuccess GHOST_WindowCocoa::setWindowCursorVisibility(bool visible)
1248 {
1249         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
1250         
1251         if ([m_window isVisible]) {
1252                 loadCursor(visible, getCursorShape());
1253         }
1254         
1255         [pool drain];
1256         return GHOST_kSuccess;
1257 }
1258
1259
1260 GHOST_TSuccess GHOST_WindowCocoa::setWindowCursorGrab(GHOST_TGrabCursorMode mode)
1261 {
1262         GHOST_TSuccess err = GHOST_kSuccess;
1263         
1264         if (mode != GHOST_kGrabDisable) {
1265                 //No need to perform grab without warp as it is always on in OS X
1266                 if (mode != GHOST_kGrabNormal) {
1267                         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1268
1269                         m_systemCocoa->getCursorPosition(m_cursorGrabInitPos[0], m_cursorGrabInitPos[1]);
1270                         setCursorGrabAccum(0, 0);
1271                         
1272                         if (mode == GHOST_kGrabHide) {
1273                                 setWindowCursorVisibility(false);
1274                         }
1275                         
1276                         //Make window key if it wasn't to get the mouse move events
1277                         [m_window makeKeyWindow];
1278                         
1279                         [pool drain];
1280                 }
1281         }
1282         else {
1283                 if (m_cursorGrab==GHOST_kGrabHide) {
1284                         m_systemCocoa->setCursorPosition(m_cursorGrabInitPos[0], m_cursorGrabInitPos[1]);
1285                         setWindowCursorVisibility(true);
1286                 }
1287                 
1288                 /* Almost works without but important otherwise the mouse GHOST location can be incorrect on exit */
1289                 setCursorGrabAccum(0, 0);
1290                 m_cursorGrabBounds.m_l= m_cursorGrabBounds.m_r= -1; /* disable */
1291         }
1292         return err;
1293 }
1294         
1295 GHOST_TSuccess GHOST_WindowCocoa::setWindowCursorShape(GHOST_TStandardCursor shape)
1296 {
1297         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1298
1299         if (m_customCursor) {
1300                 [m_customCursor release];
1301                 m_customCursor = nil;
1302         }
1303
1304         if ([m_window isVisible]) {
1305                 loadCursor(getCursorVisibility(), shape);
1306         }
1307         
1308         [pool drain];
1309         return GHOST_kSuccess;
1310 }
1311
1312 /** Reverse the bits in a GHOST_TUns8
1313 static GHOST_TUns8 uns8ReverseBits(GHOST_TUns8 ch)
1314 {
1315         ch= ((ch >> 1) & 0x55) | ((ch << 1) & 0xAA);
1316         ch= ((ch >> 2) & 0x33) | ((ch << 2) & 0xCC);
1317         ch= ((ch >> 4) & 0x0F) | ((ch << 4) & 0xF0);
1318         return ch;
1319 }
1320 */
1321
1322
1323 /** Reverse the bits in a GHOST_TUns16 */
1324 static GHOST_TUns16 uns16ReverseBits(GHOST_TUns16 shrt)
1325 {
1326         shrt = ((shrt >> 1) & 0x5555) | ((shrt << 1) & 0xAAAA);
1327         shrt = ((shrt >> 2) & 0x3333) | ((shrt << 2) & 0xCCCC);
1328         shrt = ((shrt >> 4) & 0x0F0F) | ((shrt << 4) & 0xF0F0);
1329         shrt = ((shrt >> 8) & 0x00FF) | ((shrt << 8) & 0xFF00);
1330         return shrt;
1331 }
1332
1333 GHOST_TSuccess GHOST_WindowCocoa::setWindowCustomCursorShape(GHOST_TUns8 *bitmap, GHOST_TUns8 *mask,
1334                                                              int sizex, int sizey, int hotX, int hotY, int fg_color, int bg_color)
1335 {
1336         int y,nbUns16;
1337         NSPoint hotSpotPoint;
1338         NSBitmapImageRep *cursorImageRep;
1339         NSImage *cursorImage;
1340         NSSize imSize;
1341         GHOST_TUns16 *cursorBitmap;
1342         
1343         
1344         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1345         
1346         if (m_customCursor) {
1347                 [m_customCursor release];
1348                 m_customCursor = nil;
1349         }
1350         
1351
1352         cursorImageRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:nil
1353                                                                                                                         pixelsWide:sizex
1354                                                                                                                         pixelsHigh:sizey
1355                                                                                                                         bitsPerSample:1
1356                                                                                                                         samplesPerPixel:2
1357                                                                                                                         hasAlpha:YES
1358                                                                                                                         isPlanar:YES
1359                                                                                                                         colorSpaceName:NSDeviceWhiteColorSpace
1360                                                                                                                         bytesPerRow:(sizex/8 + (sizex%8 >0 ?1:0))
1361                                                                                                                         bitsPerPixel:1];
1362         
1363         
1364         cursorBitmap = (GHOST_TUns16*)[cursorImageRep bitmapData];
1365         nbUns16 = [cursorImageRep bytesPerPlane]/2;
1366         
1367         for (y=0; y<nbUns16; y++) {
1368 #if !defined(__LITTLE_ENDIAN__)
1369                 cursorBitmap[y] = ~uns16ReverseBits((bitmap[2*y]<<0) | (bitmap[2*y+1]<<8));
1370                 cursorBitmap[nbUns16+y] = uns16ReverseBits((mask[2*y]<<0) | (mask[2*y+1]<<8));
1371 #else
1372                 cursorBitmap[y] = ~uns16ReverseBits((bitmap[2*y+1]<<0) | (bitmap[2*y]<<8));
1373                 cursorBitmap[nbUns16+y] = uns16ReverseBits((mask[2*y+1]<<0) | (mask[2*y]<<8));
1374 #endif
1375                 
1376         }
1377         
1378         
1379         imSize.width = sizex;
1380         imSize.height= sizey;
1381         cursorImage = [[NSImage alloc] initWithSize:imSize];
1382         [cursorImage addRepresentation:cursorImageRep];
1383         
1384         hotSpotPoint.x = hotX;
1385         hotSpotPoint.y = hotY;
1386         
1387         //foreground and background color parameter is not handled for now (10.6)
1388         m_customCursor = [[NSCursor alloc] initWithImage:cursorImage
1389                                                          hotSpot:hotSpotPoint];
1390         
1391         [cursorImageRep release];
1392         [cursorImage release];
1393         
1394         if ([m_window isVisible]) {
1395                 loadCursor(getCursorVisibility(), GHOST_kStandardCursorCustom);
1396         }
1397         [pool drain];
1398         return GHOST_kSuccess;
1399 }
1400
1401 GHOST_TSuccess GHOST_WindowCocoa::setWindowCustomCursorShape(GHOST_TUns8 bitmap[16][2], 
1402                                                              GHOST_TUns8 mask[16][2], int hotX, int hotY)
1403 {
1404         return setWindowCustomCursorShape((GHOST_TUns8*)bitmap, (GHOST_TUns8*) mask, 16, 16, hotX, hotY, 0, 1);
1405 }