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