Cocoa : fix drag and drop was no more operational after having fullscreened the window
[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 - (void) drawRect:(NSRect)rect
284 {
285     if ([self inLiveResize])
286     {
287         //Don't redraw while in live resize
288     }
289     else
290     {
291         [super drawRect:rect];
292     }
293 }
294
295 @end
296
297
298 #pragma mark initialization / finalization
299
300 NSOpenGLContext* GHOST_WindowCocoa::s_firstOpenGLcontext = nil;
301
302 GHOST_WindowCocoa::GHOST_WindowCocoa(
303         GHOST_SystemCocoa *systemCocoa,
304         const STR_String& title,
305         GHOST_TInt32 left,
306         GHOST_TInt32 top,
307         GHOST_TUns32 width,
308         GHOST_TUns32 height,
309         GHOST_TWindowState state,
310         GHOST_TDrawingContextType type,
311         const bool stereoVisual, const GHOST_TUns16 numOfAASamples
312 ) :
313         GHOST_Window(title, left, top, width, height, state, GHOST_kDrawingContextTypeNone, stereoVisual, numOfAASamples),
314         m_customCursor(0)
315 {
316         NSOpenGLPixelFormatAttribute pixelFormatAttrsWindow[40];
317         NSOpenGLPixelFormat *pixelFormat = nil;
318         int i;
319                 
320         m_systemCocoa = systemCocoa;
321         m_fullScreen = false;
322         
323         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
324         
325
326         //Creates the window
327         NSRect rect;
328         NSSize  minSize;
329         
330         rect.origin.x = left;
331         rect.origin.y = top;
332         rect.size.width = width;
333         rect.size.height = height;
334         
335         m_window = [[CocoaWindow alloc] initWithContentRect:rect
336                                                                                    styleMask:NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask | NSMiniaturizableWindowMask
337                                                                                          backing:NSBackingStoreBuffered defer:NO];
338         if (m_window == nil) {
339                 [pool drain];
340                 return;
341         }
342         
343         [m_window setSystemAndWindowCocoa:systemCocoa windowCocoa:this];
344         
345         //Forbid to resize the window below the blender defined minimum one
346         minSize.width = 320;
347         minSize.height = 240;
348         [m_window setContentMinSize:minSize];
349         
350         setTitle(title);
351         
352         
353         // Pixel Format Attributes for the windowed NSOpenGLContext
354         i=0;
355         pixelFormatAttrsWindow[i++] = NSOpenGLPFADoubleBuffer;
356         
357         // Guarantees the back buffer contents to be valid after a call to NSOpenGLContext object’s flushBuffer
358         // needed for 'Draw Overlap' drawing method
359         pixelFormatAttrsWindow[i++] = NSOpenGLPFABackingStore; 
360         
361         pixelFormatAttrsWindow[i++] = NSOpenGLPFAAccelerated;
362         //pixelFormatAttrsWindow[i++] = NSOpenGLPFAAllowOfflineRenderers,;   // Removed to allow 10.4 builds, and 2 GPUs rendering is not used anyway
363
364         pixelFormatAttrsWindow[i++] = NSOpenGLPFADepthSize;
365         pixelFormatAttrsWindow[i++] = (NSOpenGLPixelFormatAttribute) 32;
366         
367         
368         if (stereoVisual) pixelFormatAttrsWindow[i++] = NSOpenGLPFAStereo;
369         
370         if (numOfAASamples>0) {
371                 // Multisample anti-aliasing
372                 pixelFormatAttrsWindow[i++] = NSOpenGLPFAMultisample;
373                 
374                 pixelFormatAttrsWindow[i++] = NSOpenGLPFASampleBuffers;
375                 pixelFormatAttrsWindow[i++] = (NSOpenGLPixelFormatAttribute) 1;
376                 
377                 pixelFormatAttrsWindow[i++] = NSOpenGLPFASamples;
378                 pixelFormatAttrsWindow[i++] = (NSOpenGLPixelFormatAttribute) numOfAASamples;
379                 
380                 pixelFormatAttrsWindow[i++] = NSOpenGLPFANoRecovery;
381         }
382         
383         pixelFormatAttrsWindow[i] = (NSOpenGLPixelFormatAttribute) 0;
384         
385         pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:pixelFormatAttrsWindow];
386         
387         
388         //Fall back to no multisampling if Antialiasing init failed
389         if (pixelFormat == nil) {
390                 i=0;
391                 pixelFormatAttrsWindow[i++] = NSOpenGLPFADoubleBuffer;
392                 
393                 // Guarantees the back buffer contents to be valid after a call to NSOpenGLContext object’s flushBuffer
394                 // needed for 'Draw Overlap' drawing method
395                 pixelFormatAttrsWindow[i++] = NSOpenGLPFABackingStore;
396                 
397                 pixelFormatAttrsWindow[i++] = NSOpenGLPFAAccelerated;
398                 //pixelFormatAttrsWindow[i++] = NSOpenGLPFAAllowOfflineRenderers,;   // Removed to allow 10.4 builds, and 2 GPUs rendering is not used anyway
399                 
400                 pixelFormatAttrsWindow[i++] = NSOpenGLPFADepthSize;
401                 pixelFormatAttrsWindow[i++] = (NSOpenGLPixelFormatAttribute) 32;
402                 
403                 if (stereoVisual) pixelFormatAttrsWindow[i++] = NSOpenGLPFAStereo;
404                 
405                 pixelFormatAttrsWindow[i] = (NSOpenGLPixelFormatAttribute) 0;
406                 
407                 pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:pixelFormatAttrsWindow];
408                 
409         }
410         
411         if (numOfAASamples>0) { //Set m_numOfAASamples to the actual value
412                 GLint gli;
413                 [pixelFormat getValues:&gli forAttribute:NSOpenGLPFASamples forVirtualScreen:0];
414                 if (m_numOfAASamples != (GHOST_TUns16)gli) {
415                         m_numOfAASamples = (GHOST_TUns16)gli;
416                         printf("GHOST_Window could be created with anti-aliasing of only %i samples\n",m_numOfAASamples);
417                 }
418         }
419                 
420         //Creates the OpenGL View inside the window
421         m_openGLView = [[CocoaOpenGLView alloc] initWithFrame:rect
422                                                                                                  pixelFormat:pixelFormat];
423         
424         [pixelFormat release];
425         
426         m_openGLContext = [m_openGLView openGLContext]; //This context will be replaced by the proper one just after
427         
428         [m_window setContentView:m_openGLView];
429         [m_window setInitialFirstResponder:m_openGLView];
430         
431         [m_window setReleasedWhenClosed:NO]; //To avoid bad pointer exception in case of user closing the window
432         
433         [m_window makeKeyAndOrderFront:nil];
434         
435         setDrawingContextType(type);
436         updateDrawingContext();
437         activateDrawingContext();
438         
439         m_tablet.Active = GHOST_kTabletModeNone;
440         
441         CocoaWindowDelegate *windowDelegate = [[CocoaWindowDelegate alloc] init];
442         [windowDelegate setSystemAndWindowCocoa:systemCocoa windowCocoa:this];
443         [m_window setDelegate:windowDelegate];
444         
445         [m_window setAcceptsMouseMovedEvents:YES];
446         
447         [m_window registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType,
448                                                                                   NSStringPboardType, NSTIFFPboardType, nil]];
449                                                                                   
450         if (state == GHOST_kWindowStateFullScreen)
451                 setState(GHOST_kWindowStateFullScreen);
452                 
453         [pool drain];
454 }
455
456
457 GHOST_WindowCocoa::~GHOST_WindowCocoa()
458 {
459         if (m_customCursor) delete m_customCursor;
460
461     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
462         [m_openGLView release];
463         
464         if (m_window) {
465                 [m_window close];
466                 [[m_window delegate] release];
467                 [m_window release];
468                 m_window = nil;
469         }
470         
471         //Check for other blender opened windows and make the frontmost key
472         NSArray *windowsList = [NSApp orderedWindows];
473         if ([windowsList count]) {
474                 [[windowsList objectAtIndex:0] makeKeyAndOrderFront:nil];
475         }
476         [pool drain];
477 }
478
479 #pragma mark accessors
480
481 bool GHOST_WindowCocoa::getValid() const
482 {
483         return (m_window != 0);
484 }
485
486 void* GHOST_WindowCocoa::getOSWindow() const
487 {
488         return (void*)m_window;
489 }
490
491 void GHOST_WindowCocoa::setTitle(const STR_String& title)
492 {
493     GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setTitle(): window invalid")
494         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
495
496         NSString *windowTitle = [[NSString alloc] initWithCString:title encoding:NSISOLatin1StringEncoding];
497         
498         //Set associated file if applicable
499         if (windowTitle && [windowTitle hasPrefix:@"Blender"])
500         {
501                 NSRange fileStrRange;
502                 NSString *associatedFileName;
503                 int len;
504                 
505                 fileStrRange.location = [windowTitle rangeOfString:@"["].location+1;
506                 len = [windowTitle rangeOfString:@"]"].location - fileStrRange.location;
507         
508                 if (len >0)
509                 {
510                         fileStrRange.length = len;
511                         associatedFileName = [windowTitle substringWithRange:fileStrRange];
512                         [m_window setTitle:[associatedFileName lastPathComponent]];
513
514                         //Blender used file open/save functions converte file names into legal URL ones
515                         associatedFileName = [associatedFileName stringByAddingPercentEscapesUsingEncoding:NSISOLatin1StringEncoding];
516                         @try {
517                                 [m_window setRepresentedFilename:associatedFileName];
518                         }
519                         @catch (NSException * e) {
520                                 printf("\nInvalid file path given in window title");
521                         }
522                 }
523                 else {
524                         [m_window setTitle:windowTitle];
525                         [m_window setRepresentedFilename:@""];
526                 }
527
528         } else {
529                 [m_window setTitle:windowTitle];
530                 [m_window setRepresentedFilename:@""];
531         }
532
533         
534         [windowTitle release];
535         [pool drain];
536 }
537
538
539 void GHOST_WindowCocoa::getTitle(STR_String& title) const
540 {
541     GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getTitle(): window invalid")
542
543         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
544
545         NSString *windowTitle = [m_window title];
546
547         if (windowTitle != nil) {
548                 title = [windowTitle UTF8String];               
549         }
550         
551         [pool drain];
552 }
553
554
555 void GHOST_WindowCocoa::getWindowBounds(GHOST_Rect& bounds) const
556 {
557         NSRect rect;
558         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getWindowBounds(): window invalid")
559
560         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
561         
562         NSRect screenSize = [[m_window screen] visibleFrame];
563
564         rect = [m_window frame];
565
566         bounds.m_b = screenSize.size.height - (rect.origin.y -screenSize.origin.y);
567         bounds.m_l = rect.origin.x -screenSize.origin.x;
568         bounds.m_r = rect.origin.x-screenSize.origin.x + rect.size.width;
569         bounds.m_t = screenSize.size.height - (rect.origin.y + rect.size.height -screenSize.origin.y);
570         
571         [pool drain];
572 }
573
574
575 void GHOST_WindowCocoa::getClientBounds(GHOST_Rect& bounds) const
576 {
577         NSRect rect;
578         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getClientBounds(): window invalid")
579         
580         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
581         
582         if (!m_fullScreen)
583         {
584                 NSRect screenSize = [[m_window screen] visibleFrame];
585
586                 //Max window contents as screen size (excluding title bar...)
587                 NSRect contentRect = [CocoaWindow contentRectForFrameRect:screenSize
588                                                                                                          styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)];
589
590                 rect = [m_window contentRectForFrameRect:[m_window frame]];
591                 
592                 bounds.m_b = contentRect.size.height - (rect.origin.y -contentRect.origin.y);
593                 bounds.m_l = rect.origin.x -contentRect.origin.x;
594                 bounds.m_r = rect.origin.x-contentRect.origin.x + rect.size.width;
595                 bounds.m_t = contentRect.size.height - (rect.origin.y + rect.size.height -contentRect.origin.y);
596         }
597         else {
598                 NSRect screenSize = [[m_window screen] frame];
599                 
600                 bounds.m_b = screenSize.origin.y + screenSize.size.height;
601                 bounds.m_l = screenSize.origin.x;
602                 bounds.m_r = screenSize.origin.x + screenSize.size.width;
603                 bounds.m_t = screenSize.origin.y;
604         }
605         [pool drain];
606 }
607
608
609 GHOST_TSuccess GHOST_WindowCocoa::setClientWidth(GHOST_TUns32 width)
610 {
611         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setClientWidth(): window invalid")
612         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
613         GHOST_Rect cBnds, wBnds;
614         getClientBounds(cBnds);
615         if (((GHOST_TUns32)cBnds.getWidth()) != width) {
616                 NSSize size;
617                 size.width=width;
618                 size.height=cBnds.getHeight();
619                 [m_window setContentSize:size];
620         }
621         [pool drain];
622         return GHOST_kSuccess;
623 }
624
625
626 GHOST_TSuccess GHOST_WindowCocoa::setClientHeight(GHOST_TUns32 height)
627 {
628         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setClientHeight(): window invalid")
629         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
630         GHOST_Rect cBnds, wBnds;
631         getClientBounds(cBnds);
632         if (((GHOST_TUns32)cBnds.getHeight()) != height) {
633                 NSSize size;
634                 size.width=cBnds.getWidth();
635                 size.height=height;
636                 [m_window setContentSize:size];
637         }
638         [pool drain];
639         return GHOST_kSuccess;
640 }
641
642
643 GHOST_TSuccess GHOST_WindowCocoa::setClientSize(GHOST_TUns32 width, GHOST_TUns32 height)
644 {
645         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setClientSize(): window invalid")
646         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
647         GHOST_Rect cBnds, wBnds;
648         getClientBounds(cBnds);
649         if ((((GHOST_TUns32)cBnds.getWidth()) != width) ||
650             (((GHOST_TUns32)cBnds.getHeight()) != height)) {
651                 NSSize size;
652                 size.width=width;
653                 size.height=height;
654                 [m_window setContentSize:size];
655         }
656         [pool drain];
657         return GHOST_kSuccess;
658 }
659
660
661 GHOST_TWindowState GHOST_WindowCocoa::getState() const
662 {
663         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getState(): window invalid")
664         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
665         GHOST_TWindowState state;
666         if (m_fullScreen) {
667                 state = GHOST_kWindowStateFullScreen;
668         } 
669         else if ([m_window isMiniaturized]) {
670                 state = GHOST_kWindowStateMinimized;
671         }
672         else if ([m_window isZoomed]) {
673                 state = GHOST_kWindowStateMaximized;
674         }
675         else {
676                 state = GHOST_kWindowStateNormal;
677         }
678         [pool drain];
679         return state;
680 }
681
682
683 void GHOST_WindowCocoa::screenToClient(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
684 {
685         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::screenToClient(): window invalid")
686         
687         NSPoint screenCoord;
688         NSPoint baseCoord;
689         
690         screenCoord.x = inX;
691         screenCoord.y = inY;
692         
693         baseCoord = [m_window convertScreenToBase:screenCoord];
694         
695         outX = baseCoord.x;
696         outY = baseCoord.y;
697 }
698
699
700 void GHOST_WindowCocoa::clientToScreen(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
701 {
702         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::clientToScreen(): window invalid")
703         
704         NSPoint screenCoord;
705         NSPoint baseCoord;
706         
707         baseCoord.x = inX;
708         baseCoord.y = inY;
709         
710         screenCoord = [m_window convertBaseToScreen:baseCoord];
711         
712         outX = screenCoord.x;
713         outY = screenCoord.y;
714 }
715
716
717 NSScreen* GHOST_WindowCocoa::getScreen()
718 {
719         return [m_window screen];
720 }
721
722
723 /**
724  * @note Fullscreen switch is not actual fullscreen with display capture. As this capture removes all OS X window manager features.
725  * Instead, the menu bar and the dock are hidden, and the window is made borderless and enlarged.
726  * Thus, process switch, exposé, spaces, ... still work in fullscreen mode
727  */
728 GHOST_TSuccess GHOST_WindowCocoa::setState(GHOST_TWindowState state)
729 {
730         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setState(): window invalid")
731     switch (state) {
732                 case GHOST_kWindowStateMinimized:
733             [m_window miniaturize:nil];
734             break;
735                 case GHOST_kWindowStateMaximized:
736                         [m_window zoom:nil];
737                         break;
738                 
739                 case GHOST_kWindowStateFullScreen:
740                         if (!m_fullScreen)
741                         {
742                                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
743                         
744                                 //This status change needs to be done before Cocoa call to enter fullscreen mode
745                                 //to give window delegate hint not to forward its deactivation to ghost wm that doesn't know view/window difference
746                                 m_fullScreen = true;
747
748 #ifdef MAC_OS_X_VERSION_10_6
749                                 //10.6 provides Cocoa functions to autoshow menu bar, and to change a window style
750                                 //Hide menu & dock if needed
751                                 if ([[m_window screen] isEqual:[[NSScreen screens] objectAtIndex:0]])
752                                 {
753                                         [NSApp setPresentationOptions:(NSApplicationPresentationHideDock | NSApplicationPresentationAutoHideMenuBar)];
754                                 }
755                                 //Make window borderless and enlarge it
756                                 [m_window setStyleMask:NSBorderlessWindowMask];
757                                 [m_window setFrame:[[m_window screen] frame] display:YES];
758                                 [m_window makeFirstResponder:m_openGLView];
759 #else
760                                 //With 10.5, we need to create a new window to change its style to borderless
761                                 //Hide menu & dock if needed
762                                 if ([[m_window screen] isEqual:[[NSScreen screens] objectAtIndex:0]])
763                                 {
764                                         //Cocoa function in 10.5 does not allow to set the menu bar in auto-show mode [NSMenu setMenuBarVisible:NO];
765                                         //One of the very few 64bit compatible Carbon function
766                                         SetSystemUIMode(kUIModeAllHidden,kUIOptionAutoShowMenuBar);
767                                 }
768                                 //Create a fullscreen borderless window
769                                 CocoaWindow *tmpWindow = [[CocoaWindow alloc]
770                                                                                   initWithContentRect:[[m_window screen] frame]
771                                                                                   styleMask:NSBorderlessWindowMask
772                                                                                   backing:NSBackingStoreBuffered
773                                                                                   defer:YES];
774                                 //Copy current window parameters
775                                 [tmpWindow setTitle:[m_window title]];
776                                 [tmpWindow setRepresentedFilename:[m_window representedFilename]];
777                                 [tmpWindow setReleasedWhenClosed:NO];
778                                 [tmpWindow setAcceptsMouseMovedEvents:YES];
779                                 [tmpWindow setDelegate:[m_window delegate]];
780                                 [tmpWindow registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType,
781                                                                                                    NSStringPboardType, NSTIFFPboardType, nil]];
782                                 
783                                 //Assign the openGL view to the new window
784                                 [tmpWindow setContentView:m_openGLView];
785                                 
786                                 //Show the new window
787                                 [tmpWindow makeKeyAndOrderFront:m_openGLView];
788                                 //Close and release old window
789                                 [m_window setDelegate:nil]; // To avoid the notification of "window closed" event
790                                 [m_window close];
791                                 [m_window release];
792                                 m_window = tmpWindow;
793 #endif
794                         
795                                 //Tell WM of view new size
796                                 m_systemCocoa->handleWindowEvent(GHOST_kEventWindowSize, this);
797                                 
798                                 [pool drain];
799                                 }
800                         break;
801                 case GHOST_kWindowStateNormal:
802         default:
803                         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
804                         if (m_fullScreen)
805                         {
806                                 m_fullScreen = false;
807
808                                 //Exit fullscreen
809 #ifdef MAC_OS_X_VERSION_10_6
810                                 //Show again menu & dock if needed
811                                 if ([[m_window screen] isEqual:[NSScreen mainScreen]])
812                                 {
813                                         [NSApp setPresentationOptions:NSApplicationPresentationDefault];
814                                 }
815                                 //Make window normal and resize it
816                                 [m_window setStyleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)];
817                                 [m_window setFrame:[[m_window screen] visibleFrame] display:YES];
818                                 //TODO for 10.6 only : window title is forgotten after the style change
819                                 [m_window makeFirstResponder:m_openGLView];
820 #else
821                                 //With 10.5, we need to create a new window to change its style to borderless
822                                 //Show menu & dock if needed
823                                 if ([[m_window screen] isEqual:[NSScreen mainScreen]])
824                                 {
825                                         //Cocoa function in 10.5 does not allow to set the menu bar in auto-show mode [NSMenu setMenuBarVisible:YES];
826                                         SetSystemUIMode(kUIModeNormal, 0); //One of the very few 64bit compatible Carbon function
827                                 }
828                                 //Create a fullscreen borderless window
829                                 CocoaWindow *tmpWindow = [[CocoaWindow alloc]
830                                                                                   initWithContentRect:[[m_window screen] frame]
831                                                                                                         styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)
832                                                                                                           backing:NSBackingStoreBuffered
833                                                                                                                 defer:YES];
834                                 //Copy current window parameters
835                                 [tmpWindow setTitle:[m_window title]];
836                                 [tmpWindow setRepresentedFilename:[m_window representedFilename]];
837                                 [tmpWindow setReleasedWhenClosed:NO];
838                                 [tmpWindow setAcceptsMouseMovedEvents:YES];
839                                 [tmpWindow setDelegate:[m_window delegate]];
840                                 [tmpWindow registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType,
841                                                                                                    NSStringPboardType, NSTIFFPboardType, nil]];
842                                 //Forbid to resize the window below the blender defined minimum one
843                                 [tmpWindow setContentMinSize:NSMakeSize(320, 240)];
844                                 
845                                 //Assign the openGL view to the new window
846                                 [tmpWindow setContentView:m_openGLView];
847                                 
848                                 //Show the new window
849                                 [tmpWindow makeKeyAndOrderFront:nil];
850                                 //Close and release old window
851                                 [m_window setDelegate:nil]; // To avoid the notification of "window closed" event
852                                 [m_window close];
853                                 [m_window release];
854                                 m_window = tmpWindow;
855 #endif
856                         
857                                 //Tell WM of view new size
858                                 m_systemCocoa->handleWindowEvent(GHOST_kEventWindowSize, this);
859                         }
860             else if ([m_window isMiniaturized])
861                                 [m_window deminiaturize:nil];
862                         else if ([m_window isZoomed])
863                                 [m_window zoom:nil];
864                         [pool drain];
865             break;
866     }
867
868     return GHOST_kSuccess;
869 }
870
871 GHOST_TSuccess GHOST_WindowCocoa::setModifiedState(bool isUnsavedChanges)
872 {
873         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
874         
875         [m_window setDocumentEdited:isUnsavedChanges];
876         
877         [pool drain];
878         return GHOST_Window::setModifiedState(isUnsavedChanges);
879 }
880
881
882
883 GHOST_TSuccess GHOST_WindowCocoa::setOrder(GHOST_TWindowOrder order)
884 {
885         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
886         
887         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setOrder(): window invalid")
888     if (order == GHOST_kWindowOrderTop) {
889                 [m_window makeKeyAndOrderFront:nil];
890     }
891     else {
892                 NSArray *windowsList;
893                 
894                 [m_window orderBack:nil];
895                 
896                 //Check for other blender opened windows and make the frontmost key
897                 windowsList = [NSApp orderedWindows];
898                 if ([windowsList count]) {
899                         [[windowsList objectAtIndex:0] makeKeyAndOrderFront:nil];
900                 }
901     }
902         
903         [pool drain];
904     return GHOST_kSuccess;
905 }
906
907 #pragma mark Drawing context
908
909 /*#define  WAIT_FOR_VSYNC 1*/
910
911 GHOST_TSuccess GHOST_WindowCocoa::swapBuffers()
912 {
913     if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) {
914         if (m_openGLContext != nil) {
915                         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
916                         [m_openGLContext flushBuffer];
917                         [pool drain];
918             return GHOST_kSuccess;
919         }
920     }
921     return GHOST_kFailure;
922 }
923
924 GHOST_TSuccess GHOST_WindowCocoa::updateDrawingContext()
925 {
926         if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) {
927                 if (m_openGLContext != nil) {
928                         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
929                         [m_openGLContext update];
930                         [pool drain];
931                         return GHOST_kSuccess;
932                 }
933         }
934         return GHOST_kFailure;
935 }
936
937 GHOST_TSuccess GHOST_WindowCocoa::activateDrawingContext()
938 {
939         if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) {
940                 if (m_openGLContext != nil) {
941                         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
942                         [m_openGLContext makeCurrentContext];
943                         
944                         // Disable AA by default
945                         if (m_numOfAASamples > 0) glDisable(GL_MULTISAMPLE_ARB);
946                         [pool drain];
947                         return GHOST_kSuccess;
948                 }
949         }
950         return GHOST_kFailure;
951 }
952
953
954 GHOST_TSuccess GHOST_WindowCocoa::installDrawingContext(GHOST_TDrawingContextType type)
955 {
956         GHOST_TSuccess success = GHOST_kFailure;
957         
958         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
959         
960         NSOpenGLPixelFormat *pixelFormat;
961         NSOpenGLContext *tmpOpenGLContext;
962         
963         /***** Multithreaded opengl code : uncomment for enabling
964         CGLContextObj cglCtx;
965         */
966          
967         switch (type) {
968                 case GHOST_kDrawingContextTypeOpenGL:
969                         if (!getValid()) break;
970                                         
971                         pixelFormat = [m_openGLView pixelFormat];
972                         tmpOpenGLContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat
973                                                                                                                           shareContext:s_firstOpenGLcontext];
974                         if (tmpOpenGLContext == nil) {
975                                 success = GHOST_kFailure;
976                                 break;
977                         }
978                         
979                         //Switch openGL to multhreaded mode
980                         /******* Multithreaded opengl code : uncomment for enabling
981                         cglCtx = (CGLContextObj)[tmpOpenGLContext CGLContextObj];
982                         if (CGLEnable(cglCtx, kCGLCEMPEngine) == kCGLNoError)
983                                 printf("\nSwitched openGL to multithreaded mode");
984                          */
985                         
986                         if (!s_firstOpenGLcontext) s_firstOpenGLcontext = tmpOpenGLContext;
987 #ifdef WAIT_FOR_VSYNC
988                         {
989                                 GLint swapInt = 1;
990                                 /* wait for vsync, to avoid tearing artifacts */
991                                 [tmpOpenGLContext setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
992                         }
993 #endif
994                         [m_openGLView setOpenGLContext:tmpOpenGLContext];
995                         [tmpOpenGLContext setView:m_openGLView];
996                         
997                         m_openGLContext = tmpOpenGLContext;
998                         break;
999         
1000                 case GHOST_kDrawingContextTypeNone:
1001                         success = GHOST_kSuccess;
1002                         break;
1003                 
1004                 default:
1005                         break;
1006         }
1007         [pool drain];
1008         return success;
1009 }
1010
1011
1012 GHOST_TSuccess GHOST_WindowCocoa::removeDrawingContext()
1013 {
1014         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1015         switch (m_drawingContextType) {
1016                 case GHOST_kDrawingContextTypeOpenGL:
1017                         if (m_openGLContext)
1018                         {
1019                                 [m_openGLView clearGLContext];
1020                                 if (s_firstOpenGLcontext == m_openGLContext) s_firstOpenGLcontext = nil;
1021                                 m_openGLContext = nil;
1022                         }
1023                         [pool drain];
1024                         return GHOST_kSuccess;
1025                 case GHOST_kDrawingContextTypeNone:
1026                         [pool drain];
1027                         return GHOST_kSuccess;
1028                         break;
1029                 default:
1030                         [pool drain];
1031                         return GHOST_kFailure;
1032         }
1033 }
1034
1035
1036 GHOST_TSuccess GHOST_WindowCocoa::invalidate()
1037 {
1038         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::invalidate(): window invalid")
1039         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1040         [m_openGLView setNeedsDisplay:YES];
1041         [pool drain];
1042         return GHOST_kSuccess;
1043 }
1044
1045 #pragma mark Progress bar
1046
1047 GHOST_TSuccess GHOST_WindowCocoa::setProgressBar(float progress)
1048 {
1049         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1050         
1051         if ((progress >=0.0) && (progress <=1.0)) {
1052                 NSImage* dockIcon = [[NSImage alloc] initWithSize:NSMakeSize(128,128)];
1053                 
1054                 [dockIcon lockFocus];
1055         NSRect progressBox = {{4, 4}, {120, 16}};
1056
1057         [[NSImage imageNamed:@"NSApplicationIcon"] dissolveToPoint:NSZeroPoint fraction:1.0];
1058         
1059         // Track & Outline
1060         [[NSColor blackColor] setFill];
1061         NSRectFill(progressBox);
1062         
1063         [[NSColor whiteColor] set];
1064         NSFrameRect(progressBox);
1065         
1066         // Progress fill
1067         progressBox = NSInsetRect(progressBox, 1, 1);
1068         [[NSColor knobColor] setFill];
1069         progressBox.size.width = progressBox.size.width * progress;
1070                 NSRectFill(progressBox);
1071                 
1072                 [dockIcon unlockFocus];
1073                 
1074                 [NSApp setApplicationIconImage:dockIcon];
1075                 [dockIcon release];
1076                 
1077                 m_progressBarVisible = true;
1078         }
1079         
1080         [pool drain];
1081         return GHOST_kSuccess;
1082 }
1083
1084
1085 GHOST_TSuccess GHOST_WindowCocoa::endProgressBar()
1086 {
1087         if (!m_progressBarVisible) return GHOST_kFailure;
1088         m_progressBarVisible = false;
1089         
1090         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1091         
1092         NSImage* dockIcon = [[NSImage alloc] initWithSize:NSMakeSize(128,128)];
1093         [dockIcon lockFocus];
1094         [[NSImage imageNamed:@"NSApplicationIcon"] dissolveToPoint:NSZeroPoint fraction:1.0];
1095         [dockIcon unlockFocus];
1096         [NSApp setApplicationIconImage:dockIcon];
1097         [dockIcon release];
1098         
1099         [pool drain];
1100         return GHOST_kSuccess;
1101 }
1102
1103
1104
1105 #pragma mark Cursor handling
1106
1107 void GHOST_WindowCocoa::loadCursor(bool visible, GHOST_TStandardCursor cursor) const
1108 {
1109         static bool systemCursorVisible = true;
1110         
1111         NSCursor *tmpCursor =nil;
1112         
1113         if (visible != systemCursorVisible) {
1114                 if (visible) {
1115                         [NSCursor unhide];
1116                         systemCursorVisible = true;
1117                 }
1118                 else {
1119                         [NSCursor hide];
1120                         systemCursorVisible = false;
1121                 }
1122         }
1123
1124         if (cursor == GHOST_kStandardCursorCustom && m_customCursor) {
1125                 tmpCursor = m_customCursor;
1126         } else {
1127                 switch (cursor) {
1128                         case GHOST_kStandardCursorDestroy:
1129                                 tmpCursor = [NSCursor disappearingItemCursor];
1130                                 break;
1131                         case GHOST_kStandardCursorText:
1132                                 tmpCursor = [NSCursor IBeamCursor];
1133                                 break;
1134                         case GHOST_kStandardCursorCrosshair:
1135                                 tmpCursor = [NSCursor crosshairCursor];
1136                                 break;
1137                         case GHOST_kStandardCursorUpDown:
1138                                 tmpCursor = [NSCursor resizeUpDownCursor];
1139                                 break;
1140                         case GHOST_kStandardCursorLeftRight:
1141                                 tmpCursor = [NSCursor resizeLeftRightCursor];
1142                                 break;
1143                         case GHOST_kStandardCursorTopSide:
1144                                 tmpCursor = [NSCursor resizeUpCursor];
1145                                 break;
1146                         case GHOST_kStandardCursorBottomSide:
1147                                 tmpCursor = [NSCursor resizeDownCursor];
1148                                 break;
1149                         case GHOST_kStandardCursorLeftSide:
1150                                 tmpCursor = [NSCursor resizeLeftCursor];
1151                                 break;
1152                         case GHOST_kStandardCursorRightSide:
1153                                 tmpCursor = [NSCursor resizeRightCursor];
1154                                 break;
1155                         case GHOST_kStandardCursorRightArrow:
1156                         case GHOST_kStandardCursorInfo:
1157                         case GHOST_kStandardCursorLeftArrow:
1158                         case GHOST_kStandardCursorHelp:
1159                         case GHOST_kStandardCursorCycle:
1160                         case GHOST_kStandardCursorSpray:
1161                         case GHOST_kStandardCursorWait:
1162                         case GHOST_kStandardCursorTopLeftCorner:
1163                         case GHOST_kStandardCursorTopRightCorner:
1164                         case GHOST_kStandardCursorBottomRightCorner:
1165                         case GHOST_kStandardCursorBottomLeftCorner:
1166                         case GHOST_kStandardCursorCopy:
1167                         case GHOST_kStandardCursorDefault:
1168                         default:
1169                                 tmpCursor = [NSCursor arrowCursor];
1170                                 break;
1171                 };
1172         }
1173         [tmpCursor set];
1174 }
1175
1176
1177
1178 GHOST_TSuccess GHOST_WindowCocoa::setWindowCursorVisibility(bool visible)
1179 {
1180         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
1181         
1182         if ([m_window isVisible]) {
1183                 loadCursor(visible, getCursorShape());
1184         }
1185         
1186         [pool drain];
1187         return GHOST_kSuccess;
1188 }
1189
1190
1191 GHOST_TSuccess GHOST_WindowCocoa::setWindowCursorGrab(GHOST_TGrabCursorMode mode)
1192 {
1193         GHOST_TSuccess err = GHOST_kSuccess;
1194         
1195         if (mode != GHOST_kGrabDisable)
1196         {
1197                 //No need to perform grab without warp as it is always on in OS X
1198                 if(mode != GHOST_kGrabNormal) {
1199                         GHOST_TInt32 x_old,y_old;
1200                         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1201
1202                         m_systemCocoa->getCursorPosition(x_old,y_old);
1203                         screenToClient(x_old, y_old, m_cursorGrabInitPos[0], m_cursorGrabInitPos[1]);
1204                         //Warp position is stored in client (window base) coordinates
1205                         setCursorGrabAccum(0, 0);
1206                         
1207                         if(mode == GHOST_kGrabHide) {
1208                                 setWindowCursorVisibility(false);
1209                         }
1210                         
1211                         //Make window key if it wasn't to get the mouse move events
1212                         [m_window makeKeyWindow];
1213                         
1214                         //Dissociate cursor position even for warp mode, to allow mouse acceleration to work even when warping the cursor
1215                         err = CGAssociateMouseAndMouseCursorPosition(false) == kCGErrorSuccess ? GHOST_kSuccess : GHOST_kFailure;
1216                         
1217                         [pool drain];
1218                 }
1219         }
1220         else {
1221                 if(m_cursorGrab==GHOST_kGrabHide)
1222                 {
1223                         //No need to set again cursor position, as it has not changed for Cocoa
1224                         setWindowCursorVisibility(true);
1225                 }
1226                 
1227                 err = CGAssociateMouseAndMouseCursorPosition(true) == kCGErrorSuccess ? GHOST_kSuccess : GHOST_kFailure;
1228                 /* Almost works without but important otherwise the mouse GHOST location can be incorrect on exit */
1229                 setCursorGrabAccum(0, 0);
1230                 m_cursorGrabBounds.m_l= m_cursorGrabBounds.m_r= -1; /* disable */
1231         }
1232         return err;
1233 }
1234         
1235 GHOST_TSuccess GHOST_WindowCocoa::setWindowCursorShape(GHOST_TStandardCursor shape)
1236 {
1237         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1238
1239         if (m_customCursor) {
1240                 [m_customCursor release];
1241                 m_customCursor = nil;
1242         }
1243
1244         if ([m_window isVisible]) {
1245                 loadCursor(getCursorVisibility(), shape);
1246         }
1247         
1248         [pool drain];
1249         return GHOST_kSuccess;
1250 }
1251
1252 /** Reverse the bits in a GHOST_TUns8
1253 static GHOST_TUns8 uns8ReverseBits(GHOST_TUns8 ch)
1254 {
1255         ch= ((ch>>1)&0x55) | ((ch<<1)&0xAA);
1256         ch= ((ch>>2)&0x33) | ((ch<<2)&0xCC);
1257         ch= ((ch>>4)&0x0F) | ((ch<<4)&0xF0);
1258         return ch;
1259 }
1260 */
1261
1262
1263 /** Reverse the bits in a GHOST_TUns16 */
1264 static GHOST_TUns16 uns16ReverseBits(GHOST_TUns16 shrt)
1265 {
1266         shrt= ((shrt>>1)&0x5555) | ((shrt<<1)&0xAAAA);
1267         shrt= ((shrt>>2)&0x3333) | ((shrt<<2)&0xCCCC);
1268         shrt= ((shrt>>4)&0x0F0F) | ((shrt<<4)&0xF0F0);
1269         shrt= ((shrt>>8)&0x00FF) | ((shrt<<8)&0xFF00);
1270         return shrt;
1271 }
1272
1273 GHOST_TSuccess GHOST_WindowCocoa::setWindowCustomCursorShape(GHOST_TUns8 *bitmap, GHOST_TUns8 *mask,
1274                                         int sizex, int sizey, int hotX, int hotY, int fg_color, int bg_color)
1275 {
1276         int y,nbUns16;
1277         NSPoint hotSpotPoint;
1278         NSBitmapImageRep *cursorImageRep;
1279         NSImage *cursorImage;
1280         NSSize imSize;
1281         GHOST_TUns16 *cursorBitmap;
1282         
1283         
1284         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1285         
1286         if (m_customCursor) {
1287                 [m_customCursor release];
1288                 m_customCursor = nil;
1289         }
1290         
1291
1292         cursorImageRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:nil
1293                                                                                                                          pixelsWide:sizex
1294                                                                                                                          pixelsHigh:sizey
1295                                                                                                                   bitsPerSample:1 
1296                                                                                                                 samplesPerPixel:2
1297                                                                                                                            hasAlpha:YES
1298                                                                                                                            isPlanar:YES
1299                                                                                                                  colorSpaceName:NSDeviceWhiteColorSpace
1300                                                                                                                         bytesPerRow:(sizex/8 + (sizex%8 >0 ?1:0))
1301                                                                                                                    bitsPerPixel:1];
1302         
1303         
1304         cursorBitmap = (GHOST_TUns16*)[cursorImageRep bitmapData];
1305         nbUns16 = [cursorImageRep bytesPerPlane]/2;
1306         
1307         for (y=0; y<nbUns16; y++) {
1308 #if !defined(__LITTLE_ENDIAN__)
1309                 cursorBitmap[y] = ~uns16ReverseBits((bitmap[2*y]<<0) | (bitmap[2*y+1]<<8));
1310                 cursorBitmap[nbUns16+y] = uns16ReverseBits((mask[2*y]<<0) | (mask[2*y+1]<<8));
1311 #else
1312                 cursorBitmap[y] = ~uns16ReverseBits((bitmap[2*y+1]<<0) | (bitmap[2*y]<<8));
1313                 cursorBitmap[nbUns16+y] = uns16ReverseBits((mask[2*y+1]<<0) | (mask[2*y]<<8));
1314 #endif
1315                 
1316         }
1317         
1318         
1319         imSize.width = sizex;
1320         imSize.height= sizey;
1321         cursorImage = [[NSImage alloc] initWithSize:imSize];
1322         [cursorImage addRepresentation:cursorImageRep];
1323         
1324         hotSpotPoint.x = hotX;
1325         hotSpotPoint.y = hotY;
1326         
1327         //foreground and background color parameter is not handled for now (10.6)
1328         m_customCursor = [[NSCursor alloc] initWithImage:cursorImage
1329                                                                                          hotSpot:hotSpotPoint];
1330         
1331         [cursorImageRep release];
1332         [cursorImage release];
1333         
1334         if ([m_window isVisible]) {
1335                 loadCursor(getCursorVisibility(), GHOST_kStandardCursorCustom);
1336         }
1337         [pool drain];
1338         return GHOST_kSuccess;
1339 }
1340
1341 GHOST_TSuccess GHOST_WindowCocoa::setWindowCustomCursorShape(GHOST_TUns8 bitmap[16][2], 
1342                                                                                                 GHOST_TUns8 mask[16][2], int hotX, int hotY)
1343 {
1344         return setWindowCustomCursorShape((GHOST_TUns8*)bitmap, (GHOST_TUns8*) mask, 16, 16, hotX, hotY, 0, 1);
1345 }