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