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