Cocoa : fix secondary window display bug issue
[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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, 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 "GHOST_WindowCocoa.h"
38 #include "GHOST_SystemCocoa.h"
39 #include "GHOST_Debug.h"
40
41
42 // Pixel Format Attributes for the windowed NSOpenGLContext
43 static const NSOpenGLPixelFormatAttribute pixelFormatAttrsWindow[] =
44 {
45         NSOpenGLPFADoubleBuffer,
46         NSOpenGLPFAAccelerated,
47         NSOpenGLPFAAllowOfflineRenderers,   // NOTE: Needed to connect to secondary GPUs
48         NSOpenGLPFADepthSize, 32,
49         0
50 };
51
52 #pragma mark Cocoa window delegate object
53
54 @interface CocoaWindowDelegate : NSObject
55 {
56         GHOST_SystemCocoa *systemCocoa;
57         GHOST_WindowCocoa *associatedWindow;
58 }
59
60 - (void)setSystemAndWindowCocoa:(const GHOST_SystemCocoa *)sysCocoa windowCocoa:(GHOST_WindowCocoa *)winCocoa;
61 - (void)windowWillClose:(NSNotification *)notification;
62 - (void)windowDidBecomeKey:(NSNotification *)notification;
63 - (void)windowDidResignKey:(NSNotification *)notification;
64 - (void)windowDidUpdate:(NSNotification *)notification;
65 - (void)windowDidResize:(NSNotification *)notification;
66 @end
67
68 @implementation CocoaWindowDelegate : NSObject
69 - (void)setSystemAndWindowCocoa:(GHOST_SystemCocoa *)sysCocoa windowCocoa:(GHOST_WindowCocoa *)winCocoa
70 {
71         systemCocoa = sysCocoa;
72         associatedWindow = winCocoa;
73 }
74
75 - (void)windowWillClose:(NSNotification *)notification
76 {
77         systemCocoa->handleWindowEvent(GHOST_kEventWindowClose, associatedWindow);
78 }
79
80 - (void)windowDidBecomeKey:(NSNotification *)notification
81 {
82         systemCocoa->handleWindowEvent(GHOST_kEventWindowActivate, associatedWindow);
83 }
84
85 - (void)windowDidResignKey:(NSNotification *)notification
86 {
87         //The window is no more key when its own view becomes fullscreen
88         //but ghost doesn't know the view/window difference, so hide this fact
89         if (associatedWindow->getState() != GHOST_kWindowStateFullScreen)
90                 systemCocoa->handleWindowEvent(GHOST_kEventWindowDeactivate, associatedWindow);
91 }
92
93 - (void)windowDidUpdate:(NSNotification *)notification
94 {
95         systemCocoa->handleWindowEvent(GHOST_kEventWindowUpdate, associatedWindow);
96 }
97
98 - (void)windowDidResize:(NSNotification *)notification
99 {
100         systemCocoa->handleWindowEvent(GHOST_kEventWindowSize, associatedWindow);
101 }
102 @end
103
104 #pragma mark NSWindow subclass
105 //We need to subclass it to tell that even borderless (fullscreen), it can become key (receive user events)
106 @interface CocoaWindow: NSWindow
107 {
108
109 }
110 -(BOOL)canBecomeKeyWindow;
111
112 @end
113 @implementation CocoaWindow
114
115 -(BOOL)canBecomeKeyWindow
116 {
117         return YES;
118 }
119
120 @end
121
122
123
124 #pragma mark NSOpenGLView subclass
125 //We need to subclass it in order to give Cocoa the feeling key events are trapped
126 @interface CocoaOpenGLView : NSOpenGLView
127 {
128         
129 }
130 @end
131 @implementation CocoaOpenGLView
132
133 - (BOOL)acceptsFirstResponder
134 {
135     return YES;
136 }
137
138 //The trick to prevent Cocoa from complaining (beeping)
139 - (void)keyDown:(NSEvent *)theEvent
140 {}
141
142 - (BOOL)isOpaque
143 {
144     return YES;
145 }
146
147 @end
148
149
150 #pragma mark initialization / finalization
151
152 NSOpenGLContext* GHOST_WindowCocoa::s_firstOpenGLcontext = nil;
153
154 GHOST_WindowCocoa::GHOST_WindowCocoa(
155         GHOST_SystemCocoa *systemCocoa,
156         const STR_String& title,
157         GHOST_TInt32 left,
158         GHOST_TInt32 top,
159         GHOST_TUns32 width,
160         GHOST_TUns32 height,
161         GHOST_TWindowState state,
162         GHOST_TDrawingContextType type,
163         const bool stereoVisual
164 ) :
165         GHOST_Window(title, left, top, width, height, state, GHOST_kDrawingContextTypeNone),
166         m_customCursor(0)
167 {
168         m_systemCocoa = systemCocoa;
169         m_fullScreen = false;
170         
171         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
172         
173
174         //Creates the window
175         NSRect rect;
176         
177         rect.origin.x = left;
178         rect.origin.y = top;
179         rect.size.width = width;
180         rect.size.height = height;
181         
182         m_window = [[CocoaWindow alloc] initWithContentRect:rect
183                                                                                    styleMask:NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask | NSMiniaturizableWindowMask
184                                                                                          backing:NSBackingStoreBuffered defer:NO];
185         if (m_window == nil) {
186                 [pool drain];
187                 return;
188         }
189         
190         [m_window setTitle:[NSString stringWithUTF8String:title]];
191         
192                         
193         //Creates the OpenGL View inside the window
194         NSOpenGLPixelFormat *pixelFormat =
195         [[NSOpenGLPixelFormat alloc] initWithAttributes:pixelFormatAttrsWindow];
196         
197         m_openGLView = [[CocoaOpenGLView alloc] initWithFrame:rect
198                                                                                                  pixelFormat:pixelFormat];
199         
200         [pixelFormat release];
201         
202         m_openGLContext = [m_openGLView openGLContext]; //This context will be replaced by the proper one just after
203         
204         [m_window setContentView:m_openGLView];
205         [m_window setInitialFirstResponder:m_openGLView];
206         
207         [m_window setReleasedWhenClosed:NO]; //To avoid bad pointer exception in case of user closing the window
208         
209         [m_window makeKeyAndOrderFront:nil];
210         
211         setDrawingContextType(type);
212         updateDrawingContext();
213         activateDrawingContext();
214         
215         m_tablet.Active = GHOST_kTabletModeNone;
216         
217         CocoaWindowDelegate *windowDelegate = [[CocoaWindowDelegate alloc] init];
218         [windowDelegate setSystemAndWindowCocoa:systemCocoa windowCocoa:this];
219         [m_window setDelegate:windowDelegate];
220         
221         [m_window setAcceptsMouseMovedEvents:YES];
222         
223         if (state == GHOST_kWindowStateFullScreen)
224                 setState(GHOST_kWindowStateFullScreen);
225                 
226         [pool drain];
227 }
228
229
230 GHOST_WindowCocoa::~GHOST_WindowCocoa()
231 {
232         if (m_customCursor) delete m_customCursor;
233
234     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
235         [m_openGLView release];
236         
237         if (m_window) {
238                 [m_window close];
239                 [[m_window delegate] release];
240                 [m_window release];
241                 m_window = nil;
242         }
243         
244         //Check for other blender opened windows and make the frontmost key
245         NSArray *windowsList = [NSApp orderedWindows];
246         if ([windowsList count]) {
247                 [[windowsList objectAtIndex:0] makeKeyAndOrderFront:nil];
248         }
249         [pool drain];
250 }
251
252 #pragma mark accessors
253
254 bool GHOST_WindowCocoa::getValid() const
255 {
256     bool valid;
257     if (!m_fullScreen) {
258         valid = (m_window != 0); //&& ::IsValidWindowPtr(m_windowRef);
259     }
260     else {
261         valid = true;
262     }
263     return valid;
264 }
265
266
267 void GHOST_WindowCocoa::setTitle(const STR_String& title)
268 {
269     GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setTitle(): window invalid")
270         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
271
272         NSString *windowTitle = [[NSString alloc] initWithUTF8String:title];
273         
274         [m_window setTitle:windowTitle];
275         
276         [windowTitle release];
277         [pool drain];
278 }
279
280
281 void GHOST_WindowCocoa::getTitle(STR_String& title) const
282 {
283     GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getTitle(): window invalid")
284
285         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
286
287         NSString *windowTitle = [m_window title];
288
289         if (windowTitle != nil) {
290                 title = [windowTitle UTF8String];               
291         }
292         
293         [pool drain];
294 }
295
296
297 void GHOST_WindowCocoa::getWindowBounds(GHOST_Rect& bounds) const
298 {
299         NSRect rect;
300         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getWindowBounds(): window invalid")
301
302         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
303         
304         NSRect screenSize = [[m_window screen] visibleFrame];
305
306         rect = [m_window frame];
307
308         bounds.m_b = screenSize.size.height - (rect.origin.y -screenSize.origin.y);
309         bounds.m_l = rect.origin.x -screenSize.origin.x;
310         bounds.m_r = rect.origin.x-screenSize.origin.x + rect.size.width;
311         bounds.m_t = screenSize.size.height - (rect.origin.y + rect.size.height -screenSize.origin.y);
312         
313         [pool drain];
314 }
315
316
317 void GHOST_WindowCocoa::getClientBounds(GHOST_Rect& bounds) const
318 {
319         NSRect rect;
320         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getClientBounds(): window invalid")
321         
322         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
323         
324         if (!m_fullScreen)
325         {
326                 NSRect screenSize = [[m_window screen] visibleFrame];
327
328                 //Max window contents as screen size (excluding title bar...)
329                 NSRect contentRect = [CocoaWindow contentRectForFrameRect:screenSize
330                                                                                                          styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)];
331
332                 rect = [m_window contentRectForFrameRect:[m_window frame]];
333                 
334                 bounds.m_b = contentRect.size.height - (rect.origin.y -contentRect.origin.y);
335                 bounds.m_l = rect.origin.x -contentRect.origin.x;
336                 bounds.m_r = rect.origin.x-contentRect.origin.x + rect.size.width;
337                 bounds.m_t = contentRect.size.height - (rect.origin.y + rect.size.height -contentRect.origin.y);
338         }
339         else {
340                 NSRect screenSize = [[m_window screen] frame];
341                 
342                 bounds.m_b = screenSize.origin.y + screenSize.size.height;
343                 bounds.m_l = screenSize.origin.x;
344                 bounds.m_r = screenSize.origin.x + screenSize.size.width;
345                 bounds.m_t = screenSize.origin.y;
346         }
347         [pool drain];
348 }
349
350
351 GHOST_TSuccess GHOST_WindowCocoa::setClientWidth(GHOST_TUns32 width)
352 {
353         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setClientWidth(): window invalid")
354         GHOST_Rect cBnds, wBnds;
355         getClientBounds(cBnds);
356         if (((GHOST_TUns32)cBnds.getWidth()) != width) {
357                 NSSize size;
358                 size.width=width;
359                 size.height=cBnds.getHeight();
360                 [m_window setContentSize:size];
361         }
362         return GHOST_kSuccess;
363 }
364
365
366 GHOST_TSuccess GHOST_WindowCocoa::setClientHeight(GHOST_TUns32 height)
367 {
368         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setClientHeight(): window invalid")
369         GHOST_Rect cBnds, wBnds;
370         getClientBounds(cBnds);
371         if (((GHOST_TUns32)cBnds.getHeight()) != height) {
372                 NSSize size;
373                 size.width=cBnds.getWidth();
374                 size.height=height;
375                 [m_window setContentSize:size];
376         }
377         return GHOST_kSuccess;
378 }
379
380
381 GHOST_TSuccess GHOST_WindowCocoa::setClientSize(GHOST_TUns32 width, GHOST_TUns32 height)
382 {
383         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setClientSize(): window invalid")
384         GHOST_Rect cBnds, wBnds;
385         getClientBounds(cBnds);
386         if ((((GHOST_TUns32)cBnds.getWidth()) != width) ||
387             (((GHOST_TUns32)cBnds.getHeight()) != height)) {
388                 NSSize size;
389                 size.width=width;
390                 size.height=height;
391                 [m_window setContentSize:size];
392         }
393         return GHOST_kSuccess;
394 }
395
396
397 GHOST_TWindowState GHOST_WindowCocoa::getState() const
398 {
399         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getState(): window invalid")
400         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
401         GHOST_TWindowState state;
402         if (m_fullScreen) {
403                 state = GHOST_kWindowStateFullScreen;
404         } 
405         else if ([m_window isMiniaturized]) {
406                 state = GHOST_kWindowStateMinimized;
407         }
408         else if ([m_window isZoomed]) {
409                 state = GHOST_kWindowStateMaximized;
410         }
411         else {
412                 state = GHOST_kWindowStateNormal;
413         }
414         [pool drain];
415         return state;
416 }
417
418
419 void GHOST_WindowCocoa::screenToClient(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
420 {
421         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::screenToClient(): window invalid")
422         
423         NSPoint screenCoord;
424         NSPoint baseCoord;
425         
426         screenCoord.x = inX;
427         screenCoord.y = inY;
428         
429         baseCoord = [m_window convertScreenToBase:screenCoord];
430         
431         outX = baseCoord.x;
432         outY = baseCoord.y;
433 }
434
435
436 void GHOST_WindowCocoa::clientToScreen(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
437 {
438         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::clientToScreen(): window invalid")
439         
440         NSPoint screenCoord;
441         NSPoint baseCoord;
442         
443         baseCoord.x = inX;
444         baseCoord.y = inY;
445         
446         screenCoord = [m_window convertBaseToScreen:baseCoord];
447         
448         outX = screenCoord.x;
449         outY = screenCoord.y;
450 }
451
452 /**
453  * @note Fullscreen switch is not actual fullscreen with display capture. As this capture removes all OS X window manager features.
454  * Instead, the menu bar and the dock are hidden, and the window is made borderless and enlarged.
455  * Thus, process switch, exposé, spaces, ... still work in fullscreen mode
456  */
457 GHOST_TSuccess GHOST_WindowCocoa::setState(GHOST_TWindowState state)
458 {
459         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setState(): window invalid")
460     switch (state) {
461                 case GHOST_kWindowStateMinimized:
462             [m_window miniaturize:nil];
463             break;
464                 case GHOST_kWindowStateMaximized:
465                         [m_window zoom:nil];
466                         break;
467                 
468                 case GHOST_kWindowStateFullScreen:
469                         if (!m_fullScreen)
470                         {
471                                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
472                         
473                                 //This status change needs to be done before Cocoa call to enter fullscreen mode
474                                 //to give window delegate hint not to forward its deactivation to ghost wm that doesn't know view/window difference
475                                 m_fullScreen = true;
476
477 #ifdef MAC_OS_X_VERSION_10_6
478                                 //10.6 provides Cocoa functions to autoshow menu bar, and to change a window style
479                                 //Hide menu & dock if needed
480                                 if ([[m_window screen] isEqual:[NSScreen mainScreen]])
481                                 {
482                                         [NSApp setPresentationOptions:(NSApplicationPresentationHideDock | NSApplicationPresentationAutoHideMenuBar)];
483                                 }
484                                 //Make window borderless and enlarge it
485                                 [m_window setStyleMask:NSBorderlessWindowMask];
486                                 [m_window setFrame:[[m_window screen] frame] display:YES];
487 #else
488                                 //With 10.5, we need to create a new window to change its style to borderless
489                                 //Hide menu & dock if needed
490                                 if ([[m_window screen] isEqual:[NSScreen mainScreen]])
491                                 {
492                                         //Cocoa function in 10.5 does not allow to set the menu bar in auto-show mode [NSMenu setMenuBarVisible:NO];
493                                         //One of the very few 64bit compatible Carbon function
494                                         SetSystemUIMode(kUIModeAllHidden,kUIOptionAutoShowMenuBar);
495                                 }
496                                 //Create a fullscreen borderless window
497                                 CocoaWindow *tmpWindow = [[CocoaWindow alloc]
498                                                                                   initWithContentRect:[[m_window screen] frame]
499                                                                                   styleMask:NSBorderlessWindowMask
500                                                                                   backing:NSBackingStoreBuffered
501                                                                                   defer:YES];
502                                 //Copy current window parameters
503                                 [tmpWindow setTitle:[m_window title]];
504                                 [tmpWindow setRepresentedURL:[m_window representedURL]];
505                                 [tmpWindow setReleasedWhenClosed:NO];
506                                 [tmpWindow setAcceptsMouseMovedEvents:YES];
507                                 [tmpWindow setDelegate:[m_window delegate]];
508                                 
509                                 //Assign the openGL view to the new window
510                                 [tmpWindow setContentView:m_openGLView];
511                                 
512                                 //Show the new window
513                                 [tmpWindow makeKeyAndOrderFront:nil];
514                                 //Close and release old window
515                                 [m_window setDelegate:nil]; // To avoid the notification of "window closed" event
516                                 [m_window close];
517                                 [m_window release];
518                                 m_window = tmpWindow;
519 #endif
520                         
521                                 //Tell WM of view new size
522                                 m_systemCocoa->handleWindowEvent(GHOST_kEventWindowSize, this);
523                                 
524                                 [pool drain];
525                                 }
526                         break;
527                 case GHOST_kWindowStateNormal:
528         default:
529                         if (m_fullScreen)
530                         {
531                                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
532                                 m_fullScreen = false;
533
534                                 //Exit fullscreen
535 #ifdef MAC_OS_X_VERSION_10_6
536                                 //Show again menu & dock if needed
537                                 if ([[m_window screen] isEqual:[NSScreen mainScreen]])
538                                 {
539                                         [NSApp setPresentationOptions:NSApplicationPresentationDefault];
540                                 }
541                                 //Make window normal and resize it
542                                 [m_window setStyleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)];
543                                 [m_window setFrame:[[m_window screen] visibleFrame] display:YES];
544 #else
545                                 //With 10.5, we need to create a new window to change its style to borderless
546                                 //Show menu & dock if needed
547                                 if ([[m_window screen] isEqual:[NSScreen mainScreen]])
548                                 {
549                                         //Cocoa function in 10.5 does not allow to set the menu bar in auto-show mode [NSMenu setMenuBarVisible:YES];
550                                         SetSystemUIMode(kUIModeNormal, 0); //One of the very few 64bit compatible Carbon function
551                                 }
552                                 //Create a fullscreen borderless window
553                                 CocoaWindow *tmpWindow = [[CocoaWindow alloc]
554                                                                                   initWithContentRect:[[m_window screen] frame]
555                                                                                                         styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)
556                                                                                                           backing:NSBackingStoreBuffered
557                                                                                                                 defer:YES];
558                                 //Copy current window parameters
559                                 [tmpWindow setTitle:[m_window title]];
560                                 [tmpWindow setRepresentedURL:[m_window representedURL]];
561                                 [tmpWindow setReleasedWhenClosed:NO];
562                                 [tmpWindow setAcceptsMouseMovedEvents:YES];
563                                 [tmpWindow setDelegate:[m_window delegate]];
564                                 
565                                 //Assign the openGL view to the new window
566                                 [tmpWindow setContentView:m_openGLView];
567                                 
568                                 //Show the new window
569                                 [tmpWindow makeKeyAndOrderFront:nil];
570                                 //Close and release old window
571                                 [m_window setDelegate:nil]; // To avoid the notification of "window closed" event
572                                 [m_window close];
573                                 [m_window release];
574                                 m_window = tmpWindow;
575 #endif
576                         
577                                 //Tell WM of view new size
578                                 m_systemCocoa->handleWindowEvent(GHOST_kEventWindowSize, this);
579                                 
580                                 [pool drain];
581                         }
582             else if ([m_window isMiniaturized])
583                                 [m_window deminiaturize:nil];
584                         else if ([m_window isZoomed])
585                                 [m_window zoom:nil];
586             break;
587     }
588     return GHOST_kSuccess;
589 }
590
591 GHOST_TSuccess GHOST_WindowCocoa::setModifiedState(bool isUnsavedChanges)
592 {
593         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
594         
595         [m_window setDocumentEdited:isUnsavedChanges];
596         
597         [pool drain];
598         return GHOST_Window::setModifiedState(isUnsavedChanges);
599 }
600
601
602
603 GHOST_TSuccess GHOST_WindowCocoa::setOrder(GHOST_TWindowOrder order)
604 {
605         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setOrder(): window invalid")
606     if (order == GHOST_kWindowOrderTop) {
607                 [m_window orderFront:nil];
608     }
609     else {
610                 [m_window orderBack:nil];
611     }
612     return GHOST_kSuccess;
613 }
614
615 #pragma mark Drawing context
616
617 /*#define  WAIT_FOR_VSYNC 1*/
618
619 GHOST_TSuccess GHOST_WindowCocoa::swapBuffers()
620 {
621     if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) {
622         if (m_openGLContext != nil) {
623                         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
624                         [m_openGLContext flushBuffer];
625                         [pool drain];
626             return GHOST_kSuccess;
627         }
628     }
629     return GHOST_kFailure;
630 }
631
632 GHOST_TSuccess GHOST_WindowCocoa::updateDrawingContext()
633 {
634         if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) {
635                 if (m_openGLContext != nil) {
636                         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
637                         [m_openGLContext update];
638                         [pool drain];
639                         return GHOST_kSuccess;
640                 }
641         }
642         return GHOST_kFailure;
643 }
644
645 GHOST_TSuccess GHOST_WindowCocoa::activateDrawingContext()
646 {
647         if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) {
648                 if (m_openGLContext != nil) {
649                         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
650                         [m_openGLContext makeCurrentContext];
651                         [pool drain];
652                         return GHOST_kSuccess;
653                 }
654         }
655         return GHOST_kFailure;
656 }
657
658
659 GHOST_TSuccess GHOST_WindowCocoa::installDrawingContext(GHOST_TDrawingContextType type)
660 {
661         GHOST_TSuccess success = GHOST_kFailure;
662         
663         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
664         
665         NSOpenGLPixelFormat *pixelFormat;
666         NSOpenGLContext *tmpOpenGLContext;
667         
668         switch (type) {
669                 case GHOST_kDrawingContextTypeOpenGL:
670                         if (!getValid()) break;
671                                         
672                         pixelFormat = [m_openGLView pixelFormat];
673                         tmpOpenGLContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat
674                                                                                                                           shareContext:s_firstOpenGLcontext];
675                         if (tmpOpenGLContext == nil) {
676                                 success = GHOST_kFailure;
677                                 break;
678                         }
679                         
680                         if (!s_firstOpenGLcontext) s_firstOpenGLcontext = tmpOpenGLContext;
681 #ifdef WAIT_FOR_VSYNC
682                                 /* wait for vsync, to avoid tearing artifacts */
683                                 [tmpOpenGLContext setValues:1 forParameter:NSOpenGLCPSwapInterval];
684 #endif
685                                 [m_openGLView setOpenGLContext:tmpOpenGLContext];
686                                 [tmpOpenGLContext setView:m_openGLView];
687                                 
688                                 m_openGLContext = tmpOpenGLContext;
689                         break;
690                 
691                 case GHOST_kDrawingContextTypeNone:
692                         success = GHOST_kSuccess;
693                         break;
694                 
695                 default:
696                         break;
697         }
698         [pool drain];
699         return success;
700 }
701
702
703 GHOST_TSuccess GHOST_WindowCocoa::removeDrawingContext()
704 {
705         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
706         switch (m_drawingContextType) {
707                 case GHOST_kDrawingContextTypeOpenGL:
708                         if (m_openGLContext)
709                         {
710                                 [m_openGLView clearGLContext];
711                                 if (s_firstOpenGLcontext == m_openGLContext) s_firstOpenGLcontext = nil;
712                                 m_openGLContext = nil;
713                         }
714                         [pool drain];
715                         return GHOST_kSuccess;
716                 case GHOST_kDrawingContextTypeNone:
717                         [pool drain];
718                         return GHOST_kSuccess;
719                         break;
720                 default:
721                         [pool drain];
722                         return GHOST_kFailure;
723         }
724 }
725
726
727 GHOST_TSuccess GHOST_WindowCocoa::invalidate()
728 {
729         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::invalidate(): window invalid")
730         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
731         [m_openGLView setNeedsDisplay:YES];
732         [pool drain];
733         return GHOST_kSuccess;
734 }
735
736 #pragma mark Cursor handling
737
738 void GHOST_WindowCocoa::loadCursor(bool visible, GHOST_TStandardCursor cursor) const
739 {
740         static bool systemCursorVisible = true;
741         
742         NSAutoreleasePool *pool =[[NSAutoreleasePool alloc] init];
743
744         NSCursor *tmpCursor =nil;
745         
746         if (visible != systemCursorVisible) {
747                 if (visible) {
748                         [NSCursor unhide];
749                         systemCursorVisible = true;
750                 }
751                 else {
752                         [NSCursor hide];
753                         systemCursorVisible = false;
754                 }
755         }
756
757         if (cursor == GHOST_kStandardCursorCustom && m_customCursor) {
758                 tmpCursor = m_customCursor;
759         } else {
760                 switch (cursor) {
761                         case GHOST_kStandardCursorDestroy:
762                                 tmpCursor = [NSCursor disappearingItemCursor];
763                                 break;
764                         case GHOST_kStandardCursorText:
765                                 tmpCursor = [NSCursor IBeamCursor];
766                                 break;
767                         case GHOST_kStandardCursorCrosshair:
768                                 tmpCursor = [NSCursor crosshairCursor];
769                                 break;
770                         case GHOST_kStandardCursorUpDown:
771                                 tmpCursor = [NSCursor resizeUpDownCursor];
772                                 break;
773                         case GHOST_kStandardCursorLeftRight:
774                                 tmpCursor = [NSCursor resizeLeftRightCursor];
775                                 break;
776                         case GHOST_kStandardCursorTopSide:
777                                 tmpCursor = [NSCursor resizeUpCursor];
778                                 break;
779                         case GHOST_kStandardCursorBottomSide:
780                                 tmpCursor = [NSCursor resizeDownCursor];
781                                 break;
782                         case GHOST_kStandardCursorLeftSide:
783                                 tmpCursor = [NSCursor resizeLeftCursor];
784                                 break;
785                         case GHOST_kStandardCursorRightSide:
786                                 tmpCursor = [NSCursor resizeRightCursor];
787                                 break;
788                         case GHOST_kStandardCursorRightArrow:
789                         case GHOST_kStandardCursorInfo:
790                         case GHOST_kStandardCursorLeftArrow:
791                         case GHOST_kStandardCursorHelp:
792                         case GHOST_kStandardCursorCycle:
793                         case GHOST_kStandardCursorSpray:
794                         case GHOST_kStandardCursorWait:
795                         case GHOST_kStandardCursorTopLeftCorner:
796                         case GHOST_kStandardCursorTopRightCorner:
797                         case GHOST_kStandardCursorBottomRightCorner:
798                         case GHOST_kStandardCursorBottomLeftCorner:
799                         case GHOST_kStandardCursorDefault:
800                         default:
801                                 tmpCursor = [NSCursor arrowCursor];
802                                 break;
803                 };
804         }
805         [tmpCursor set];
806         [pool drain];
807 }
808
809
810
811 GHOST_TSuccess GHOST_WindowCocoa::setWindowCursorVisibility(bool visible)
812 {
813         if ([m_window isVisible]) {
814                 loadCursor(visible, getCursorShape());
815         }
816         
817         return GHOST_kSuccess;
818 }
819
820
821 //Override this method to provide set feature even if not in warp
822 inline bool GHOST_WindowCocoa::setCursorWarpAccum(GHOST_TInt32 x, GHOST_TInt32 y)
823 {
824         m_cursorWarpAccumPos[0]= x;
825         m_cursorWarpAccumPos[1]= y;
826         
827         return GHOST_kSuccess;
828 }
829
830
831 GHOST_TSuccess GHOST_WindowCocoa::setWindowCursorGrab(bool grab, bool warp, bool restore)
832 {
833         if (grab)
834         {
835                 //No need to perform grab without warp as it is always on in OS X
836                 if(warp) {
837                         GHOST_TInt32 x_old,y_old;
838
839                         m_cursorWarp= true;
840                         m_systemCocoa->getCursorPosition(x_old,y_old);
841                         screenToClient(x_old, y_old, m_cursorWarpInitPos[0], m_cursorWarpInitPos[1]);
842                         //Warp position is stored in client (window base) coordinates
843                         setWindowCursorVisibility(false);
844                         return CGAssociateMouseAndMouseCursorPosition(false) == kCGErrorSuccess ? GHOST_kSuccess : GHOST_kFailure;
845                 }
846         }
847         else {
848                 if(m_cursorWarp)
849                 {/* are we exiting warp */
850                         setWindowCursorVisibility(true);
851                         /* Almost works without but important otherwise the mouse GHOST location can be incorrect on exit */
852                         if(restore) {
853                                 GHOST_Rect bounds;
854                                 GHOST_TInt32 x_new, y_new, x_cur, y_cur;
855                                 
856                                 getClientBounds(bounds);
857                                 x_new= m_cursorWarpInitPos[0]+m_cursorWarpAccumPos[0];
858                                 y_new= m_cursorWarpInitPos[1]+m_cursorWarpAccumPos[1];
859                                 
860                                 if(x_new < 0)           x_new = 0;
861                                 if(y_new < 0)           y_new = 0;
862                                 if(x_new > bounds.getWidth())   x_new = bounds.getWidth();
863                                 if(y_new > bounds.getHeight())  y_new = bounds.getHeight();
864                                 
865                                 //get/set cursor position works in screen coordinates
866                                 clientToScreen(x_new, y_new, x_cur, y_cur);
867                                 m_systemCocoa->setCursorPosition(x_cur, y_cur);
868                                 
869                                 //As Cocoa will give as first deltaX,deltaY this change in cursor position, we need to compensate for it
870                                 //Issue appearing in case of two transform operations conducted w/o mouse motion in between
871                                 x_new=m_cursorWarpAccumPos[0];
872                                 y_new=m_cursorWarpAccumPos[1];
873                                 setCursorWarpAccum(-x_new, -y_new);
874                         }
875                         else {
876                                 m_systemCocoa->setCursorPosition(m_cursorWarpInitPos[0], m_cursorWarpInitPos[1]);
877                                 setCursorWarpAccum(0, 0);
878                         }
879                         
880                         m_cursorWarp= false;
881                         return CGAssociateMouseAndMouseCursorPosition(true) == kCGErrorSuccess ? GHOST_kSuccess : GHOST_kFailure;
882                 }
883         }
884         return GHOST_kSuccess;
885 }
886         
887 GHOST_TSuccess GHOST_WindowCocoa::setWindowCursorShape(GHOST_TStandardCursor shape)
888 {
889         if (m_customCursor) {
890                 [m_customCursor release];
891                 m_customCursor = nil;
892         }
893
894         if ([m_window isVisible]) {
895                 loadCursor(getCursorVisibility(), shape);
896         }
897         
898         return GHOST_kSuccess;
899 }
900
901 #if 0
902 /** Reverse the bits in a GHOST_TUns8 */
903 static GHOST_TUns8 uns8ReverseBits(GHOST_TUns8 ch)
904 {
905         ch= ((ch>>1)&0x55) | ((ch<<1)&0xAA);
906         ch= ((ch>>2)&0x33) | ((ch<<2)&0xCC);
907         ch= ((ch>>4)&0x0F) | ((ch<<4)&0xF0);
908         return ch;
909 }
910 #endif
911
912
913 /** Reverse the bits in a GHOST_TUns16 */
914 static GHOST_TUns16 uns16ReverseBits(GHOST_TUns16 shrt)
915 {
916         shrt= ((shrt>>1)&0x5555) | ((shrt<<1)&0xAAAA);
917         shrt= ((shrt>>2)&0x3333) | ((shrt<<2)&0xCCCC);
918         shrt= ((shrt>>4)&0x0F0F) | ((shrt<<4)&0xF0F0);
919         shrt= ((shrt>>8)&0x00FF) | ((shrt<<8)&0xFF00);
920         return shrt;
921 }
922
923 GHOST_TSuccess GHOST_WindowCocoa::setWindowCustomCursorShape(GHOST_TUns8 *bitmap, GHOST_TUns8 *mask,
924                                         int sizex, int sizey, int hotX, int hotY, int fg_color, int bg_color)
925 {
926         int y;
927         NSPoint hotSpotPoint;
928         NSImage *cursorImage;
929         
930         if (m_customCursor) {
931                 [m_customCursor release];
932                 m_customCursor = nil;
933         }
934         /*TODO: implement this (but unused inproject at present)
935         cursorImage = [[NSImage alloc] initWithData:bitmap];
936         
937         for (y=0; y<16; y++) {
938 #if !defined(__LITTLE_ENDIAN__)
939                 m_customCursor->data[y] = uns16ReverseBits((bitmap[2*y]<<0) | (bitmap[2*y+1]<<8));
940                 m_customCursor->mask[y] = uns16ReverseBits((mask[2*y]<<0) | (mask[2*y+1]<<8));
941 #else
942                 m_customCursor->data[y] = uns16ReverseBits((bitmap[2*y+1]<<0) | (bitmap[2*y]<<8));
943                 m_customCursor->mask[y] = uns16ReverseBits((mask[2*y+1]<<0) | (mask[2*y]<<8));
944 #endif
945                         
946         }
947         
948         
949         hotSpotPoint.x = hotX;
950         hotSpotPoint.y = hotY;
951         
952         m_customCursor = [[NSCursor alloc] initWithImage:cursorImage
953                                                                  foregroundColorHint:<#(NSColor *)fg#>
954                                                                  backgroundColorHint:<#(NSColor *)bg#>
955                                                                                          hotSpot:hotSpotPoint];
956         
957         [cursorImage release];
958         
959         if ([m_window isVisible]) {
960                 loadCursor(getCursorVisibility(), GHOST_kStandardCursorCustom);
961         }
962         */
963         return GHOST_kSuccess;
964 }
965
966 GHOST_TSuccess GHOST_WindowCocoa::setWindowCustomCursorShape(GHOST_TUns8 bitmap[16][2], 
967                                                                                                 GHOST_TUns8 mask[16][2], int hotX, int hotY)
968 {
969         return setWindowCustomCursorShape((GHOST_TUns8*)bitmap, (GHOST_TUns8*) mask, 16, 16, hotX, hotY, 0, 1);
970 }
971
972 #pragma mark Old carbon stuff to remove
973
974 #if 0
975 void GHOST_WindowCocoa::setMac_windowState(short value)
976 {
977         mac_windowState = value;
978 }
979
980 short GHOST_WindowCocoa::getMac_windowState()
981 {
982         return mac_windowState;
983 }
984
985 void GHOST_WindowCocoa::gen2mac(const STR_String& in, Str255 out) const
986 {
987         STR_String tempStr  = in;
988         int num = tempStr.Length();
989         if (num > 255) num = 255;
990         ::memcpy(out+1, tempStr.Ptr(), num);
991         out[0] = num;
992 }
993
994
995 void GHOST_WindowCocoa::mac2gen(const Str255 in, STR_String& out) const
996 {
997         char tmp[256];
998         ::memcpy(tmp, in+1, in[0]);
999         tmp[in[0]] = '\0';
1000         out = tmp;
1001 }
1002
1003 #endif