Improved error checks in the Freestyle.Nature class.
[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 NSOpenGLPixelFormatAttribute pixelFormatAttrsWindow[] =
44 {
45         NSOpenGLPFADoubleBuffer,
46         NSOpenGLPFAAccelerated,
47         //NSOpenGLPFAAllowOfflineRenderers,   // Removed to allow 10.4 builds, and 2 GPUs rendering is not used anyway
48         NSOpenGLPFADepthSize, (NSOpenGLPixelFormatAttribute) 32,
49         (NSOpenGLPixelFormatAttribute) 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         setTitle(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         //Set associated file if applicable
275         if ([windowTitle hasPrefix:@"Blender"])
276         {
277                 NSRange fileStrRange;
278                 NSString *associatedFileName;
279                 int len;
280                 
281                 fileStrRange.location = [windowTitle rangeOfString:@"["].location+1;
282                 len = [windowTitle rangeOfString:@"]"].location - fileStrRange.location;
283         
284                 if (len >0)
285                 {
286                         fileStrRange.length = len;
287                         associatedFileName = [windowTitle substringWithRange:fileStrRange];
288                         @try {
289                                 [m_window setRepresentedFilename:associatedFileName];
290                         }
291                         @catch (NSException * e) {
292                                 printf("\nInvalid file path given in window title");
293                         }
294                         [m_window setTitle:[associatedFileName lastPathComponent]];
295                 }
296                 else {
297                         [m_window setTitle:windowTitle];
298                         [m_window setRepresentedFilename:@""];
299                 }
300
301         } else {
302                 [m_window setTitle:windowTitle];
303                 [m_window setRepresentedFilename:@""];
304         }
305
306         
307         [windowTitle release];
308         [pool drain];
309 }
310
311
312 void GHOST_WindowCocoa::getTitle(STR_String& title) const
313 {
314     GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getTitle(): window invalid")
315
316         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
317
318         NSString *windowTitle = [m_window title];
319
320         if (windowTitle != nil) {
321                 title = [windowTitle UTF8String];               
322         }
323         
324         [pool drain];
325 }
326
327
328 void GHOST_WindowCocoa::getWindowBounds(GHOST_Rect& bounds) const
329 {
330         NSRect rect;
331         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getWindowBounds(): window invalid")
332
333         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
334         
335         NSRect screenSize = [[m_window screen] visibleFrame];
336
337         rect = [m_window frame];
338
339         bounds.m_b = screenSize.size.height - (rect.origin.y -screenSize.origin.y);
340         bounds.m_l = rect.origin.x -screenSize.origin.x;
341         bounds.m_r = rect.origin.x-screenSize.origin.x + rect.size.width;
342         bounds.m_t = screenSize.size.height - (rect.origin.y + rect.size.height -screenSize.origin.y);
343         
344         [pool drain];
345 }
346
347
348 void GHOST_WindowCocoa::getClientBounds(GHOST_Rect& bounds) const
349 {
350         NSRect rect;
351         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getClientBounds(): window invalid")
352         
353         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
354         
355         if (!m_fullScreen)
356         {
357                 NSRect screenSize = [[m_window screen] visibleFrame];
358
359                 //Max window contents as screen size (excluding title bar...)
360                 NSRect contentRect = [CocoaWindow contentRectForFrameRect:screenSize
361                                                                                                          styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)];
362
363                 rect = [m_window contentRectForFrameRect:[m_window frame]];
364                 
365                 bounds.m_b = contentRect.size.height - (rect.origin.y -contentRect.origin.y);
366                 bounds.m_l = rect.origin.x -contentRect.origin.x;
367                 bounds.m_r = rect.origin.x-contentRect.origin.x + rect.size.width;
368                 bounds.m_t = contentRect.size.height - (rect.origin.y + rect.size.height -contentRect.origin.y);
369         }
370         else {
371                 NSRect screenSize = [[m_window screen] frame];
372                 
373                 bounds.m_b = screenSize.origin.y + screenSize.size.height;
374                 bounds.m_l = screenSize.origin.x;
375                 bounds.m_r = screenSize.origin.x + screenSize.size.width;
376                 bounds.m_t = screenSize.origin.y;
377         }
378         [pool drain];
379 }
380
381
382 GHOST_TSuccess GHOST_WindowCocoa::setClientWidth(GHOST_TUns32 width)
383 {
384         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setClientWidth(): window invalid")
385         GHOST_Rect cBnds, wBnds;
386         getClientBounds(cBnds);
387         if (((GHOST_TUns32)cBnds.getWidth()) != width) {
388                 NSSize size;
389                 size.width=width;
390                 size.height=cBnds.getHeight();
391                 [m_window setContentSize:size];
392         }
393         return GHOST_kSuccess;
394 }
395
396
397 GHOST_TSuccess GHOST_WindowCocoa::setClientHeight(GHOST_TUns32 height)
398 {
399         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setClientHeight(): window invalid")
400         GHOST_Rect cBnds, wBnds;
401         getClientBounds(cBnds);
402         if (((GHOST_TUns32)cBnds.getHeight()) != height) {
403                 NSSize size;
404                 size.width=cBnds.getWidth();
405                 size.height=height;
406                 [m_window setContentSize:size];
407         }
408         return GHOST_kSuccess;
409 }
410
411
412 GHOST_TSuccess GHOST_WindowCocoa::setClientSize(GHOST_TUns32 width, GHOST_TUns32 height)
413 {
414         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setClientSize(): window invalid")
415         GHOST_Rect cBnds, wBnds;
416         getClientBounds(cBnds);
417         if ((((GHOST_TUns32)cBnds.getWidth()) != width) ||
418             (((GHOST_TUns32)cBnds.getHeight()) != height)) {
419                 NSSize size;
420                 size.width=width;
421                 size.height=height;
422                 [m_window setContentSize:size];
423         }
424         return GHOST_kSuccess;
425 }
426
427
428 GHOST_TWindowState GHOST_WindowCocoa::getState() const
429 {
430         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getState(): window invalid")
431         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
432         GHOST_TWindowState state;
433         if (m_fullScreen) {
434                 state = GHOST_kWindowStateFullScreen;
435         } 
436         else if ([m_window isMiniaturized]) {
437                 state = GHOST_kWindowStateMinimized;
438         }
439         else if ([m_window isZoomed]) {
440                 state = GHOST_kWindowStateMaximized;
441         }
442         else {
443                 state = GHOST_kWindowStateNormal;
444         }
445         [pool drain];
446         return state;
447 }
448
449
450 void GHOST_WindowCocoa::screenToClient(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
451 {
452         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::screenToClient(): window invalid")
453         
454         NSPoint screenCoord;
455         NSPoint baseCoord;
456         
457         screenCoord.x = inX;
458         screenCoord.y = inY;
459         
460         baseCoord = [m_window convertScreenToBase:screenCoord];
461         
462         outX = baseCoord.x;
463         outY = baseCoord.y;
464 }
465
466
467 void GHOST_WindowCocoa::clientToScreen(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
468 {
469         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::clientToScreen(): window invalid")
470         
471         NSPoint screenCoord;
472         NSPoint baseCoord;
473         
474         baseCoord.x = inX;
475         baseCoord.y = inY;
476         
477         screenCoord = [m_window convertBaseToScreen:baseCoord];
478         
479         outX = screenCoord.x;
480         outY = screenCoord.y;
481 }
482
483 /**
484  * @note Fullscreen switch is not actual fullscreen with display capture. As this capture removes all OS X window manager features.
485  * Instead, the menu bar and the dock are hidden, and the window is made borderless and enlarged.
486  * Thus, process switch, exposé, spaces, ... still work in fullscreen mode
487  */
488 GHOST_TSuccess GHOST_WindowCocoa::setState(GHOST_TWindowState state)
489 {
490         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setState(): window invalid")
491     switch (state) {
492                 case GHOST_kWindowStateMinimized:
493             [m_window miniaturize:nil];
494             break;
495                 case GHOST_kWindowStateMaximized:
496                         [m_window zoom:nil];
497                         break;
498                 
499                 case GHOST_kWindowStateFullScreen:
500                         if (!m_fullScreen)
501                         {
502                                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
503                         
504                                 //This status change needs to be done before Cocoa call to enter fullscreen mode
505                                 //to give window delegate hint not to forward its deactivation to ghost wm that doesn't know view/window difference
506                                 m_fullScreen = true;
507
508 #ifdef MAC_OS_X_VERSION_10_6
509                                 //10.6 provides Cocoa functions to autoshow menu bar, and to change a window style
510                                 //Hide menu & dock if needed
511                                 if ([[m_window screen] isEqual:[NSScreen mainScreen]])
512                                 {
513                                         [NSApp setPresentationOptions:(NSApplicationPresentationHideDock | NSApplicationPresentationAutoHideMenuBar)];
514                                 }
515                                 //Make window borderless and enlarge it
516                                 [m_window setStyleMask:NSBorderlessWindowMask];
517                                 [m_window setFrame:[[m_window screen] frame] display:YES];
518 #else
519                                 //With 10.5, we need to create a new window to change its style to borderless
520                                 //Hide menu & dock if needed
521                                 if ([[m_window screen] isEqual:[NSScreen mainScreen]])
522                                 {
523                                         //Cocoa function in 10.5 does not allow to set the menu bar in auto-show mode [NSMenu setMenuBarVisible:NO];
524                                         //One of the very few 64bit compatible Carbon function
525                                         SetSystemUIMode(kUIModeAllHidden,kUIOptionAutoShowMenuBar);
526                                 }
527                                 //Create a fullscreen borderless window
528                                 CocoaWindow *tmpWindow = [[CocoaWindow alloc]
529                                                                                   initWithContentRect:[[m_window screen] frame]
530                                                                                   styleMask:NSBorderlessWindowMask
531                                                                                   backing:NSBackingStoreBuffered
532                                                                                   defer:YES];
533                                 //Copy current window parameters
534                                 [tmpWindow setTitle:[m_window title]];
535                                 [tmpWindow setRepresentedFilename:[m_window representedFilename]];
536                                 [tmpWindow setReleasedWhenClosed:NO];
537                                 [tmpWindow setAcceptsMouseMovedEvents:YES];
538                                 [tmpWindow setDelegate:[m_window delegate]];
539                                 
540                                 //Assign the openGL view to the new window
541                                 [tmpWindow setContentView:m_openGLView];
542                                 
543                                 //Show the new window
544                                 [tmpWindow makeKeyAndOrderFront:nil];
545                                 //Close and release old window
546                                 [m_window setDelegate:nil]; // To avoid the notification of "window closed" event
547                                 [m_window close];
548                                 [m_window release];
549                                 m_window = tmpWindow;
550 #endif
551                         
552                                 //Tell WM of view new size
553                                 m_systemCocoa->handleWindowEvent(GHOST_kEventWindowSize, this);
554                                 
555                                 [pool drain];
556                                 }
557                         break;
558                 case GHOST_kWindowStateNormal:
559         default:
560                         if (m_fullScreen)
561                         {
562                                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
563                                 m_fullScreen = false;
564
565                                 //Exit fullscreen
566 #ifdef MAC_OS_X_VERSION_10_6
567                                 //Show again menu & dock if needed
568                                 if ([[m_window screen] isEqual:[NSScreen mainScreen]])
569                                 {
570                                         [NSApp setPresentationOptions:NSApplicationPresentationDefault];
571                                 }
572                                 //Make window normal and resize it
573                                 [m_window setStyleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)];
574                                 [m_window setFrame:[[m_window screen] visibleFrame] display:YES];
575 #else
576                                 //With 10.5, we need to create a new window to change its style to borderless
577                                 //Show menu & dock if needed
578                                 if ([[m_window screen] isEqual:[NSScreen mainScreen]])
579                                 {
580                                         //Cocoa function in 10.5 does not allow to set the menu bar in auto-show mode [NSMenu setMenuBarVisible:YES];
581                                         SetSystemUIMode(kUIModeNormal, 0); //One of the very few 64bit compatible Carbon function
582                                 }
583                                 //Create a fullscreen borderless window
584                                 CocoaWindow *tmpWindow = [[CocoaWindow alloc]
585                                                                                   initWithContentRect:[[m_window screen] frame]
586                                                                                                         styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)
587                                                                                                           backing:NSBackingStoreBuffered
588                                                                                                                 defer:YES];
589                                 //Copy current window parameters
590                                 [tmpWindow setTitle:[m_window title]];
591                                 [tmpWindow setRepresentedFilename:[m_window representedFilename]];
592                                 [tmpWindow setReleasedWhenClosed:NO];
593                                 [tmpWindow setAcceptsMouseMovedEvents:YES];
594                                 [tmpWindow setDelegate:[m_window delegate]];
595                                 
596                                 //Assign the openGL view to the new window
597                                 [tmpWindow setContentView:m_openGLView];
598                                 
599                                 //Show the new window
600                                 [tmpWindow makeKeyAndOrderFront:nil];
601                                 //Close and release old window
602                                 [m_window setDelegate:nil]; // To avoid the notification of "window closed" event
603                                 [m_window close];
604                                 [m_window release];
605                                 m_window = tmpWindow;
606 #endif
607                         
608                                 //Tell WM of view new size
609                                 m_systemCocoa->handleWindowEvent(GHOST_kEventWindowSize, this);
610                                 
611                                 [pool drain];
612                         }
613             else if ([m_window isMiniaturized])
614                                 [m_window deminiaturize:nil];
615                         else if ([m_window isZoomed])
616                                 [m_window zoom:nil];
617             break;
618     }
619     return GHOST_kSuccess;
620 }
621
622 GHOST_TSuccess GHOST_WindowCocoa::setModifiedState(bool isUnsavedChanges)
623 {
624         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
625         
626         [m_window setDocumentEdited:isUnsavedChanges];
627         
628         [pool drain];
629         return GHOST_Window::setModifiedState(isUnsavedChanges);
630 }
631
632
633
634 GHOST_TSuccess GHOST_WindowCocoa::setOrder(GHOST_TWindowOrder order)
635 {
636         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setOrder(): window invalid")
637     if (order == GHOST_kWindowOrderTop) {
638                 [m_window orderFront:nil];
639     }
640     else {
641                 [m_window orderBack:nil];
642     }
643     return GHOST_kSuccess;
644 }
645
646 #pragma mark Drawing context
647
648 /*#define  WAIT_FOR_VSYNC 1*/
649
650 GHOST_TSuccess GHOST_WindowCocoa::swapBuffers()
651 {
652     if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) {
653         if (m_openGLContext != nil) {
654                         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
655                         [m_openGLContext flushBuffer];
656                         [pool drain];
657             return GHOST_kSuccess;
658         }
659     }
660     return GHOST_kFailure;
661 }
662
663 GHOST_TSuccess GHOST_WindowCocoa::updateDrawingContext()
664 {
665         if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) {
666                 if (m_openGLContext != nil) {
667                         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
668                         [m_openGLContext update];
669                         [pool drain];
670                         return GHOST_kSuccess;
671                 }
672         }
673         return GHOST_kFailure;
674 }
675
676 GHOST_TSuccess GHOST_WindowCocoa::activateDrawingContext()
677 {
678         if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) {
679                 if (m_openGLContext != nil) {
680                         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
681                         [m_openGLContext makeCurrentContext];
682                         [pool drain];
683                         return GHOST_kSuccess;
684                 }
685         }
686         return GHOST_kFailure;
687 }
688
689
690 GHOST_TSuccess GHOST_WindowCocoa::installDrawingContext(GHOST_TDrawingContextType type)
691 {
692         GHOST_TSuccess success = GHOST_kFailure;
693         
694         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
695         
696         NSOpenGLPixelFormat *pixelFormat;
697         NSOpenGLContext *tmpOpenGLContext;
698         
699         switch (type) {
700                 case GHOST_kDrawingContextTypeOpenGL:
701                         if (!getValid()) break;
702                                         
703                         pixelFormat = [m_openGLView pixelFormat];
704                         tmpOpenGLContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat
705                                                                                                                           shareContext:s_firstOpenGLcontext];
706                         if (tmpOpenGLContext == nil) {
707                                 success = GHOST_kFailure;
708                                 break;
709                         }
710                         
711                         if (!s_firstOpenGLcontext) s_firstOpenGLcontext = tmpOpenGLContext;
712 #ifdef WAIT_FOR_VSYNC
713                                 /* wait for vsync, to avoid tearing artifacts */
714                                 [tmpOpenGLContext setValues:1 forParameter:NSOpenGLCPSwapInterval];
715 #endif
716                                 [m_openGLView setOpenGLContext:tmpOpenGLContext];
717                                 [tmpOpenGLContext setView:m_openGLView];
718                                 
719                                 m_openGLContext = tmpOpenGLContext;
720                         break;
721                 
722                 case GHOST_kDrawingContextTypeNone:
723                         success = GHOST_kSuccess;
724                         break;
725                 
726                 default:
727                         break;
728         }
729         [pool drain];
730         return success;
731 }
732
733
734 GHOST_TSuccess GHOST_WindowCocoa::removeDrawingContext()
735 {
736         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
737         switch (m_drawingContextType) {
738                 case GHOST_kDrawingContextTypeOpenGL:
739                         if (m_openGLContext)
740                         {
741                                 [m_openGLView clearGLContext];
742                                 if (s_firstOpenGLcontext == m_openGLContext) s_firstOpenGLcontext = nil;
743                                 m_openGLContext = nil;
744                         }
745                         [pool drain];
746                         return GHOST_kSuccess;
747                 case GHOST_kDrawingContextTypeNone:
748                         [pool drain];
749                         return GHOST_kSuccess;
750                         break;
751                 default:
752                         [pool drain];
753                         return GHOST_kFailure;
754         }
755 }
756
757
758 GHOST_TSuccess GHOST_WindowCocoa::invalidate()
759 {
760         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::invalidate(): window invalid")
761         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
762         [m_openGLView setNeedsDisplay:YES];
763         [pool drain];
764         return GHOST_kSuccess;
765 }
766
767 #pragma mark Cursor handling
768
769 void GHOST_WindowCocoa::loadCursor(bool visible, GHOST_TStandardCursor cursor) const
770 {
771         static bool systemCursorVisible = true;
772         
773         NSAutoreleasePool *pool =[[NSAutoreleasePool alloc] init];
774
775         NSCursor *tmpCursor =nil;
776         
777         if (visible != systemCursorVisible) {
778                 if (visible) {
779                         [NSCursor unhide];
780                         systemCursorVisible = true;
781                 }
782                 else {
783                         [NSCursor hide];
784                         systemCursorVisible = false;
785                 }
786         }
787
788         if (cursor == GHOST_kStandardCursorCustom && m_customCursor) {
789                 tmpCursor = m_customCursor;
790         } else {
791                 switch (cursor) {
792                         case GHOST_kStandardCursorDestroy:
793                                 tmpCursor = [NSCursor disappearingItemCursor];
794                                 break;
795                         case GHOST_kStandardCursorText:
796                                 tmpCursor = [NSCursor IBeamCursor];
797                                 break;
798                         case GHOST_kStandardCursorCrosshair:
799                                 tmpCursor = [NSCursor crosshairCursor];
800                                 break;
801                         case GHOST_kStandardCursorUpDown:
802                                 tmpCursor = [NSCursor resizeUpDownCursor];
803                                 break;
804                         case GHOST_kStandardCursorLeftRight:
805                                 tmpCursor = [NSCursor resizeLeftRightCursor];
806                                 break;
807                         case GHOST_kStandardCursorTopSide:
808                                 tmpCursor = [NSCursor resizeUpCursor];
809                                 break;
810                         case GHOST_kStandardCursorBottomSide:
811                                 tmpCursor = [NSCursor resizeDownCursor];
812                                 break;
813                         case GHOST_kStandardCursorLeftSide:
814                                 tmpCursor = [NSCursor resizeLeftCursor];
815                                 break;
816                         case GHOST_kStandardCursorRightSide:
817                                 tmpCursor = [NSCursor resizeRightCursor];
818                                 break;
819                         case GHOST_kStandardCursorRightArrow:
820                         case GHOST_kStandardCursorInfo:
821                         case GHOST_kStandardCursorLeftArrow:
822                         case GHOST_kStandardCursorHelp:
823                         case GHOST_kStandardCursorCycle:
824                         case GHOST_kStandardCursorSpray:
825                         case GHOST_kStandardCursorWait:
826                         case GHOST_kStandardCursorTopLeftCorner:
827                         case GHOST_kStandardCursorTopRightCorner:
828                         case GHOST_kStandardCursorBottomRightCorner:
829                         case GHOST_kStandardCursorBottomLeftCorner:
830                         case GHOST_kStandardCursorDefault:
831                         default:
832                                 tmpCursor = [NSCursor arrowCursor];
833                                 break;
834                 };
835         }
836         [tmpCursor set];
837         [pool drain];
838 }
839
840
841
842 GHOST_TSuccess GHOST_WindowCocoa::setWindowCursorVisibility(bool visible)
843 {
844         if ([m_window isVisible]) {
845                 loadCursor(visible, getCursorShape());
846         }
847         
848         return GHOST_kSuccess;
849 }
850
851
852 //Override this method to provide set feature even if not in warp
853 inline bool GHOST_WindowCocoa::setCursorWarpAccum(GHOST_TInt32 x, GHOST_TInt32 y)
854 {
855         m_cursorWarpAccumPos[0]= x;
856         m_cursorWarpAccumPos[1]= y;
857         
858         return GHOST_kSuccess;
859 }
860
861
862 GHOST_TSuccess GHOST_WindowCocoa::setWindowCursorGrab(bool grab, bool warp, bool restore)
863 {
864         if (grab)
865         {
866                 //No need to perform grab without warp as it is always on in OS X
867                 if(warp) {
868                         GHOST_TInt32 x_old,y_old;
869
870                         m_cursorWarp= true;
871                         m_systemCocoa->getCursorPosition(x_old,y_old);
872                         screenToClient(x_old, y_old, m_cursorWarpInitPos[0], m_cursorWarpInitPos[1]);
873                         //Warp position is stored in client (window base) coordinates
874                         setWindowCursorVisibility(false);
875                         return CGAssociateMouseAndMouseCursorPosition(false) == kCGErrorSuccess ? GHOST_kSuccess : GHOST_kFailure;
876                 }
877         }
878         else {
879                 if(m_cursorWarp)
880                 {/* are we exiting warp */
881                         setWindowCursorVisibility(true);
882                         /* Almost works without but important otherwise the mouse GHOST location can be incorrect on exit */
883                         if(restore) {
884                                 GHOST_Rect bounds;
885                                 GHOST_TInt32 x_new, y_new, x_cur, y_cur;
886                                 
887                                 getClientBounds(bounds);
888                                 x_new= m_cursorWarpInitPos[0]+m_cursorWarpAccumPos[0];
889                                 y_new= m_cursorWarpInitPos[1]+m_cursorWarpAccumPos[1];
890                                 
891                                 if(x_new < 0)           x_new = 0;
892                                 if(y_new < 0)           y_new = 0;
893                                 if(x_new > bounds.getWidth())   x_new = bounds.getWidth();
894                                 if(y_new > bounds.getHeight())  y_new = bounds.getHeight();
895                                 
896                                 //get/set cursor position works in screen coordinates
897                                 clientToScreen(x_new, y_new, x_cur, y_cur);
898                                 m_systemCocoa->setCursorPosition(x_cur, y_cur);
899                                 
900                                 //As Cocoa will give as first deltaX,deltaY this change in cursor position, we need to compensate for it
901                                 //Issue appearing in case of two transform operations conducted w/o mouse motion in between
902                                 x_new=m_cursorWarpAccumPos[0];
903                                 y_new=m_cursorWarpAccumPos[1];
904                                 setCursorWarpAccum(-x_new, -y_new);
905                         }
906                         else {
907                                 GHOST_TInt32 x_new, y_new;
908                                 //get/set cursor position works in screen coordinates
909                                 clientToScreen(m_cursorWarpInitPos[0], m_cursorWarpInitPos[1], x_new, y_new);
910                                 m_systemCocoa->setCursorPosition(x_new, y_new);
911                                 setCursorWarpAccum(0, 0);
912                         }
913                         
914                         m_cursorWarp= false;
915                         return CGAssociateMouseAndMouseCursorPosition(true) == kCGErrorSuccess ? GHOST_kSuccess : GHOST_kFailure;
916                 }
917         }
918         return GHOST_kSuccess;
919 }
920         
921 GHOST_TSuccess GHOST_WindowCocoa::setWindowCursorShape(GHOST_TStandardCursor shape)
922 {
923         if (m_customCursor) {
924                 [m_customCursor release];
925                 m_customCursor = nil;
926         }
927
928         if ([m_window isVisible]) {
929                 loadCursor(getCursorVisibility(), shape);
930         }
931         
932         return GHOST_kSuccess;
933 }
934
935 /** Reverse the bits in a GHOST_TUns8
936 static GHOST_TUns8 uns8ReverseBits(GHOST_TUns8 ch)
937 {
938         ch= ((ch>>1)&0x55) | ((ch<<1)&0xAA);
939         ch= ((ch>>2)&0x33) | ((ch<<2)&0xCC);
940         ch= ((ch>>4)&0x0F) | ((ch<<4)&0xF0);
941         return ch;
942 }
943 */
944
945
946 /** Reverse the bits in a GHOST_TUns16 */
947 static GHOST_TUns16 uns16ReverseBits(GHOST_TUns16 shrt)
948 {
949         shrt= ((shrt>>1)&0x5555) | ((shrt<<1)&0xAAAA);
950         shrt= ((shrt>>2)&0x3333) | ((shrt<<2)&0xCCCC);
951         shrt= ((shrt>>4)&0x0F0F) | ((shrt<<4)&0xF0F0);
952         shrt= ((shrt>>8)&0x00FF) | ((shrt<<8)&0xFF00);
953         return shrt;
954 }
955
956 GHOST_TSuccess GHOST_WindowCocoa::setWindowCustomCursorShape(GHOST_TUns8 *bitmap, GHOST_TUns8 *mask,
957                                         int sizex, int sizey, int hotX, int hotY, int fg_color, int bg_color)
958 {
959         int y,nbUns16;
960         NSPoint hotSpotPoint;
961         NSBitmapImageRep *cursorImageRep;
962         NSImage *cursorImage;
963         NSSize imSize;
964         GHOST_TUns16 *cursorBitmap;
965         
966         
967         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
968         
969         if (m_customCursor) {
970                 [m_customCursor release];
971                 m_customCursor = nil;
972         }
973         
974
975         cursorImageRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:nil
976                                                                                                                          pixelsWide:sizex
977                                                                                                                          pixelsHigh:sizey
978                                                                                                                   bitsPerSample:1 
979                                                                                                                 samplesPerPixel:2
980                                                                                                                            hasAlpha:YES
981                                                                                                                            isPlanar:YES
982                                                                                                                  colorSpaceName:NSDeviceBlackColorSpace
983                                                                                                                         bytesPerRow:(sizex/8 + (sizex%8 >0 ?1:0))
984                                                                                                                    bitsPerPixel:1];
985         
986         
987         cursorBitmap = (GHOST_TUns16*)[cursorImageRep bitmapData];
988         nbUns16 = [cursorImageRep bytesPerPlane]/2;
989         
990         for (y=0; y<nbUns16; y++) {
991 #if !defined(__LITTLE_ENDIAN__)
992                 cursorBitmap[y] = uns16ReverseBits((bitmap[2*y]<<0) | (bitmap[2*y+1]<<8));
993                 cursorBitmap[nbUns16+y] = uns16ReverseBits((mask[2*y]<<0) | (mask[2*y+1]<<8));
994 #else
995                 cursorBitmap[y] = uns16ReverseBits((bitmap[2*y+1]<<0) | (bitmap[2*y]<<8));
996                 cursorBitmap[nbUns16+y] = uns16ReverseBits((mask[2*y+1]<<0) | (mask[2*y]<<8));
997 #endif
998                 
999         }
1000         
1001         
1002         imSize.width = sizex;
1003         imSize.height= sizey;
1004         cursorImage = [[NSImage alloc] initWithSize:imSize];
1005         [cursorImage addRepresentation:cursorImageRep];
1006         
1007         hotSpotPoint.x = hotX;
1008         hotSpotPoint.y = hotY;
1009         
1010         //foreground and background color parameter is not handled for now (10.6)
1011         m_customCursor = [[NSCursor alloc] initWithImage:cursorImage
1012                                                                                          hotSpot:hotSpotPoint];
1013         
1014         [cursorImageRep release];
1015         [cursorImage release];
1016         
1017         if ([m_window isVisible]) {
1018                 loadCursor(getCursorVisibility(), GHOST_kStandardCursorCustom);
1019         }
1020         [pool drain];
1021         return GHOST_kSuccess;
1022 }
1023
1024 GHOST_TSuccess GHOST_WindowCocoa::setWindowCustomCursorShape(GHOST_TUns8 bitmap[16][2], 
1025                                                                                                 GHOST_TUns8 mask[16][2], int hotX, int hotY)
1026 {
1027         return setWindowCustomCursorShape((GHOST_TUns8*)bitmap, (GHOST_TUns8*) mask, 16, 16, hotX, hotY, 0, 1);
1028 }