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