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