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