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 #include "GHOST_WindowCocoa.h"
33 #include "GHOST_SystemCocoa.h"
34 #include "GHOST_Debug.h"
35
36
37 // Pixel Format Attributes for the windowed NSOpenGLContext
38 static const NSOpenGLPixelFormatAttribute pixelFormatAttrsWindow[] =
39 {
40         NSOpenGLPFADoubleBuffer,
41         NSOpenGLPFAAccelerated,
42         NSOpenGLPFAAllowOfflineRenderers,   // NOTE: Needed to connect to secondary GPUs
43         NSOpenGLPFADepthSize, 32,
44         0
45 };
46
47 #pragma mark Cocoa delegate object
48
49 @interface CocoaWindowDelegate : NSObject
50 {
51         GHOST_SystemCocoa *systemCocoa;
52         GHOST_WindowCocoa *associatedWindow;
53 }
54
55 - (void)setSystemAndWindowCocoa:(const GHOST_SystemCocoa *)sysCocoa windowCocoa:(GHOST_WindowCocoa *)winCocoa;
56 - (void)windowWillClose:(NSNotification *)notification;
57 - (void)windowDidBecomeKey:(NSNotification *)notification;
58 - (void)windowDidResignKey:(NSNotification *)notification;
59 - (void)windowDidUpdate:(NSNotification *)notification;
60 - (void)windowDidResize:(NSNotification *)notification;
61 @end
62
63 @implementation CocoaWindowDelegate : NSObject
64 - (void)setSystemAndWindowCocoa:(GHOST_SystemCocoa *)sysCocoa windowCocoa:(GHOST_WindowCocoa *)winCocoa
65 {
66         systemCocoa = sysCocoa;
67         associatedWindow = winCocoa;
68 }
69
70 - (void)windowWillClose:(NSNotification *)notification
71 {
72         systemCocoa->handleWindowEvent(GHOST_kEventWindowClose, associatedWindow);
73 }
74
75 - (void)windowDidBecomeKey:(NSNotification *)notification
76 {
77         systemCocoa->handleWindowEvent(GHOST_kEventWindowActivate, associatedWindow);
78 }
79
80 - (void)windowDidResignKey:(NSNotification *)notification
81 {
82         //The window is no more key when its own view becomes fullscreen
83         //but ghost doesn't know the view/window difference, so hide this fact
84         if (associatedWindow->getState() != GHOST_kWindowStateFullScreen)
85                 systemCocoa->handleWindowEvent(GHOST_kEventWindowDeactivate, associatedWindow);
86 }
87
88 - (void)windowDidUpdate:(NSNotification *)notification
89 {
90         systemCocoa->handleWindowEvent(GHOST_kEventWindowUpdate, associatedWindow);
91 }
92
93 - (void)windowDidResize:(NSNotification *)notification
94 {
95         systemCocoa->handleWindowEvent(GHOST_kEventWindowSize, associatedWindow);
96 }
97 @end
98
99 #pragma mark NSOpenGLView subclass
100 //We need to subclass it in order to give Cocoa the feeling key events are trapped
101 @interface CocoaOpenGLView : NSOpenGLView
102 {
103         
104 }
105 @end
106 @implementation CocoaOpenGLView
107
108 - (BOOL)acceptsFirstResponder
109 {
110     return YES;
111 }
112
113 //The trick to prevent Cocoa from complaining (beeping)
114 - (void)keyDown:(NSEvent *)theEvent
115 {}
116
117 - (BOOL)isOpaque
118 {
119     return YES;
120 }
121
122 @end
123
124
125 #pragma mark initialization / finalization
126
127 GHOST_WindowCocoa::GHOST_WindowCocoa(
128         GHOST_SystemCocoa *systemCocoa,
129         const STR_String& title,
130         GHOST_TInt32 left,
131         GHOST_TInt32 top,
132         GHOST_TUns32 width,
133         GHOST_TUns32 height,
134         GHOST_TWindowState state,
135         GHOST_TDrawingContextType type,
136         const bool stereoVisual
137 ) :
138         GHOST_Window(title, left, top, width, height, state, GHOST_kDrawingContextTypeNone),
139         m_customCursor(0)
140 {
141         m_systemCocoa = systemCocoa;
142         m_fullScreen = false;
143         
144         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
145         
146
147         //Creates the window
148         NSRect rect;
149         
150         rect.origin.x = left;
151         rect.origin.y = top;
152         rect.size.width = width;
153         rect.size.height = height;
154         
155         m_window = [[NSWindow alloc] initWithContentRect:rect
156                                                                                    styleMask:NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask | NSMiniaturizableWindowMask
157                                                                                          backing:NSBackingStoreBuffered defer:NO];
158         if (m_window == nil) {
159                 [pool drain];
160                 return;
161         }
162         
163         [m_window setTitle:[NSString stringWithUTF8String:title]];
164         
165                         
166         //Creates the OpenGL View inside the window
167         NSOpenGLPixelFormat *pixelFormat =
168         [[NSOpenGLPixelFormat alloc] initWithAttributes:pixelFormatAttrsWindow];
169         
170         m_openGLView = [[CocoaOpenGLView alloc] initWithFrame:rect
171                                                                                                  pixelFormat:pixelFormat];
172         
173         [pixelFormat release];
174         
175         m_openGLContext = [m_openGLView openGLContext];
176         
177         [m_window setContentView:m_openGLView];
178         [m_window setInitialFirstResponder:m_openGLView];
179         
180         [m_window setReleasedWhenClosed:NO]; //To avoid bad pointer exception in case of user closing the window
181         
182         [m_window makeKeyAndOrderFront:nil];
183         
184         setDrawingContextType(type);
185         updateDrawingContext();
186         activateDrawingContext();
187         
188         m_tablet.Active = GHOST_kTabletModeNone;
189         
190         CocoaWindowDelegate *windowDelegate = [[CocoaWindowDelegate alloc] init];
191         [windowDelegate setSystemAndWindowCocoa:systemCocoa windowCocoa:this];
192         [m_window setDelegate:windowDelegate];
193         
194         [m_window setAcceptsMouseMovedEvents:YES];
195         
196         if (state == GHOST_kWindowStateFullScreen)
197                 setState(GHOST_kWindowStateFullScreen);
198                 
199         [pool drain];
200 }
201
202
203 GHOST_WindowCocoa::~GHOST_WindowCocoa()
204 {
205         if (m_customCursor) delete m_customCursor;
206
207         /*if(ugly_hack==m_windowRef) ugly_hack= NULL;
208         
209         if(ugly_hack==NULL) setDrawingContextType(GHOST_kDrawingContextTypeNone);*/
210     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
211         [m_openGLView release];
212         
213         if (m_window) {
214                 [m_window close];
215                 [m_window release];
216                 m_window = nil;
217         }
218         [pool drain];
219 }
220
221 #pragma mark accessors
222
223 bool GHOST_WindowCocoa::getValid() const
224 {
225     bool valid;
226     if (!m_fullScreen) {
227         valid = (m_window != 0); //&& ::IsValidWindowPtr(m_windowRef);
228     }
229     else {
230         valid = true;
231     }
232     return valid;
233 }
234
235
236 void GHOST_WindowCocoa::setTitle(const STR_String& title)
237 {
238     GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setTitle(): window invalid")
239         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
240
241         NSString *windowTitle = [[NSString alloc] initWithUTF8String:title];
242         
243         [m_window setTitle:windowTitle];
244         
245         [windowTitle release];
246         [pool drain];
247 }
248
249
250 void GHOST_WindowCocoa::getTitle(STR_String& title) const
251 {
252     GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getTitle(): window invalid")
253
254         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
255
256         NSString *windowTitle = [m_window title];
257
258         if (windowTitle != nil) {
259                 title = [windowTitle UTF8String];               
260         }
261         
262         [pool drain];
263 }
264
265
266 void GHOST_WindowCocoa::getWindowBounds(GHOST_Rect& bounds) const
267 {
268         NSRect rect;
269         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getWindowBounds(): window invalid")
270
271         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
272         
273         NSRect screenSize = [[m_window screen] visibleFrame];
274
275         rect = [m_window frame];
276
277         bounds.m_b = screenSize.size.height - (rect.origin.y -screenSize.origin.y);
278         bounds.m_l = rect.origin.x -screenSize.origin.x;
279         bounds.m_r = rect.origin.x-screenSize.origin.x + rect.size.width;
280         bounds.m_t = screenSize.size.height - (rect.origin.y + rect.size.height -screenSize.origin.y);
281         
282         [pool drain];
283 }
284
285
286 void GHOST_WindowCocoa::getClientBounds(GHOST_Rect& bounds) const
287 {
288         NSRect rect;
289         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getClientBounds(): window invalid")
290         
291         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
292         
293         if (!m_fullScreen)
294         {
295                 NSRect screenSize = [[m_window screen] visibleFrame];
296
297                 //Max window contents as screen size (excluding title bar...)
298                 NSRect contentRect = [NSWindow contentRectForFrameRect:screenSize
299                                                                                                          styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask)];
300
301                 rect = [m_window contentRectForFrameRect:[m_window frame]];
302                 
303                 bounds.m_b = contentRect.size.height - (rect.origin.y -contentRect.origin.y);
304                 bounds.m_l = rect.origin.x -contentRect.origin.x;
305                 bounds.m_r = rect.origin.x-contentRect.origin.x + rect.size.width;
306                 bounds.m_t = contentRect.size.height - (rect.origin.y + rect.size.height -contentRect.origin.y);
307         }
308         else {
309                 NSRect screenSize = [[m_window screen] frame];
310                 
311                 bounds.m_b = screenSize.origin.y + screenSize.size.height;
312                 bounds.m_l = screenSize.origin.x;
313                 bounds.m_r = screenSize.origin.x + screenSize.size.width;
314                 bounds.m_t = screenSize.origin.y;
315         }
316         [pool drain];
317 }
318
319
320 GHOST_TSuccess GHOST_WindowCocoa::setClientWidth(GHOST_TUns32 width)
321 {
322         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setClientWidth(): window invalid")
323         GHOST_Rect cBnds, wBnds;
324         getClientBounds(cBnds);
325         if (((GHOST_TUns32)cBnds.getWidth()) != width) {
326                 NSSize size;
327                 size.width=width;
328                 size.height=cBnds.getHeight();
329                 [m_window setContentSize:size];
330         }
331         return GHOST_kSuccess;
332 }
333
334
335 GHOST_TSuccess GHOST_WindowCocoa::setClientHeight(GHOST_TUns32 height)
336 {
337         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setClientHeight(): window invalid")
338         GHOST_Rect cBnds, wBnds;
339         getClientBounds(cBnds);
340         if (((GHOST_TUns32)cBnds.getHeight()) != height) {
341                 NSSize size;
342                 size.width=cBnds.getWidth();
343                 size.height=height;
344                 [m_window setContentSize:size];
345         }
346         return GHOST_kSuccess;
347 }
348
349
350 GHOST_TSuccess GHOST_WindowCocoa::setClientSize(GHOST_TUns32 width, GHOST_TUns32 height)
351 {
352         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setClientSize(): window invalid")
353         GHOST_Rect cBnds, wBnds;
354         getClientBounds(cBnds);
355         if ((((GHOST_TUns32)cBnds.getWidth()) != width) ||
356             (((GHOST_TUns32)cBnds.getHeight()) != height)) {
357                 NSSize size;
358                 size.width=width;
359                 size.height=height;
360                 [m_window setContentSize:size];
361         }
362         return GHOST_kSuccess;
363 }
364
365
366 GHOST_TWindowState GHOST_WindowCocoa::getState() const
367 {
368         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getState(): window invalid")
369         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
370         GHOST_TWindowState state;
371         if (m_fullScreen) {
372                 state = GHOST_kWindowStateFullScreen;
373         } 
374         else if ([m_window isMiniaturized]) {
375                 state = GHOST_kWindowStateMinimized;
376         }
377         else if ([m_window isZoomed]) {
378                 state = GHOST_kWindowStateMaximized;
379         }
380         else {
381                 state = GHOST_kWindowStateNormal;
382         }
383         [pool drain];
384         return state;
385 }
386
387
388 void GHOST_WindowCocoa::screenToClient(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
389 {
390         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::screenToClient(): window invalid")
391         
392         NSPoint screenCoord;
393         NSPoint baseCoord;
394         
395         screenCoord.x = inX;
396         screenCoord.y = inY;
397         
398         baseCoord = [m_window convertScreenToBase:screenCoord];
399         
400         outX = baseCoord.x;
401         outY = baseCoord.y;
402 }
403
404
405 void GHOST_WindowCocoa::clientToScreen(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
406 {
407         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::clientToScreen(): window invalid")
408         
409         NSPoint screenCoord;
410         NSPoint baseCoord;
411         
412         baseCoord.x = inX;
413         baseCoord.y = inY;
414         
415         screenCoord = [m_window convertBaseToScreen:baseCoord];
416         
417         outX = screenCoord.x;
418         outY = screenCoord.y;
419 }
420
421
422 GHOST_TSuccess GHOST_WindowCocoa::setState(GHOST_TWindowState state)
423 {
424         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setState(): window invalid")
425     switch (state) {
426                 case GHOST_kWindowStateMinimized:
427             [m_window miniaturize:nil];
428             break;
429                 case GHOST_kWindowStateMaximized:
430                         [m_window zoom:nil];
431                         break;
432                 
433                 case GHOST_kWindowStateFullScreen:
434                         if (!m_fullScreen)
435                         {
436                                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
437                         
438                                 //This status change needs to be done before Cocoa call to enter fullscreen mode
439                                 //to give window delegate hint not to forward its deactivation to ghost wm that doesn't know view/window difference
440                                 m_fullScreen = true;
441
442                                 //Only 10.6 API will enable to manage several display in fullscreen mode, and topmenu autoshow
443                                 [m_openGLView enterFullScreenMode:[m_window screen] withOptions:nil];
444                                 
445                                 //Tell WM of view new size
446                                 m_systemCocoa->handleWindowEvent(GHOST_kEventWindowSize, this);
447                                 
448                                 [pool drain];
449                                 }
450                         break;
451                 case GHOST_kWindowStateNormal:
452         default:
453                         if (m_fullScreen)
454                         {
455                                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
456                                 m_fullScreen = false;
457
458                                 //Exit fullscreen
459                                 [m_openGLView exitFullScreenModeWithOptions:nil];
460                                 
461                                 [m_window makeKeyAndOrderFront:nil];
462                                 [m_window makeFirstResponder:m_openGLView];
463                                 
464                                 //Tell WM of view new size
465                                 m_systemCocoa->handleWindowEvent(GHOST_kEventWindowSize, this);
466                                 
467                                 [pool drain];
468                         }
469             else if ([m_window isMiniaturized])
470                                 [m_window deminiaturize:nil];
471                         else if ([m_window isZoomed])
472                                 [m_window zoom:nil];
473             break;
474     }
475     return GHOST_kSuccess;
476 }
477
478 GHOST_TSuccess GHOST_WindowCocoa::setModifiedState(bool isUnsavedChanges)
479 {
480         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
481         
482         [m_window setDocumentEdited:isUnsavedChanges];
483         
484         [pool drain];
485         return GHOST_Window::setModifiedState(isUnsavedChanges);
486 }
487
488
489
490 GHOST_TSuccess GHOST_WindowCocoa::setOrder(GHOST_TWindowOrder order)
491 {
492         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setOrder(): window invalid")
493     if (order == GHOST_kWindowOrderTop) {
494                 [m_window orderFront:nil];
495     }
496     else {
497                 [m_window orderBack:nil];
498     }
499     return GHOST_kSuccess;
500 }
501
502 #pragma mark Drawing context
503
504 /*#define  WAIT_FOR_VSYNC 1*/
505
506 GHOST_TSuccess GHOST_WindowCocoa::swapBuffers()
507 {
508     if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) {
509         if (m_openGLContext != nil) {
510                         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
511                         [m_openGLContext flushBuffer];
512                         [pool drain];
513             return GHOST_kSuccess;
514         }
515     }
516     return GHOST_kFailure;
517 }
518
519 GHOST_TSuccess GHOST_WindowCocoa::updateDrawingContext()
520 {
521         if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) {
522                 if (m_openGLContext != nil) {
523                         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
524                         [m_openGLContext update];
525                         [pool drain];
526                         return GHOST_kSuccess;
527                 }
528         }
529         return GHOST_kFailure;
530 }
531
532 GHOST_TSuccess GHOST_WindowCocoa::activateDrawingContext()
533 {
534         if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) {
535                 if (m_openGLContext != nil) {
536                         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
537                         
538                         [m_openGLContext makeCurrentContext];
539 #ifdef GHOST_DRAW_CARBON_GUTTER
540                         // Restrict drawing to non-gutter area
541                         ::aglEnable(m_aglCtx, AGL_BUFFER_RECT);
542                         GHOST_Rect bnds;
543                         getClientBounds(bnds);
544                         GLint b[4] =
545                         {
546                                 bnds.m_l,
547                                 bnds.m_t+s_sizeRectSize,
548                                 bnds.m_r-bnds.m_l,
549                                 bnds.m_b-bnds.m_t
550                         };
551                         GLboolean result = ::aglSetInteger(m_aglCtx, AGL_BUFFER_RECT, b);
552 #endif //GHOST_DRAW_CARBON_GUTTER
553                         [pool drain];
554                         return GHOST_kSuccess;
555                 }
556         }
557         return GHOST_kFailure;
558 }
559
560
561 GHOST_TSuccess GHOST_WindowCocoa::installDrawingContext(GHOST_TDrawingContextType type)
562 {
563         GHOST_TSuccess success = GHOST_kFailure;
564         
565         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
566         
567         NSOpenGLPixelFormat *pixelFormat;
568         NSOpenGLContext *tmpOpenGLContext;
569         
570         switch (type) {
571                 case GHOST_kDrawingContextTypeOpenGL:
572                         if (!getValid()) break;
573                                         
574                                 pixelFormat = [m_openGLView pixelFormat];
575                                 tmpOpenGLContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat
576                                                                                                                           shareContext:m_openGLContext];
577                                 if (tmpOpenGLContext == nil)
578                                         success = GHOST_kFailure;
579                                         break;
580 #ifdef WAIT_FOR_VSYNC
581                                 /* wait for vsync, to avoid tearing artifacts */
582                                 [tmpOpenGLContext setValues:1 forParameter:NSOpenGLCPSwapInterval];
583 #endif
584                                 [m_openGLView setOpenGLContext:tmpOpenGLContext];
585                                 [tmpOpenGLContext setView:m_openGLView];
586                                 
587                                 [m_openGLContext release];
588                                 m_openGLContext = tmpOpenGLContext;
589                         break;
590                 
591                 case GHOST_kDrawingContextTypeNone:
592                         success = GHOST_kSuccess;
593                         break;
594                 
595                 default:
596                         break;
597         }
598         [pool drain];
599         return success;
600 }
601
602
603 GHOST_TSuccess GHOST_WindowCocoa::removeDrawingContext()
604 {
605         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
606         switch (m_drawingContextType) {
607                 case GHOST_kDrawingContextTypeOpenGL:
608                         [m_openGLView clearGLContext];
609                         [pool drain];
610                         return GHOST_kSuccess;
611                 case GHOST_kDrawingContextTypeNone:
612                         [pool drain];
613                         return GHOST_kSuccess;
614                         break;
615                 default:
616                         [pool drain];
617                         return GHOST_kFailure;
618         }
619 }
620
621
622 GHOST_TSuccess GHOST_WindowCocoa::invalidate()
623 {
624         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::invalidate(): window invalid")
625         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
626         [m_openGLView setNeedsDisplay:YES];
627         [pool drain];
628         return GHOST_kSuccess;
629 }
630
631 #pragma mark Cursor handling
632
633 void GHOST_WindowCocoa::loadCursor(bool visible, GHOST_TStandardCursor cursor) const
634 {
635         static bool systemCursorVisible = true;
636         
637         NSAutoreleasePool *pool =[[NSAutoreleasePool alloc] init];
638
639         NSCursor *tmpCursor =nil;
640         
641         if (visible != systemCursorVisible) {
642                 if (visible) {
643                         [NSCursor unhide];
644                         systemCursorVisible = true;
645                 }
646                 else {
647                         [NSCursor hide];
648                         systemCursorVisible = false;
649                 }
650         }
651
652         if (cursor == GHOST_kStandardCursorCustom && m_customCursor) {
653                 tmpCursor = m_customCursor;
654         } else {
655                 switch (cursor) {
656                         case GHOST_kStandardCursorDestroy:
657                                 tmpCursor = [NSCursor disappearingItemCursor];
658                                 break;
659                         case GHOST_kStandardCursorText:
660                                 tmpCursor = [NSCursor IBeamCursor];
661                                 break;
662                         case GHOST_kStandardCursorCrosshair:
663                                 tmpCursor = [NSCursor crosshairCursor];
664                                 break;
665                         case GHOST_kStandardCursorUpDown:
666                                 tmpCursor = [NSCursor resizeUpDownCursor];
667                                 break;
668                         case GHOST_kStandardCursorLeftRight:
669                                 tmpCursor = [NSCursor resizeLeftRightCursor];
670                                 break;
671                         case GHOST_kStandardCursorTopSide:
672                                 tmpCursor = [NSCursor resizeUpCursor];
673                                 break;
674                         case GHOST_kStandardCursorBottomSide:
675                                 tmpCursor = [NSCursor resizeDownCursor];
676                                 break;
677                         case GHOST_kStandardCursorLeftSide:
678                                 tmpCursor = [NSCursor resizeLeftCursor];
679                                 break;
680                         case GHOST_kStandardCursorRightSide:
681                                 tmpCursor = [NSCursor resizeRightCursor];
682                                 break;
683                         case GHOST_kStandardCursorRightArrow:
684                         case GHOST_kStandardCursorInfo:
685                         case GHOST_kStandardCursorLeftArrow:
686                         case GHOST_kStandardCursorHelp:
687                         case GHOST_kStandardCursorCycle:
688                         case GHOST_kStandardCursorSpray:
689                         case GHOST_kStandardCursorWait:
690                         case GHOST_kStandardCursorTopLeftCorner:
691                         case GHOST_kStandardCursorTopRightCorner:
692                         case GHOST_kStandardCursorBottomRightCorner:
693                         case GHOST_kStandardCursorBottomLeftCorner:
694                         case GHOST_kStandardCursorDefault:
695                         default:
696                                 tmpCursor = [NSCursor arrowCursor];
697                                 break;
698                 };
699         }
700         [tmpCursor set];
701         [pool drain];
702 }
703
704
705
706 GHOST_TSuccess GHOST_WindowCocoa::setWindowCursorVisibility(bool visible)
707 {
708         if ([m_window isVisible]) {
709                 loadCursor(visible, getCursorShape());
710         }
711         
712         return GHOST_kSuccess;
713 }
714
715 GHOST_TSuccess GHOST_WindowCocoa::setWindowCursorGrab(bool grab, bool warp, bool restore)
716 {
717         if (grab)
718         {
719                 if(warp) {
720                         m_systemCocoa->getCursorPosition(m_cursorWarpInitPos[0], m_cursorWarpInitPos[1]);
721                         
722                         setCursorWarpAccum(0, 0);
723                         setWindowCursorVisibility(false);
724                         m_cursorWarp= true;
725                 }
726                 return CGAssociateMouseAndMouseCursorPosition(false) == kCGErrorSuccess ? GHOST_kSuccess : GHOST_kFailure;
727         }
728         else {
729                 if(m_cursorWarp)
730                 {/* are we exiting warp */
731                         setWindowCursorVisibility(true);
732                         /* Almost works without but important otherwise the mouse GHOST location can be incorrect on exit */
733                         if(restore) {
734                                 GHOST_Rect bounds;
735                                 GHOST_TInt32 x_new, y_new, x_rel, y_rel;
736                                 
737                                 getClientBounds(bounds);
738                                 printf("\ncursor ungrab with restore");
739                                 x_new= m_cursorWarpInitPos[0]+m_cursorWarpAccumPos[0];
740                                 y_new= m_cursorWarpInitPos[1]+m_cursorWarpAccumPos[1];
741                                 
742                                 screenToClient(x_new, y_new, x_rel, y_rel);
743                                 
744                                 if(x_rel < 0)           x_new = (x_new-x_rel) + 2;
745                                 if(y_rel < 0)           y_new = (y_new-y_rel) + 2;
746                                 if(x_rel > bounds.getWidth())   x_new -= (x_rel-bounds.getWidth()) + 2;
747                                 if(y_rel > bounds.getHeight())  y_new -= (y_rel-bounds.getHeight()) + 2;
748                                 
749                                 clientToScreen(x_new, y_new, x_rel, y_rel);
750                                 m_systemCocoa->setCursorPosition(x_rel, y_rel);
751                                 
752                         }
753                         else {
754                                 m_systemCocoa->setCursorPosition(m_cursorWarpInitPos[0], m_cursorWarpInitPos[1]);
755                         }
756                         
757                         setCursorWarpAccum(0, 0);
758                         m_cursorWarp= false;
759                 }
760                 return CGAssociateMouseAndMouseCursorPosition(true) == kCGErrorSuccess ? GHOST_kSuccess : GHOST_kFailure;
761         }
762         
763 }
764         
765 GHOST_TSuccess GHOST_WindowCocoa::setWindowCursorShape(GHOST_TStandardCursor shape)
766 {
767         if (m_customCursor) {
768                 [m_customCursor release];
769                 m_customCursor = nil;
770         }
771
772         if ([m_window isVisible]) {
773                 loadCursor(getCursorVisibility(), shape);
774         }
775         
776         return GHOST_kSuccess;
777 }
778
779 #if 0
780 /** Reverse the bits in a GHOST_TUns8 */
781 static GHOST_TUns8 uns8ReverseBits(GHOST_TUns8 ch)
782 {
783         ch= ((ch>>1)&0x55) | ((ch<<1)&0xAA);
784         ch= ((ch>>2)&0x33) | ((ch<<2)&0xCC);
785         ch= ((ch>>4)&0x0F) | ((ch<<4)&0xF0);
786         return ch;
787 }
788 #endif
789
790
791 /** Reverse the bits in a GHOST_TUns16 */
792 static GHOST_TUns16 uns16ReverseBits(GHOST_TUns16 shrt)
793 {
794         shrt= ((shrt>>1)&0x5555) | ((shrt<<1)&0xAAAA);
795         shrt= ((shrt>>2)&0x3333) | ((shrt<<2)&0xCCCC);
796         shrt= ((shrt>>4)&0x0F0F) | ((shrt<<4)&0xF0F0);
797         shrt= ((shrt>>8)&0x00FF) | ((shrt<<8)&0xFF00);
798         return shrt;
799 }
800
801 GHOST_TSuccess GHOST_WindowCocoa::setWindowCustomCursorShape(GHOST_TUns8 *bitmap, GHOST_TUns8 *mask,
802                                         int sizex, int sizey, int hotX, int hotY, int fg_color, int bg_color)
803 {
804         int y;
805         NSPoint hotSpotPoint;
806         NSImage *cursorImage;
807         
808         if (m_customCursor) {
809                 [m_customCursor release];
810                 m_customCursor = nil;
811         }
812         /*TODO: implement this (but unused inproject at present)
813         cursorImage = [[NSImage alloc] initWithData:bitmap];
814         
815         for (y=0; y<16; y++) {
816 #if !defined(__LITTLE_ENDIAN__)
817                 m_customCursor->data[y] = uns16ReverseBits((bitmap[2*y]<<0) | (bitmap[2*y+1]<<8));
818                 m_customCursor->mask[y] = uns16ReverseBits((mask[2*y]<<0) | (mask[2*y+1]<<8));
819 #else
820                 m_customCursor->data[y] = uns16ReverseBits((bitmap[2*y+1]<<0) | (bitmap[2*y]<<8));
821                 m_customCursor->mask[y] = uns16ReverseBits((mask[2*y+1]<<0) | (mask[2*y]<<8));
822 #endif
823                         
824         }
825         
826         
827         hotSpotPoint.x = hotX;
828         hotSpotPoint.y = hotY;
829         
830         m_customCursor = [[NSCursor alloc] initWithImage:cursorImage
831                                                                  foregroundColorHint:<#(NSColor *)fg#>
832                                                                  backgroundColorHint:<#(NSColor *)bg#>
833                                                                                          hotSpot:hotSpotPoint];
834         
835         [cursorImage release];
836         
837         if ([m_window isVisible]) {
838                 loadCursor(getCursorVisibility(), GHOST_kStandardCursorCustom);
839         }
840         */
841         return GHOST_kSuccess;
842 }
843
844 GHOST_TSuccess GHOST_WindowCocoa::setWindowCustomCursorShape(GHOST_TUns8 bitmap[16][2], 
845                                                                                                 GHOST_TUns8 mask[16][2], int hotX, int hotY)
846 {
847         return setWindowCustomCursorShape((GHOST_TUns8*)bitmap, (GHOST_TUns8*) mask, 16, 16, hotX, hotY, 0, 1);
848 }
849
850 #pragma mark Old carbon stuff to remove
851
852 #if 0
853 void GHOST_WindowCocoa::setMac_windowState(short value)
854 {
855         mac_windowState = value;
856 }
857
858 short GHOST_WindowCocoa::getMac_windowState()
859 {
860         return mac_windowState;
861 }
862
863 void GHOST_WindowCocoa::gen2mac(const STR_String& in, Str255 out) const
864 {
865         STR_String tempStr  = in;
866         int num = tempStr.Length();
867         if (num > 255) num = 255;
868         ::memcpy(out+1, tempStr.Ptr(), num);
869         out[0] = num;
870 }
871
872
873 void GHOST_WindowCocoa::mac2gen(const Str255 in, STR_String& out) const
874 {
875         char tmp[256];
876         ::memcpy(tmp, in+1, in[0]);
877         tmp[in[0]] = '\0';
878         out = tmp;
879 }
880
881 #endif