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