3 * ***** BEGIN GPL LICENSE BLOCK *****
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.
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.
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.
19 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
20 * All rights reserved.
22 * The Original Code is: all of this file.
24 * Contributor(s): Maarten Gribnau 05/2001
25 Damien Plisson 10/2009
27 * ***** END GPL LICENSE BLOCK *****
30 #include <Cocoa/Cocoa.h>
32 #ifndef MAC_OS_X_VERSION_10_6
33 //Use of the SetSystemUIMode function (64bit compatible)
34 #include <Carbon/Carbon.h>
37 #include "GHOST_WindowCocoa.h"
38 #include "GHOST_SystemCocoa.h"
39 #include "GHOST_Debug.h"
42 // Pixel Format Attributes for the windowed NSOpenGLContext
43 static NSOpenGLPixelFormatAttribute pixelFormatAttrsWindow[] =
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
52 #pragma mark Cocoa window delegate object
54 @interface CocoaWindowDelegate : NSObject
56 GHOST_SystemCocoa *systemCocoa;
57 GHOST_WindowCocoa *associatedWindow;
60 - (void)setSystemAndWindowCocoa:(const GHOST_SystemCocoa *)sysCocoa windowCocoa:(GHOST_WindowCocoa *)winCocoa;
61 - (void)windowWillClose:(NSNotification *)notification;
62 - (void)windowDidBecomeKey:(NSNotification *)notification;
63 - (void)windowDidResignKey:(NSNotification *)notification;
64 - (void)windowDidUpdate:(NSNotification *)notification;
65 - (void)windowDidResize:(NSNotification *)notification;
68 @implementation CocoaWindowDelegate : NSObject
69 - (void)setSystemAndWindowCocoa:(GHOST_SystemCocoa *)sysCocoa windowCocoa:(GHOST_WindowCocoa *)winCocoa
71 systemCocoa = sysCocoa;
72 associatedWindow = winCocoa;
75 - (void)windowWillClose:(NSNotification *)notification
77 systemCocoa->handleWindowEvent(GHOST_kEventWindowClose, associatedWindow);
80 - (void)windowDidBecomeKey:(NSNotification *)notification
82 systemCocoa->handleWindowEvent(GHOST_kEventWindowActivate, associatedWindow);
85 - (void)windowDidResignKey:(NSNotification *)notification
87 //The window is no more key when its own view becomes fullscreen
88 //but ghost doesn't know the view/window difference, so hide this fact
89 if (associatedWindow->getState() != GHOST_kWindowStateFullScreen)
90 systemCocoa->handleWindowEvent(GHOST_kEventWindowDeactivate, associatedWindow);
93 - (void)windowDidUpdate:(NSNotification *)notification
95 systemCocoa->handleWindowEvent(GHOST_kEventWindowUpdate, associatedWindow);
98 - (void)windowDidResize:(NSNotification *)notification
100 systemCocoa->handleWindowEvent(GHOST_kEventWindowSize, associatedWindow);
104 #pragma mark NSWindow subclass
105 //We need to subclass it to tell that even borderless (fullscreen), it can become key (receive user events)
106 @interface CocoaWindow: NSWindow
110 -(BOOL)canBecomeKeyWindow;
113 @implementation CocoaWindow
115 -(BOOL)canBecomeKeyWindow
124 #pragma mark NSOpenGLView subclass
125 //We need to subclass it in order to give Cocoa the feeling key events are trapped
126 @interface CocoaOpenGLView : NSOpenGLView
131 @implementation CocoaOpenGLView
133 - (BOOL)acceptsFirstResponder
138 //The trick to prevent Cocoa from complaining (beeping)
139 - (void)keyDown:(NSEvent *)theEvent
150 #pragma mark initialization / finalization
152 NSOpenGLContext* GHOST_WindowCocoa::s_firstOpenGLcontext = nil;
154 GHOST_WindowCocoa::GHOST_WindowCocoa(
155 GHOST_SystemCocoa *systemCocoa,
156 const STR_String& title,
161 GHOST_TWindowState state,
162 GHOST_TDrawingContextType type,
163 const bool stereoVisual
165 GHOST_Window(title, left, top, width, height, state, GHOST_kDrawingContextTypeNone),
168 m_systemCocoa = systemCocoa;
169 m_fullScreen = false;
171 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
177 rect.origin.x = left;
179 rect.size.width = width;
180 rect.size.height = height;
182 m_window = [[CocoaWindow alloc] initWithContentRect:rect
183 styleMask:NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask | NSMiniaturizableWindowMask
184 backing:NSBackingStoreBuffered defer:NO];
185 if (m_window == nil) {
193 //Creates the OpenGL View inside the window
194 NSOpenGLPixelFormat *pixelFormat =
195 [[NSOpenGLPixelFormat alloc] initWithAttributes:pixelFormatAttrsWindow];
197 m_openGLView = [[CocoaOpenGLView alloc] initWithFrame:rect
198 pixelFormat:pixelFormat];
200 [pixelFormat release];
202 m_openGLContext = [m_openGLView openGLContext]; //This context will be replaced by the proper one just after
204 [m_window setContentView:m_openGLView];
205 [m_window setInitialFirstResponder:m_openGLView];
207 [m_window setReleasedWhenClosed:NO]; //To avoid bad pointer exception in case of user closing the window
209 [m_window makeKeyAndOrderFront:nil];
211 setDrawingContextType(type);
212 updateDrawingContext();
213 activateDrawingContext();
215 m_tablet.Active = GHOST_kTabletModeNone;
217 CocoaWindowDelegate *windowDelegate = [[CocoaWindowDelegate alloc] init];
218 [windowDelegate setSystemAndWindowCocoa:systemCocoa windowCocoa:this];
219 [m_window setDelegate:windowDelegate];
221 [m_window setAcceptsMouseMovedEvents:YES];
223 if (state == GHOST_kWindowStateFullScreen)
224 setState(GHOST_kWindowStateFullScreen);
230 GHOST_WindowCocoa::~GHOST_WindowCocoa()
232 if (m_customCursor) delete m_customCursor;
234 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
235 [m_openGLView release];
239 [[m_window delegate] release];
244 //Check for other blender opened windows and make the frontmost key
245 NSArray *windowsList = [NSApp orderedWindows];
246 if ([windowsList count]) {
247 [[windowsList objectAtIndex:0] makeKeyAndOrderFront:nil];
252 #pragma mark accessors
254 bool GHOST_WindowCocoa::getValid() const
258 valid = (m_window != 0); //&& ::IsValidWindowPtr(m_windowRef);
267 void GHOST_WindowCocoa::setTitle(const STR_String& title)
269 GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setTitle(): window invalid")
270 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
272 NSString *windowTitle = [[NSString alloc] initWithUTF8String:title];
274 //Set associated file if applicable
275 if ([windowTitle hasPrefix:@"Blender"])
277 NSRange fileStrRange;
278 NSString *associatedFileName;
281 fileStrRange.location = [windowTitle rangeOfString:@"["].location+1;
282 len = [windowTitle rangeOfString:@"]"].location - fileStrRange.location;
286 fileStrRange.length = len;
287 associatedFileName = [windowTitle substringWithRange:fileStrRange];
289 [m_window setRepresentedFilename:associatedFileName];
291 @catch (NSException * e) {
292 printf("\nInvalid file path given in window title");
294 [m_window setTitle:[associatedFileName lastPathComponent]];
297 [m_window setTitle:windowTitle];
298 [m_window setRepresentedFilename:@""];
302 [m_window setTitle:windowTitle];
303 [m_window setRepresentedFilename:@""];
307 [windowTitle release];
312 void GHOST_WindowCocoa::getTitle(STR_String& title) const
314 GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getTitle(): window invalid")
316 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
318 NSString *windowTitle = [m_window title];
320 if (windowTitle != nil) {
321 title = [windowTitle UTF8String];
328 void GHOST_WindowCocoa::getWindowBounds(GHOST_Rect& bounds) const
331 GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getWindowBounds(): window invalid")
333 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
335 NSRect screenSize = [[m_window screen] visibleFrame];
337 rect = [m_window frame];
339 bounds.m_b = screenSize.size.height - (rect.origin.y -screenSize.origin.y);
340 bounds.m_l = rect.origin.x -screenSize.origin.x;
341 bounds.m_r = rect.origin.x-screenSize.origin.x + rect.size.width;
342 bounds.m_t = screenSize.size.height - (rect.origin.y + rect.size.height -screenSize.origin.y);
348 void GHOST_WindowCocoa::getClientBounds(GHOST_Rect& bounds) const
351 GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getClientBounds(): window invalid")
353 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
357 NSRect screenSize = [[m_window screen] visibleFrame];
359 //Max window contents as screen size (excluding title bar...)
360 NSRect contentRect = [CocoaWindow contentRectForFrameRect:screenSize
361 styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)];
363 rect = [m_window contentRectForFrameRect:[m_window frame]];
365 bounds.m_b = contentRect.size.height - (rect.origin.y -contentRect.origin.y);
366 bounds.m_l = rect.origin.x -contentRect.origin.x;
367 bounds.m_r = rect.origin.x-contentRect.origin.x + rect.size.width;
368 bounds.m_t = contentRect.size.height - (rect.origin.y + rect.size.height -contentRect.origin.y);
371 NSRect screenSize = [[m_window screen] frame];
373 bounds.m_b = screenSize.origin.y + screenSize.size.height;
374 bounds.m_l = screenSize.origin.x;
375 bounds.m_r = screenSize.origin.x + screenSize.size.width;
376 bounds.m_t = screenSize.origin.y;
382 GHOST_TSuccess GHOST_WindowCocoa::setClientWidth(GHOST_TUns32 width)
384 GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setClientWidth(): window invalid")
385 GHOST_Rect cBnds, wBnds;
386 getClientBounds(cBnds);
387 if (((GHOST_TUns32)cBnds.getWidth()) != width) {
390 size.height=cBnds.getHeight();
391 [m_window setContentSize:size];
393 return GHOST_kSuccess;
397 GHOST_TSuccess GHOST_WindowCocoa::setClientHeight(GHOST_TUns32 height)
399 GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setClientHeight(): window invalid")
400 GHOST_Rect cBnds, wBnds;
401 getClientBounds(cBnds);
402 if (((GHOST_TUns32)cBnds.getHeight()) != height) {
404 size.width=cBnds.getWidth();
406 [m_window setContentSize:size];
408 return GHOST_kSuccess;
412 GHOST_TSuccess GHOST_WindowCocoa::setClientSize(GHOST_TUns32 width, GHOST_TUns32 height)
414 GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setClientSize(): window invalid")
415 GHOST_Rect cBnds, wBnds;
416 getClientBounds(cBnds);
417 if ((((GHOST_TUns32)cBnds.getWidth()) != width) ||
418 (((GHOST_TUns32)cBnds.getHeight()) != height)) {
422 [m_window setContentSize:size];
424 return GHOST_kSuccess;
428 GHOST_TWindowState GHOST_WindowCocoa::getState() const
430 GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getState(): window invalid")
431 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
432 GHOST_TWindowState state;
434 state = GHOST_kWindowStateFullScreen;
436 else if ([m_window isMiniaturized]) {
437 state = GHOST_kWindowStateMinimized;
439 else if ([m_window isZoomed]) {
440 state = GHOST_kWindowStateMaximized;
443 state = GHOST_kWindowStateNormal;
450 void GHOST_WindowCocoa::screenToClient(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
452 GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::screenToClient(): window invalid")
460 baseCoord = [m_window convertScreenToBase:screenCoord];
467 void GHOST_WindowCocoa::clientToScreen(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
469 GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::clientToScreen(): window invalid")
477 screenCoord = [m_window convertBaseToScreen:baseCoord];
479 outX = screenCoord.x;
480 outY = screenCoord.y;
484 * @note Fullscreen switch is not actual fullscreen with display capture. As this capture removes all OS X window manager features.
485 * Instead, the menu bar and the dock are hidden, and the window is made borderless and enlarged.
486 * Thus, process switch, exposé, spaces, ... still work in fullscreen mode
488 GHOST_TSuccess GHOST_WindowCocoa::setState(GHOST_TWindowState state)
490 GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setState(): window invalid")
492 case GHOST_kWindowStateMinimized:
493 [m_window miniaturize:nil];
495 case GHOST_kWindowStateMaximized:
499 case GHOST_kWindowStateFullScreen:
502 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
504 //This status change needs to be done before Cocoa call to enter fullscreen mode
505 //to give window delegate hint not to forward its deactivation to ghost wm that doesn't know view/window difference
508 #ifdef MAC_OS_X_VERSION_10_6
509 //10.6 provides Cocoa functions to autoshow menu bar, and to change a window style
510 //Hide menu & dock if needed
511 if ([[m_window screen] isEqual:[NSScreen mainScreen]])
513 [NSApp setPresentationOptions:(NSApplicationPresentationHideDock | NSApplicationPresentationAutoHideMenuBar)];
515 //Make window borderless and enlarge it
516 [m_window setStyleMask:NSBorderlessWindowMask];
517 [m_window setFrame:[[m_window screen] frame] display:YES];
519 //With 10.5, we need to create a new window to change its style to borderless
520 //Hide menu & dock if needed
521 if ([[m_window screen] isEqual:[NSScreen mainScreen]])
523 //Cocoa function in 10.5 does not allow to set the menu bar in auto-show mode [NSMenu setMenuBarVisible:NO];
524 //One of the very few 64bit compatible Carbon function
525 SetSystemUIMode(kUIModeAllHidden,kUIOptionAutoShowMenuBar);
527 //Create a fullscreen borderless window
528 CocoaWindow *tmpWindow = [[CocoaWindow alloc]
529 initWithContentRect:[[m_window screen] frame]
530 styleMask:NSBorderlessWindowMask
531 backing:NSBackingStoreBuffered
533 //Copy current window parameters
534 [tmpWindow setTitle:[m_window title]];
535 [tmpWindow setRepresentedFilename:[m_window representedFilename]];
536 [tmpWindow setReleasedWhenClosed:NO];
537 [tmpWindow setAcceptsMouseMovedEvents:YES];
538 [tmpWindow setDelegate:[m_window delegate]];
540 //Assign the openGL view to the new window
541 [tmpWindow setContentView:m_openGLView];
543 //Show the new window
544 [tmpWindow makeKeyAndOrderFront:nil];
545 //Close and release old window
546 [m_window setDelegate:nil]; // To avoid the notification of "window closed" event
549 m_window = tmpWindow;
552 //Tell WM of view new size
553 m_systemCocoa->handleWindowEvent(GHOST_kEventWindowSize, this);
558 case GHOST_kWindowStateNormal:
562 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
563 m_fullScreen = false;
566 #ifdef MAC_OS_X_VERSION_10_6
567 //Show again menu & dock if needed
568 if ([[m_window screen] isEqual:[NSScreen mainScreen]])
570 [NSApp setPresentationOptions:NSApplicationPresentationDefault];
572 //Make window normal and resize it
573 [m_window setStyleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)];
574 [m_window setFrame:[[m_window screen] visibleFrame] display:YES];
576 //With 10.5, we need to create a new window to change its style to borderless
577 //Show menu & dock if needed
578 if ([[m_window screen] isEqual:[NSScreen mainScreen]])
580 //Cocoa function in 10.5 does not allow to set the menu bar in auto-show mode [NSMenu setMenuBarVisible:YES];
581 SetSystemUIMode(kUIModeNormal, 0); //One of the very few 64bit compatible Carbon function
583 //Create a fullscreen borderless window
584 CocoaWindow *tmpWindow = [[CocoaWindow alloc]
585 initWithContentRect:[[m_window screen] frame]
586 styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)
587 backing:NSBackingStoreBuffered
589 //Copy current window parameters
590 [tmpWindow setTitle:[m_window title]];
591 [tmpWindow setRepresentedFilename:[m_window representedFilename]];
592 [tmpWindow setReleasedWhenClosed:NO];
593 [tmpWindow setAcceptsMouseMovedEvents:YES];
594 [tmpWindow setDelegate:[m_window delegate]];
596 //Assign the openGL view to the new window
597 [tmpWindow setContentView:m_openGLView];
599 //Show the new window
600 [tmpWindow makeKeyAndOrderFront:nil];
601 //Close and release old window
602 [m_window setDelegate:nil]; // To avoid the notification of "window closed" event
605 m_window = tmpWindow;
608 //Tell WM of view new size
609 m_systemCocoa->handleWindowEvent(GHOST_kEventWindowSize, this);
613 else if ([m_window isMiniaturized])
614 [m_window deminiaturize:nil];
615 else if ([m_window isZoomed])
619 return GHOST_kSuccess;
622 GHOST_TSuccess GHOST_WindowCocoa::setModifiedState(bool isUnsavedChanges)
624 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
626 [m_window setDocumentEdited:isUnsavedChanges];
629 return GHOST_Window::setModifiedState(isUnsavedChanges);
634 GHOST_TSuccess GHOST_WindowCocoa::setOrder(GHOST_TWindowOrder order)
636 GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setOrder(): window invalid")
637 if (order == GHOST_kWindowOrderTop) {
638 [m_window orderFront:nil];
641 [m_window orderBack:nil];
643 return GHOST_kSuccess;
646 #pragma mark Drawing context
648 /*#define WAIT_FOR_VSYNC 1*/
650 GHOST_TSuccess GHOST_WindowCocoa::swapBuffers()
652 if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) {
653 if (m_openGLContext != nil) {
654 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
655 [m_openGLContext flushBuffer];
657 return GHOST_kSuccess;
660 return GHOST_kFailure;
663 GHOST_TSuccess GHOST_WindowCocoa::updateDrawingContext()
665 if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) {
666 if (m_openGLContext != nil) {
667 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
668 [m_openGLContext update];
670 return GHOST_kSuccess;
673 return GHOST_kFailure;
676 GHOST_TSuccess GHOST_WindowCocoa::activateDrawingContext()
678 if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) {
679 if (m_openGLContext != nil) {
680 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
681 [m_openGLContext makeCurrentContext];
683 return GHOST_kSuccess;
686 return GHOST_kFailure;
690 GHOST_TSuccess GHOST_WindowCocoa::installDrawingContext(GHOST_TDrawingContextType type)
692 GHOST_TSuccess success = GHOST_kFailure;
694 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
696 NSOpenGLPixelFormat *pixelFormat;
697 NSOpenGLContext *tmpOpenGLContext;
700 case GHOST_kDrawingContextTypeOpenGL:
701 if (!getValid()) break;
703 pixelFormat = [m_openGLView pixelFormat];
704 tmpOpenGLContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat
705 shareContext:s_firstOpenGLcontext];
706 if (tmpOpenGLContext == nil) {
707 success = GHOST_kFailure;
711 if (!s_firstOpenGLcontext) s_firstOpenGLcontext = tmpOpenGLContext;
712 #ifdef WAIT_FOR_VSYNC
713 /* wait for vsync, to avoid tearing artifacts */
714 [tmpOpenGLContext setValues:1 forParameter:NSOpenGLCPSwapInterval];
716 [m_openGLView setOpenGLContext:tmpOpenGLContext];
717 [tmpOpenGLContext setView:m_openGLView];
719 m_openGLContext = tmpOpenGLContext;
722 case GHOST_kDrawingContextTypeNone:
723 success = GHOST_kSuccess;
734 GHOST_TSuccess GHOST_WindowCocoa::removeDrawingContext()
736 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
737 switch (m_drawingContextType) {
738 case GHOST_kDrawingContextTypeOpenGL:
741 [m_openGLView clearGLContext];
742 if (s_firstOpenGLcontext == m_openGLContext) s_firstOpenGLcontext = nil;
743 m_openGLContext = nil;
746 return GHOST_kSuccess;
747 case GHOST_kDrawingContextTypeNone:
749 return GHOST_kSuccess;
753 return GHOST_kFailure;
758 GHOST_TSuccess GHOST_WindowCocoa::invalidate()
760 GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::invalidate(): window invalid")
761 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
762 [m_openGLView setNeedsDisplay:YES];
764 return GHOST_kSuccess;
767 #pragma mark Cursor handling
769 void GHOST_WindowCocoa::loadCursor(bool visible, GHOST_TStandardCursor cursor) const
771 static bool systemCursorVisible = true;
773 NSAutoreleasePool *pool =[[NSAutoreleasePool alloc] init];
775 NSCursor *tmpCursor =nil;
777 if (visible != systemCursorVisible) {
780 systemCursorVisible = true;
784 systemCursorVisible = false;
788 if (cursor == GHOST_kStandardCursorCustom && m_customCursor) {
789 tmpCursor = m_customCursor;
792 case GHOST_kStandardCursorDestroy:
793 tmpCursor = [NSCursor disappearingItemCursor];
795 case GHOST_kStandardCursorText:
796 tmpCursor = [NSCursor IBeamCursor];
798 case GHOST_kStandardCursorCrosshair:
799 tmpCursor = [NSCursor crosshairCursor];
801 case GHOST_kStandardCursorUpDown:
802 tmpCursor = [NSCursor resizeUpDownCursor];
804 case GHOST_kStandardCursorLeftRight:
805 tmpCursor = [NSCursor resizeLeftRightCursor];
807 case GHOST_kStandardCursorTopSide:
808 tmpCursor = [NSCursor resizeUpCursor];
810 case GHOST_kStandardCursorBottomSide:
811 tmpCursor = [NSCursor resizeDownCursor];
813 case GHOST_kStandardCursorLeftSide:
814 tmpCursor = [NSCursor resizeLeftCursor];
816 case GHOST_kStandardCursorRightSide:
817 tmpCursor = [NSCursor resizeRightCursor];
819 case GHOST_kStandardCursorRightArrow:
820 case GHOST_kStandardCursorInfo:
821 case GHOST_kStandardCursorLeftArrow:
822 case GHOST_kStandardCursorHelp:
823 case GHOST_kStandardCursorCycle:
824 case GHOST_kStandardCursorSpray:
825 case GHOST_kStandardCursorWait:
826 case GHOST_kStandardCursorTopLeftCorner:
827 case GHOST_kStandardCursorTopRightCorner:
828 case GHOST_kStandardCursorBottomRightCorner:
829 case GHOST_kStandardCursorBottomLeftCorner:
830 case GHOST_kStandardCursorDefault:
832 tmpCursor = [NSCursor arrowCursor];
842 GHOST_TSuccess GHOST_WindowCocoa::setWindowCursorVisibility(bool visible)
844 if ([m_window isVisible]) {
845 loadCursor(visible, getCursorShape());
848 return GHOST_kSuccess;
852 //Override this method to provide set feature even if not in warp
853 inline bool GHOST_WindowCocoa::setCursorWarpAccum(GHOST_TInt32 x, GHOST_TInt32 y)
855 m_cursorWarpAccumPos[0]= x;
856 m_cursorWarpAccumPos[1]= y;
858 return GHOST_kSuccess;
862 GHOST_TSuccess GHOST_WindowCocoa::setWindowCursorGrab(bool grab, bool warp, bool restore)
866 //No need to perform grab without warp as it is always on in OS X
868 GHOST_TInt32 x_old,y_old;
871 m_systemCocoa->getCursorPosition(x_old,y_old);
872 screenToClient(x_old, y_old, m_cursorWarpInitPos[0], m_cursorWarpInitPos[1]);
873 //Warp position is stored in client (window base) coordinates
874 setWindowCursorVisibility(false);
875 return CGAssociateMouseAndMouseCursorPosition(false) == kCGErrorSuccess ? GHOST_kSuccess : GHOST_kFailure;
880 {/* are we exiting warp */
881 setWindowCursorVisibility(true);
882 /* Almost works without but important otherwise the mouse GHOST location can be incorrect on exit */
885 GHOST_TInt32 x_new, y_new, x_cur, y_cur;
887 getClientBounds(bounds);
888 x_new= m_cursorWarpInitPos[0]+m_cursorWarpAccumPos[0];
889 y_new= m_cursorWarpInitPos[1]+m_cursorWarpAccumPos[1];
891 if(x_new < 0) x_new = 0;
892 if(y_new < 0) y_new = 0;
893 if(x_new > bounds.getWidth()) x_new = bounds.getWidth();
894 if(y_new > bounds.getHeight()) y_new = bounds.getHeight();
896 //get/set cursor position works in screen coordinates
897 clientToScreen(x_new, y_new, x_cur, y_cur);
898 m_systemCocoa->setCursorPosition(x_cur, y_cur);
900 //As Cocoa will give as first deltaX,deltaY this change in cursor position, we need to compensate for it
901 //Issue appearing in case of two transform operations conducted w/o mouse motion in between
902 x_new=m_cursorWarpAccumPos[0];
903 y_new=m_cursorWarpAccumPos[1];
904 setCursorWarpAccum(-x_new, -y_new);
907 GHOST_TInt32 x_new, y_new;
908 //get/set cursor position works in screen coordinates
909 clientToScreen(m_cursorWarpInitPos[0], m_cursorWarpInitPos[1], x_new, y_new);
910 m_systemCocoa->setCursorPosition(x_new, y_new);
911 setCursorWarpAccum(0, 0);
915 return CGAssociateMouseAndMouseCursorPosition(true) == kCGErrorSuccess ? GHOST_kSuccess : GHOST_kFailure;
918 return GHOST_kSuccess;
921 GHOST_TSuccess GHOST_WindowCocoa::setWindowCursorShape(GHOST_TStandardCursor shape)
923 if (m_customCursor) {
924 [m_customCursor release];
925 m_customCursor = nil;
928 if ([m_window isVisible]) {
929 loadCursor(getCursorVisibility(), shape);
932 return GHOST_kSuccess;
935 /** Reverse the bits in a GHOST_TUns8
936 static GHOST_TUns8 uns8ReverseBits(GHOST_TUns8 ch)
938 ch= ((ch>>1)&0x55) | ((ch<<1)&0xAA);
939 ch= ((ch>>2)&0x33) | ((ch<<2)&0xCC);
940 ch= ((ch>>4)&0x0F) | ((ch<<4)&0xF0);
946 /** Reverse the bits in a GHOST_TUns16 */
947 static GHOST_TUns16 uns16ReverseBits(GHOST_TUns16 shrt)
949 shrt= ((shrt>>1)&0x5555) | ((shrt<<1)&0xAAAA);
950 shrt= ((shrt>>2)&0x3333) | ((shrt<<2)&0xCCCC);
951 shrt= ((shrt>>4)&0x0F0F) | ((shrt<<4)&0xF0F0);
952 shrt= ((shrt>>8)&0x00FF) | ((shrt<<8)&0xFF00);
956 GHOST_TSuccess GHOST_WindowCocoa::setWindowCustomCursorShape(GHOST_TUns8 *bitmap, GHOST_TUns8 *mask,
957 int sizex, int sizey, int hotX, int hotY, int fg_color, int bg_color)
960 NSPoint hotSpotPoint;
961 NSBitmapImageRep *cursorImageRep;
962 NSImage *cursorImage;
964 GHOST_TUns16 *cursorBitmap;
967 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
969 if (m_customCursor) {
970 [m_customCursor release];
971 m_customCursor = nil;
975 cursorImageRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:nil
982 colorSpaceName:NSDeviceBlackColorSpace
983 bytesPerRow:(sizex/8 + (sizex%8 >0 ?1:0))
987 cursorBitmap = (GHOST_TUns16*)[cursorImageRep bitmapData];
988 nbUns16 = [cursorImageRep bytesPerPlane]/2;
990 for (y=0; y<nbUns16; y++) {
991 #if !defined(__LITTLE_ENDIAN__)
992 cursorBitmap[y] = uns16ReverseBits((bitmap[2*y]<<0) | (bitmap[2*y+1]<<8));
993 cursorBitmap[nbUns16+y] = uns16ReverseBits((mask[2*y]<<0) | (mask[2*y+1]<<8));
995 cursorBitmap[y] = uns16ReverseBits((bitmap[2*y+1]<<0) | (bitmap[2*y]<<8));
996 cursorBitmap[nbUns16+y] = uns16ReverseBits((mask[2*y+1]<<0) | (mask[2*y]<<8));
1002 imSize.width = sizex;
1003 imSize.height= sizey;
1004 cursorImage = [[NSImage alloc] initWithSize:imSize];
1005 [cursorImage addRepresentation:cursorImageRep];
1007 hotSpotPoint.x = hotX;
1008 hotSpotPoint.y = hotY;
1010 //foreground and background color parameter is not handled for now (10.6)
1011 m_customCursor = [[NSCursor alloc] initWithImage:cursorImage
1012 hotSpot:hotSpotPoint];
1014 [cursorImageRep release];
1015 [cursorImage release];
1017 if ([m_window isVisible]) {
1018 loadCursor(getCursorVisibility(), GHOST_kStandardCursorCustom);
1021 return GHOST_kSuccess;
1024 GHOST_TSuccess GHOST_WindowCocoa::setWindowCustomCursorShape(GHOST_TUns8 bitmap[16][2],
1025 GHOST_TUns8 mask[16][2], int hotX, int hotY)
1027 return setWindowCustomCursorShape((GHOST_TUns8*)bitmap, (GHOST_TUns8*) mask, 16, 16, hotX, hotY, 0, 1);