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