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