2 * ***** BEGIN GPL LICENSE BLOCK *****
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
19 * All rights reserved.
21 * The Original Code is: all of this file.
23 * Contributor(s): Maarten Gribnau 05/2001
24 Damien Plisson 10/2009
26 * ***** END GPL LICENSE BLOCK *****
29 #include <Cocoa/Cocoa.h>
31 #if MAC_OS_X_VERSION_MIN_REQUIRED <= 1050
32 //Use of the SetSystemUIMode function (64bit compatible)
33 #include <Carbon/Carbon.h>
36 #include <OpenGL/gl.h>
37 #include <OpenGL/CGLRenderers.h>
38 /***** Multithreaded opengl code : uncomment for enabling
39 #include <OpenGL/OpenGL.h>
43 #include "GHOST_WindowCocoa.h"
44 #include "GHOST_SystemCocoa.h"
45 #include "GHOST_Debug.h"
47 #if MAC_OS_X_VERSION_MAX_ALLOWED < 1070
48 /* Lion style fullscreen support when building with the 10.6 SDK */
50 NSWindowCollectionBehaviorFullScreenPrimary = 1 << 7,
51 NSFullScreenWindowMask = 1 << 14
55 #pragma mark Cocoa window delegate object
57 @interface CocoaWindowDelegate : NSObject
60 GHOST_SystemCocoa *systemCocoa;
61 GHOST_WindowCocoa *associatedWindow;
64 - (void)setSystemAndWindowCocoa:(GHOST_SystemCocoa *)sysCocoa windowCocoa:(GHOST_WindowCocoa *)winCocoa;
65 - (void)windowDidBecomeKey:(NSNotification *)notification;
66 - (void)windowDidResignKey:(NSNotification *)notification;
67 - (void)windowDidExpose:(NSNotification *)notification;
68 - (void)windowDidResize:(NSNotification *)notification;
69 - (void)windowDidMove:(NSNotification *)notification;
70 - (void)windowWillMove:(NSNotification *)notification;
71 - (BOOL)windowShouldClose:(id)sender;
72 - (void)windowDidChangeBackingProperties:(NSNotification *)notification;
76 @implementation CocoaWindowDelegate : NSObject
77 - (void)setSystemAndWindowCocoa:(GHOST_SystemCocoa *)sysCocoa windowCocoa:(GHOST_WindowCocoa *)winCocoa
79 systemCocoa = sysCocoa;
80 associatedWindow = winCocoa;
83 - (void)windowDidBecomeKey:(NSNotification *)notification
85 systemCocoa->handleWindowEvent(GHOST_kEventWindowActivate, associatedWindow);
88 - (void)windowDidResignKey:(NSNotification *)notification
90 systemCocoa->handleWindowEvent(GHOST_kEventWindowDeactivate, associatedWindow);
93 - (void)windowDidExpose:(NSNotification *)notification
95 systemCocoa->handleWindowEvent(GHOST_kEventWindowUpdate, associatedWindow);
98 - (void)windowDidMove:(NSNotification *)notification
100 systemCocoa->handleWindowEvent(GHOST_kEventWindowMove, associatedWindow);
103 - (void)windowWillMove:(NSNotification *)notification
105 systemCocoa->handleWindowEvent(GHOST_kEventWindowMove, associatedWindow);
108 - (void)windowDidResize:(NSNotification *)notification
110 #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
111 //if (![[notification object] inLiveResize]) {
112 //Send event only once, at end of resize operation (when user has released mouse button)
114 systemCocoa->handleWindowEvent(GHOST_kEventWindowSize, associatedWindow);
115 #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
118 /* Live resize, send event, gets handled in wm_window.c. Needed because live resize runs in a modal loop, not letting main loop run */
119 if ([[notification object] inLiveResize]) {
120 systemCocoa->dispatchEvents();
124 - (void)windowDidChangeBackingProperties:(NSNotification *)notification
126 systemCocoa->handleWindowEvent(GHOST_kEventNativeResolutionChange, associatedWindow);
129 - (BOOL)windowShouldClose:(id)sender;
131 //Let Blender close the window rather than closing immediately
132 systemCocoa->handleWindowEvent(GHOST_kEventWindowClose, associatedWindow);
138 #pragma mark NSWindow subclass
139 //We need to subclass it to tell that even borderless (fullscreen), it can become key (receive user events)
140 @interface CocoaWindow: NSWindow
142 GHOST_SystemCocoa *systemCocoa;
143 GHOST_WindowCocoa *associatedWindow;
144 GHOST_TDragnDropTypes m_draggedObjectType;
146 - (void)setSystemAndWindowCocoa:(GHOST_SystemCocoa *)sysCocoa windowCocoa:(GHOST_WindowCocoa *)winCocoa;
147 - (GHOST_SystemCocoa*)systemCocoa;
149 @implementation CocoaWindow
150 - (void)setSystemAndWindowCocoa:(GHOST_SystemCocoa *)sysCocoa windowCocoa:(GHOST_WindowCocoa *)winCocoa
152 systemCocoa = sysCocoa;
153 associatedWindow = winCocoa;
155 - (GHOST_SystemCocoa*)systemCocoa
160 -(BOOL)canBecomeKeyWindow
165 //The drag'n'drop dragging destination methods
166 - (NSDragOperation)draggingEntered:(id < NSDraggingInfo >)sender
168 NSPoint mouseLocation = [sender draggingLocation];
169 NSPasteboard *draggingPBoard = [sender draggingPasteboard];
171 if ([[draggingPBoard types] containsObject:NSTIFFPboardType]) m_draggedObjectType = GHOST_kDragnDropTypeBitmap;
172 else if ([[draggingPBoard types] containsObject:NSFilenamesPboardType]) m_draggedObjectType = GHOST_kDragnDropTypeFilenames;
173 else if ([[draggingPBoard types] containsObject:NSStringPboardType]) m_draggedObjectType = GHOST_kDragnDropTypeString;
174 else return NSDragOperationNone;
176 associatedWindow->setAcceptDragOperation(TRUE); //Drag operation is accepted by default
177 systemCocoa->handleDraggingEvent(GHOST_kEventDraggingEntered, m_draggedObjectType, associatedWindow, mouseLocation.x, mouseLocation.y, nil);
178 return NSDragOperationCopy;
181 - (BOOL)wantsPeriodicDraggingUpdates
183 return NO; //No need to overflow blender event queue. Events shall be sent only on changes
186 - (NSDragOperation)draggingUpdated:(id < NSDraggingInfo >)sender
188 NSPoint mouseLocation = [sender draggingLocation];
190 systemCocoa->handleDraggingEvent(GHOST_kEventDraggingUpdated, m_draggedObjectType, associatedWindow, mouseLocation.x, mouseLocation.y, nil);
191 return associatedWindow->canAcceptDragOperation() ? NSDragOperationCopy : NSDragOperationNone;
194 - (void)draggingExited:(id < NSDraggingInfo >)sender
196 systemCocoa->handleDraggingEvent(GHOST_kEventDraggingExited, m_draggedObjectType, associatedWindow, 0, 0, nil);
197 m_draggedObjectType = GHOST_kDragnDropTypeUnknown;
200 - (BOOL)prepareForDragOperation:(id < NSDraggingInfo >)sender
202 if (associatedWindow->canAcceptDragOperation())
208 - (BOOL)performDragOperation:(id < NSDraggingInfo >)sender
210 NSPoint mouseLocation = [sender draggingLocation];
211 NSPasteboard *draggingPBoard = [sender draggingPasteboard];
215 switch (m_draggedObjectType) {
216 case GHOST_kDragnDropTypeBitmap:
217 if ([NSImage canInitWithPasteboard:draggingPBoard]) {
218 droppedImg = [[NSImage alloc]initWithPasteboard:draggingPBoard];
219 data = droppedImg; //[draggingPBoard dataForType:NSTIFFPboardType];
223 case GHOST_kDragnDropTypeFilenames:
224 data = [draggingPBoard propertyListForType:NSFilenamesPboardType];
226 case GHOST_kDragnDropTypeString:
227 data = [draggingPBoard stringForType:NSStringPboardType];
233 systemCocoa->handleDraggingEvent(GHOST_kEventDraggingDropDone, m_draggedObjectType, associatedWindow, mouseLocation.x, mouseLocation.y, (void*)data);
241 #pragma mark NSOpenGLView subclass
242 //We need to subclass it in order to give Cocoa the feeling key events are trapped
243 @interface CocoaOpenGLView : NSOpenGLView <NSTextInput>
245 GHOST_SystemCocoa *systemCocoa;
246 GHOST_WindowCocoa *associatedWindow;
249 NSString *composing_text;
251 - (void)setSystemAndWindowCocoa:(GHOST_SystemCocoa *)sysCocoa windowCocoa:(GHOST_WindowCocoa *)winCocoa;
253 @implementation CocoaOpenGLView
255 - (void)setSystemAndWindowCocoa:(GHOST_SystemCocoa *)sysCocoa windowCocoa:(GHOST_WindowCocoa *)winCocoa
257 systemCocoa = sysCocoa;
258 associatedWindow = winCocoa;
261 composing_text = nil;
264 - (BOOL)acceptsFirstResponder
269 // The trick to prevent Cocoa from complaining (beeping)
270 - (void)keyDown:(NSEvent *)event
272 systemCocoa->handleKeyEvent(event);
274 /* Start or continue composing? */
275 if ([[event characters] length] == 0 ||
276 [[event charactersIgnoringModifiers] length] == 0 ||
281 // interpret event to call insertText
282 NSMutableArray *events;
283 events = [[NSMutableArray alloc] initWithCapacity:1];
284 [events addObject:event];
285 [self interpretKeyEvents:events]; // calls insertText
286 [events removeObject:event];
292 - (void)keyUp:(NSEvent *)event
294 systemCocoa->handleKeyEvent(event);
297 - (void)flagsChanged:(NSEvent *)event
299 systemCocoa->handleKeyEvent(event);
302 - (void)mouseDown:(NSEvent *)event
304 systemCocoa->handleMouseEvent(event);
307 - (void)mouseUp:(NSEvent *)event
309 systemCocoa->handleMouseEvent(event);
312 - (void)rightMouseDown:(NSEvent *)event
314 systemCocoa->handleMouseEvent(event);
317 - (void)rightMouseUp:(NSEvent *)event
319 systemCocoa->handleMouseEvent(event);
322 - (void)mouseMoved:(NSEvent *)event
324 systemCocoa->handleMouseEvent(event);
327 - (void)mouseDragged:(NSEvent *)event
329 systemCocoa->handleMouseEvent(event);
332 - (void)rightMouseDragged:(NSEvent *)event
334 systemCocoa->handleMouseEvent(event);
337 - (void)scrollWheel:(NSEvent *)event
339 systemCocoa->handleMouseEvent(event);
342 - (void)otherMouseDown:(NSEvent *)event
344 systemCocoa->handleMouseEvent(event);
347 - (void)otherMouseUp:(NSEvent *)event
349 systemCocoa->handleMouseEvent(event);
352 - (void)otherMouseDragged:(NSEvent *)event
354 systemCocoa->handleMouseEvent(event);
357 - (void)magnifyWithEvent:(NSEvent *)event
359 systemCocoa->handleMouseEvent(event);
362 - (void)rotateWithEvent:(NSEvent *)event
364 systemCocoa->handleMouseEvent(event);
367 - (void)beginGestureWithEvent:(NSEvent *)event
369 systemCocoa->handleMouseEvent(event);
372 - (void)endGestureWithEvent:(NSEvent *)event
374 systemCocoa->handleMouseEvent(event);
377 - (void)tabletPoint:(NSEvent *)event
379 systemCocoa->handleTabletEvent(event,[event type]);
382 - (void)tabletProximity:(NSEvent *)event
384 systemCocoa->handleTabletEvent(event,[event type]);
392 - (void) drawRect:(NSRect)rect
394 if ([self inLiveResize]) {
395 /* Don't redraw while in live resize */
398 [super drawRect:rect];
399 systemCocoa->handleWindowEvent(GHOST_kEventWindowUpdate, associatedWindow);
405 - (void)composing_free
409 if (composing_text) {
410 [composing_text release];
411 composing_text = nil;
415 - (void)insertText:(id)chars
417 [self composing_free];
420 - (void)setMarkedText:(id)chars selectedRange:(NSRange)range
422 [self composing_free];
423 if ([chars length] == 0)
428 composing_text = [chars copy];
431 if ([composing_text length] == 0)
432 [self composing_free];
437 [self composing_free];
440 - (BOOL)hasMarkedText
442 return (composing) ? YES : NO;
445 - (void)doCommandBySelector:(SEL)selector
454 - (NSInteger)conversationIdentifier
456 return (NSInteger)self;
459 - (NSAttributedString *)attributedSubstringFromRange:(NSRange)range
461 return [NSAttributedString new]; // XXX does this leak?
464 - (NSRange)markedRange
466 unsigned int length = (composing_text) ? [composing_text length] : 0;
469 return NSMakeRange(0, length);
471 return NSMakeRange(NSNotFound, 0);
474 - (NSRange)selectedRange
476 unsigned int length = (composing_text) ? [composing_text length] : 0;
477 return NSMakeRange(0, length);
480 - (NSRect)firstRectForCharacterRange:(NSRange)range
485 - (NSUInteger)characterIndexForPoint:(NSPoint)point
490 - (NSArray*)validAttributesForMarkedText
492 return [NSArray array]; // XXX does this leak?
497 #pragma mark initialization / finalization
499 #if MAC_OS_X_VERSION_MAX_ALLOWED < 1070
500 @interface NSView (NSOpenGLSurfaceResolution)
501 - (BOOL)wantsBestResolutionOpenGLSurface;
502 - (void)setWantsBestResolutionOpenGLSurface:(BOOL)flag;
503 - (NSRect)convertRectToBacking:(NSRect)bounds;
507 NSOpenGLContext* GHOST_WindowCocoa::s_firstOpenGLcontext = nil;
509 GHOST_WindowCocoa::GHOST_WindowCocoa(
510 GHOST_SystemCocoa *systemCocoa,
511 const STR_String& title,
516 GHOST_TWindowState state,
517 GHOST_TDrawingContextType type,
518 const bool stereoVisual, const GHOST_TUns16 numOfAASamples
520 GHOST_Window(width, height, state, GHOST_kDrawingContextTypeNone, stereoVisual, false, numOfAASamples),
523 NSOpenGLPixelFormatAttribute pixelFormatAttrsWindow[40];
524 NSOpenGLPixelFormat *pixelFormat = nil;
527 m_systemCocoa = systemCocoa;
528 m_fullScreen = false;
530 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
536 rect.origin.x = left;
537 rect.origin.y = bottom;
538 rect.size.width = width;
539 rect.size.height = height;
541 m_window = [[CocoaWindow alloc] initWithContentRect:rect
542 styleMask:NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask | NSMiniaturizableWindowMask
543 backing:NSBackingStoreBuffered defer:NO];
544 if (m_window == nil) {
549 [m_window setSystemAndWindowCocoa:systemCocoa windowCocoa:this];
551 //Forbid to resize the window below the blender defined minimum one
553 minSize.height = 240;
554 [m_window setContentMinSize:minSize];
559 // Pixel Format Attributes for the windowed NSOpenGLContext
561 pixelFormatAttrsWindow[i++] = NSOpenGLPFADoubleBuffer;
563 // Guarantees the back buffer contents to be valid after a call to NSOpenGLContext object's flushBuffer
564 // needed for 'Draw Overlap' drawing method
565 pixelFormatAttrsWindow[i++] = NSOpenGLPFABackingStore;
567 // Force software OpenGL, for debugging
568 if (getenv("BLENDER_SOFTWAREGL")) {
569 pixelFormatAttrsWindow[i++] = NSOpenGLPFARendererID;
570 pixelFormatAttrsWindow[i++] = kCGLRendererGenericID;
573 pixelFormatAttrsWindow[i++] = NSOpenGLPFAAccelerated;
575 //pixelFormatAttrsWindow[i++] = NSOpenGLPFAAllowOfflineRenderers,; // Removed to allow 10.4 builds, and 2 GPUs rendering is not used anyway
577 pixelFormatAttrsWindow[i++] = NSOpenGLPFADepthSize;
578 pixelFormatAttrsWindow[i++] = (NSOpenGLPixelFormatAttribute) 32;
580 pixelFormatAttrsWindow[i++] = NSOpenGLPFAAccumSize;
581 pixelFormatAttrsWindow[i++] = (NSOpenGLPixelFormatAttribute) 32;
583 if (stereoVisual) pixelFormatAttrsWindow[i++] = NSOpenGLPFAStereo;
585 if (numOfAASamples > 0) {
586 // Multisample anti-aliasing
587 pixelFormatAttrsWindow[i++] = NSOpenGLPFAMultisample;
589 pixelFormatAttrsWindow[i++] = NSOpenGLPFASampleBuffers;
590 pixelFormatAttrsWindow[i++] = (NSOpenGLPixelFormatAttribute) 1;
592 pixelFormatAttrsWindow[i++] = NSOpenGLPFASamples;
593 pixelFormatAttrsWindow[i++] = (NSOpenGLPixelFormatAttribute) numOfAASamples;
595 pixelFormatAttrsWindow[i++] = NSOpenGLPFANoRecovery;
598 pixelFormatAttrsWindow[i] = (NSOpenGLPixelFormatAttribute) 0;
600 pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:pixelFormatAttrsWindow];
603 //Fall back to no multisampling if Antialiasing init failed
604 if (pixelFormat == nil) {
606 pixelFormatAttrsWindow[i++] = NSOpenGLPFADoubleBuffer;
608 // Guarantees the back buffer contents to be valid after a call to NSOpenGLContext object's flushBuffer
609 // needed for 'Draw Overlap' drawing method
610 pixelFormatAttrsWindow[i++] = NSOpenGLPFABackingStore;
612 // Force software OpenGL, for debugging
613 if (getenv("BLENDER_SOFTWAREGL")) {
614 pixelFormatAttrsWindow[i++] = NSOpenGLPFARendererID;
615 pixelFormatAttrsWindow[i++] = kCGLRendererGenericID;
618 pixelFormatAttrsWindow[i++] = NSOpenGLPFAAccelerated;
620 //pixelFormatAttrsWindow[i++] = NSOpenGLPFAAllowOfflineRenderers,; // Removed to allow 10.4 builds, and 2 GPUs rendering is not used anyway
622 pixelFormatAttrsWindow[i++] = NSOpenGLPFADepthSize;
623 pixelFormatAttrsWindow[i++] = (NSOpenGLPixelFormatAttribute) 32;
625 pixelFormatAttrsWindow[i++] = NSOpenGLPFAAccumSize;
626 pixelFormatAttrsWindow[i++] = (NSOpenGLPixelFormatAttribute) 32;
628 if (stereoVisual) pixelFormatAttrsWindow[i++] = NSOpenGLPFAStereo;
630 pixelFormatAttrsWindow[i] = (NSOpenGLPixelFormatAttribute) 0;
632 pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:pixelFormatAttrsWindow];
636 if (numOfAASamples > 0) { //Set m_numOfAASamples to the actual value
638 [pixelFormat getValues:&gli forAttribute:NSOpenGLPFASamples forVirtualScreen:0];
639 if (m_numOfAASamples != (GHOST_TUns16)gli) {
640 m_numOfAASamples = (GHOST_TUns16)gli;
641 printf("GHOST_Window could be created with anti-aliasing of only %i samples\n",m_numOfAASamples);
645 //Creates the OpenGL View inside the window
646 m_openGLView = [[CocoaOpenGLView alloc] initWithFrame:rect
647 pixelFormat:pixelFormat];
649 [m_openGLView setSystemAndWindowCocoa:systemCocoa windowCocoa:this];
651 [pixelFormat release];
653 m_openGLContext = [m_openGLView openGLContext]; //This context will be replaced by the proper one just after
655 [m_window setContentView:m_openGLView];
656 [m_window setInitialFirstResponder:m_openGLView];
658 [m_window makeKeyAndOrderFront:nil];
660 setDrawingContextType(type);
661 updateDrawingContext();
662 activateDrawingContext();
664 if (m_systemCocoa->m_nativePixel) {
665 if ([m_openGLView respondsToSelector:@selector(setWantsBestResolutionOpenGLSurface:)]) {
666 [m_openGLView setWantsBestResolutionOpenGLSurface:YES];
668 NSRect backingBounds = [m_openGLView convertRectToBacking:[m_openGLView bounds]];
669 m_nativePixelSize = (float)backingBounds.size.width / (float)rect.size.width;
673 m_tablet.Active = GHOST_kTabletModeNone;
675 CocoaWindowDelegate *windowDelegate = [[CocoaWindowDelegate alloc] init];
676 [windowDelegate setSystemAndWindowCocoa:systemCocoa windowCocoa:this];
677 [m_window setDelegate:windowDelegate];
679 [m_window setAcceptsMouseMovedEvents:YES];
681 #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
682 NSView *view = [m_window contentView];
683 [view setAcceptsTouchEvents:YES];
686 [m_window registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType,
687 NSStringPboardType, NSTIFFPboardType, nil]];
689 #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
690 if (state != GHOST_kWindowStateFullScreen) {
691 [m_window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
695 if (state == GHOST_kWindowStateFullScreen)
696 setState(GHOST_kWindowStateFullScreen);
702 GHOST_WindowCocoa::~GHOST_WindowCocoa()
704 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
706 if (m_customCursor) {
707 [m_customCursor release];
708 m_customCursor = nil;
711 [m_openGLView release];
717 // Check for other blender opened windows and make the frontmost key
718 // Note: for some reason the closed window is still in the list
719 NSArray *windowsList = [NSApp orderedWindows];
720 for (int a = 0; a < [windowsList count]; a++) {
721 if (m_window != (CocoaWindow *)[windowsList objectAtIndex:a]) {
722 [[windowsList objectAtIndex:a] makeKeyWindow];
731 #pragma mark accessors
733 bool GHOST_WindowCocoa::getValid() const
735 return (m_window != 0);
738 void* GHOST_WindowCocoa::getOSWindow() const
740 return (void*)m_window;
743 void GHOST_WindowCocoa::setTitle(const STR_String& title)
745 GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setTitle(): window invalid");
746 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
748 NSString *windowTitle = [[NSString alloc] initWithCString:title encoding:NSUTF8StringEncoding];
750 //Set associated file if applicable
751 if (windowTitle && [windowTitle hasPrefix:@"Blender"]) {
752 NSRange fileStrRange;
753 NSString *associatedFileName;
756 fileStrRange.location = [windowTitle rangeOfString:@"["].location+1;
757 len = [windowTitle rangeOfString:@"]"].location - fileStrRange.location;
760 fileStrRange.length = len;
761 associatedFileName = [windowTitle substringWithRange:fileStrRange];
762 [m_window setTitle:[associatedFileName lastPathComponent]];
764 //Blender used file open/save functions converte file names into legal URL ones
765 associatedFileName = [associatedFileName stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
767 [m_window setRepresentedFilename:associatedFileName];
769 @catch (NSException * e) {
770 printf("\nInvalid file path given in window title");
774 [m_window setTitle:windowTitle];
775 [m_window setRepresentedFilename:@""];
780 [m_window setTitle:windowTitle];
781 [m_window setRepresentedFilename:@""];
785 [windowTitle release];
790 void GHOST_WindowCocoa::getTitle(STR_String& title) const
792 GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getTitle(): window invalid");
794 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
796 NSString *windowTitle = [m_window title];
798 if (windowTitle != nil) {
799 title = [windowTitle UTF8String];
806 void GHOST_WindowCocoa::getWindowBounds(GHOST_Rect& bounds) const
809 GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getWindowBounds(): window invalid");
811 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
813 NSRect screenSize = [[m_window screen] visibleFrame];
815 rect = [m_window frame];
817 bounds.m_b = screenSize.size.height - (rect.origin.y -screenSize.origin.y);
818 bounds.m_l = rect.origin.x -screenSize.origin.x;
819 bounds.m_r = rect.origin.x-screenSize.origin.x + rect.size.width;
820 bounds.m_t = screenSize.size.height - (rect.origin.y + rect.size.height -screenSize.origin.y);
826 void GHOST_WindowCocoa::getClientBounds(GHOST_Rect& bounds) const
829 GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getClientBounds(): window invalid");
831 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
834 NSRect screenSize = [[m_window screen] visibleFrame];
836 //Max window contents as screen size (excluding title bar...)
837 NSRect contentRect = [CocoaWindow contentRectForFrameRect:screenSize
838 styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)];
840 rect = [m_window contentRectForFrameRect:[m_window frame]];
842 bounds.m_b = contentRect.size.height - (rect.origin.y -contentRect.origin.y);
843 bounds.m_l = rect.origin.x -contentRect.origin.x;
844 bounds.m_r = rect.origin.x-contentRect.origin.x + rect.size.width;
845 bounds.m_t = contentRect.size.height - (rect.origin.y + rect.size.height -contentRect.origin.y);
848 NSRect screenSize = [[m_window screen] frame];
850 bounds.m_b = screenSize.origin.y + screenSize.size.height;
851 bounds.m_l = screenSize.origin.x;
852 bounds.m_r = screenSize.origin.x + screenSize.size.width;
853 bounds.m_t = screenSize.origin.y;
859 GHOST_TSuccess GHOST_WindowCocoa::setClientWidth(GHOST_TUns32 width)
861 GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setClientWidth(): window invalid");
862 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
863 GHOST_Rect cBnds, wBnds;
864 getClientBounds(cBnds);
865 if (((GHOST_TUns32)cBnds.getWidth()) != width) {
868 size.height=cBnds.getHeight();
869 [m_window setContentSize:size];
872 return GHOST_kSuccess;
876 GHOST_TSuccess GHOST_WindowCocoa::setClientHeight(GHOST_TUns32 height)
878 GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setClientHeight(): window invalid");
879 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
880 GHOST_Rect cBnds, wBnds;
881 getClientBounds(cBnds);
882 if (((GHOST_TUns32)cBnds.getHeight()) != height) {
884 size.width=cBnds.getWidth();
886 [m_window setContentSize:size];
889 return GHOST_kSuccess;
893 GHOST_TSuccess GHOST_WindowCocoa::setClientSize(GHOST_TUns32 width, GHOST_TUns32 height)
895 GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setClientSize(): window invalid");
896 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
897 GHOST_Rect cBnds, wBnds;
898 getClientBounds(cBnds);
899 if ((((GHOST_TUns32)cBnds.getWidth()) != width) ||
900 (((GHOST_TUns32)cBnds.getHeight()) != height))
905 [m_window setContentSize:size];
908 return GHOST_kSuccess;
912 GHOST_TWindowState GHOST_WindowCocoa::getState() const
914 GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getState(): window invalid");
915 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
916 GHOST_TWindowState state;
918 #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
919 NSUInteger masks = [m_window styleMask];
921 if (masks & NSFullScreenWindowMask) {
922 // Lion style fullscreen
923 state = GHOST_kWindowStateFullScreen;
928 state = GHOST_kWindowStateFullScreen;
930 else if ([m_window isMiniaturized]) {
931 state = GHOST_kWindowStateMinimized;
933 else if ([m_window isZoomed]) {
934 state = GHOST_kWindowStateMaximized;
937 state = GHOST_kWindowStateNormal;
944 void GHOST_WindowCocoa::screenToClient(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
946 GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::screenToClient(): window invalid");
948 screenToClientIntern(inX, inY, outX, outY);
950 /* switch y to match ghost convention */
952 getClientBounds(cBnds);
953 outY = (cBnds.getHeight() - 1) - outY;
957 void GHOST_WindowCocoa::clientToScreen(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
959 GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::clientToScreen(): window invalid");
961 /* switch y to match ghost convention */
963 getClientBounds(cBnds);
964 inY = (cBnds.getHeight() - 1) - inY;
966 clientToScreenIntern(inX, inY, outX, outY);
969 void GHOST_WindowCocoa::screenToClientIntern(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
977 baseCoord = [m_window convertScreenToBase:screenCoord];
983 void GHOST_WindowCocoa::clientToScreenIntern(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
991 screenCoord = [m_window convertBaseToScreen:baseCoord];
993 outX = screenCoord.x;
994 outY = screenCoord.y;
998 NSScreen* GHOST_WindowCocoa::getScreen()
1000 return [m_window screen];
1003 /* called for event, when window leaves monitor to another */
1004 void GHOST_WindowCocoa::setNativePixelSize(void)
1006 /* make sure 10.6 keeps running */
1007 if ([m_openGLView respondsToSelector:@selector(setWantsBestResolutionOpenGLSurface:)]) {
1008 NSRect backingBounds = [m_openGLView convertRectToBacking:[m_openGLView bounds]];
1011 getClientBounds(rect);
1013 m_nativePixelSize = (float)backingBounds.size.width / (float)rect.getWidth();
1018 * \note Fullscreen switch is not actual fullscreen with display capture.
1019 * As this capture removes all OS X window manager features.
1021 * Instead, the menu bar and the dock are hidden, and the window is made borderless and enlarged.
1022 * Thus, process switch, exposé, spaces, ... still work in fullscreen mode
1024 GHOST_TSuccess GHOST_WindowCocoa::setState(GHOST_TWindowState state)
1026 GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setState(): window invalid");
1028 case GHOST_kWindowStateMinimized:
1029 [m_window miniaturize:nil];
1031 case GHOST_kWindowStateMaximized:
1032 [m_window zoom:nil];
1035 case GHOST_kWindowStateFullScreen:
1037 #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
1038 NSUInteger masks = [m_window styleMask];
1040 if (!m_fullScreen && !(masks & NSFullScreenWindowMask)) {
1042 if (!m_fullScreen) {
1044 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1046 /* This status change needs to be done before Cocoa call to enter fullscreen mode
1047 * to give window delegate hint not to forward its deactivation to ghost wm that
1048 * doesn't know view/window difference. */
1049 m_fullScreen = true;
1051 #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
1052 /* Disable toggle for Lion style fullscreen */
1053 [m_window setCollectionBehavior:NSWindowCollectionBehaviorDefault];
1056 #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
1057 //10.6 provides Cocoa functions to autoshow menu bar, and to change a window style
1058 //Hide menu & dock if on primary screen. else only menu
1059 if ([[m_window screen] isEqual:[[NSScreen screens] objectAtIndex:0]]) {
1060 [NSApp setPresentationOptions:(NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar)];
1062 //Make window borderless and enlarge it
1063 [m_window setStyleMask:NSBorderlessWindowMask];
1064 [m_window setFrame:[[m_window screen] frame] display:YES];
1065 [m_window makeFirstResponder:m_openGLView];
1067 //With 10.5, we need to create a new window to change its style to borderless
1068 //Hide menu & dock if needed
1069 if ([[m_window screen] isEqual:[[NSScreen screens] objectAtIndex:0]]) {
1070 //Cocoa function in 10.5 does not allow to set the menu bar in auto-show mode [NSMenu setMenuBarVisible:NO];
1071 //One of the very few 64bit compatible Carbon function
1072 SetSystemUIMode(kUIModeAllHidden,kUIOptionAutoShowMenuBar);
1074 //Create a fullscreen borderless window
1075 CocoaWindow *tmpWindow = [[CocoaWindow alloc]
1076 initWithContentRect:[[m_window screen] frame]
1077 styleMask:NSBorderlessWindowMask
1078 backing:NSBackingStoreBuffered
1080 //Copy current window parameters
1081 [tmpWindow setTitle:[m_window title]];
1082 [tmpWindow setRepresentedFilename:[m_window representedFilename]];
1083 [tmpWindow setAcceptsMouseMovedEvents:YES];
1084 [tmpWindow setDelegate:[m_window delegate]];
1085 [tmpWindow setSystemAndWindowCocoa:[m_window systemCocoa] windowCocoa:this];
1086 [tmpWindow registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType,
1087 NSStringPboardType, NSTIFFPboardType, nil]];
1089 //Assign the openGL view to the new window
1090 [tmpWindow setContentView:m_openGLView];
1092 //Show the new window
1093 [tmpWindow makeKeyAndOrderFront:m_openGLView];
1094 //Close and release old window
1096 m_window = tmpWindow;
1099 //Tell WM of view new size
1100 m_systemCocoa->handleWindowEvent(GHOST_kEventWindowSize, this);
1106 case GHOST_kWindowStateNormal:
1108 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1109 #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
1110 NSUInteger masks = [m_window styleMask];
1112 if (masks & NSFullScreenWindowMask) {
1113 // Lion style fullscreen
1114 [m_window toggleFullScreen:nil];
1119 m_fullScreen = false;
1121 #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
1122 /* Enable toggle for into Lion style fullscreen */
1123 [m_window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
1127 #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
1128 //Show again menu & dock if needed
1129 if ([[m_window screen] isEqual:[NSScreen mainScreen]]) {
1130 [NSApp setPresentationOptions:NSApplicationPresentationDefault];
1132 //Make window normal and resize it
1133 [m_window setStyleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)];
1134 [m_window setFrame:[[m_window screen] visibleFrame] display:YES];
1135 //TODO for 10.6 only : window title is forgotten after the style change
1136 [m_window makeFirstResponder:m_openGLView];
1138 //With 10.5, we need to create a new window to change its style to borderless
1139 //Show menu & dock if needed
1140 if ([[m_window screen] isEqual:[NSScreen mainScreen]]) {
1141 //Cocoa function in 10.5 does not allow to set the menu bar in auto-show mode [NSMenu setMenuBarVisible:YES];
1142 SetSystemUIMode(kUIModeNormal, 0); //One of the very few 64bit compatible Carbon function
1144 //Create a fullscreen borderless window
1145 CocoaWindow *tmpWindow = [[CocoaWindow alloc]
1146 initWithContentRect:[[m_window screen] frame]
1147 styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)
1148 backing:NSBackingStoreBuffered
1150 //Copy current window parameters
1151 [tmpWindow setTitle:[m_window title]];
1152 [tmpWindow setRepresentedFilename:[m_window representedFilename]];
1153 [tmpWindow setAcceptsMouseMovedEvents:YES];
1154 [tmpWindow setDelegate:[m_window delegate]];
1155 [tmpWindow setSystemAndWindowCocoa:[m_window systemCocoa] windowCocoa:this];
1156 [tmpWindow registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType,
1157 NSStringPboardType, NSTIFFPboardType, nil]];
1158 //Forbid to resize the window below the blender defined minimum one
1159 [tmpWindow setContentMinSize:NSMakeSize(320, 240)];
1161 //Assign the openGL view to the new window
1162 [tmpWindow setContentView:m_openGLView];
1164 //Show the new window
1165 [tmpWindow makeKeyAndOrderFront:nil];
1166 //Close and release old window
1168 m_window = tmpWindow;
1171 //Tell WM of view new size
1172 m_systemCocoa->handleWindowEvent(GHOST_kEventWindowSize, this);
1174 else if ([m_window isMiniaturized])
1175 [m_window deminiaturize:nil];
1176 else if ([m_window isZoomed])
1177 [m_window zoom:nil];
1182 return GHOST_kSuccess;
1185 GHOST_TSuccess GHOST_WindowCocoa::setModifiedState(bool isUnsavedChanges)
1187 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1189 [m_window setDocumentEdited:isUnsavedChanges];
1192 return GHOST_Window::setModifiedState(isUnsavedChanges);
1197 GHOST_TSuccess GHOST_WindowCocoa::setOrder(GHOST_TWindowOrder order)
1199 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1201 GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setOrder(): window invalid");
1202 if (order == GHOST_kWindowOrderTop) {
1203 [m_window makeKeyAndOrderFront:nil];
1206 NSArray *windowsList;
1208 [m_window orderBack:nil];
1210 //Check for other blender opened windows and make the frontmost key
1211 windowsList = [NSApp orderedWindows];
1212 if ([windowsList count]) {
1213 [[windowsList objectAtIndex:0] makeKeyAndOrderFront:nil];
1218 return GHOST_kSuccess;
1221 #pragma mark Drawing context
1223 /*#define WAIT_FOR_VSYNC 1*/
1225 GHOST_TSuccess GHOST_WindowCocoa::swapBuffers()
1227 if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) {
1228 if (m_openGLContext != nil) {
1229 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1230 [m_openGLContext flushBuffer];
1232 return GHOST_kSuccess;
1235 return GHOST_kFailure;
1238 GHOST_TSuccess GHOST_WindowCocoa::updateDrawingContext()
1240 if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) {
1241 if (m_openGLContext != nil) {
1242 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1243 [m_openGLContext update];
1245 return GHOST_kSuccess;
1248 return GHOST_kFailure;
1251 GHOST_TSuccess GHOST_WindowCocoa::activateDrawingContext()
1253 if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) {
1254 if (m_openGLContext != nil) {
1255 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1256 [m_openGLContext makeCurrentContext];
1258 // Disable AA by default
1259 if (m_numOfAASamples > 0) glDisable(GL_MULTISAMPLE_ARB);
1261 return GHOST_kSuccess;
1264 return GHOST_kFailure;
1268 GHOST_TSuccess GHOST_WindowCocoa::installDrawingContext(GHOST_TDrawingContextType type)
1270 GHOST_TSuccess success = GHOST_kFailure;
1272 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1274 NSOpenGLPixelFormat *pixelFormat;
1275 NSOpenGLContext *tmpOpenGLContext;
1277 /***** Multithreaded opengl code : uncomment for enabling
1278 CGLContextObj cglCtx;
1282 case GHOST_kDrawingContextTypeOpenGL:
1283 if (!getValid()) break;
1285 pixelFormat = [m_openGLView pixelFormat];
1286 tmpOpenGLContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat
1287 shareContext:s_firstOpenGLcontext];
1288 if (tmpOpenGLContext == nil) {
1289 success = GHOST_kFailure;
1293 //Switch openGL to multhreaded mode
1294 /******* Multithreaded opengl code : uncomment for enabling
1295 cglCtx = (CGLContextObj)[tmpOpenGLContext CGLContextObj];
1296 if (CGLEnable(cglCtx, kCGLCEMPEngine) == kCGLNoError)
1297 printf("\nSwitched openGL to multithreaded mode\n");
1300 if (!s_firstOpenGLcontext) s_firstOpenGLcontext = tmpOpenGLContext;
1301 #ifdef WAIT_FOR_VSYNC
1304 /* wait for vsync, to avoid tearing artifacts */
1305 [tmpOpenGLContext setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
1308 [m_openGLView setOpenGLContext:tmpOpenGLContext];
1309 [tmpOpenGLContext setView:m_openGLView];
1311 m_openGLContext = tmpOpenGLContext;
1314 case GHOST_kDrawingContextTypeNone:
1315 success = GHOST_kSuccess;
1326 GHOST_TSuccess GHOST_WindowCocoa::removeDrawingContext()
1328 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1329 switch (m_drawingContextType) {
1330 case GHOST_kDrawingContextTypeOpenGL:
1331 if (m_openGLContext) {
1332 [m_openGLView clearGLContext];
1333 if (s_firstOpenGLcontext == m_openGLContext) s_firstOpenGLcontext = nil;
1334 m_openGLContext = nil;
1337 return GHOST_kSuccess;
1338 case GHOST_kDrawingContextTypeNone:
1340 return GHOST_kSuccess;
1344 return GHOST_kFailure;
1349 GHOST_TSuccess GHOST_WindowCocoa::invalidate()
1351 GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::invalidate(): window invalid");
1352 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1353 [m_openGLView setNeedsDisplay:YES];
1355 return GHOST_kSuccess;
1358 #pragma mark Progress bar
1360 GHOST_TSuccess GHOST_WindowCocoa::setProgressBar(float progress)
1362 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1364 if ((progress >=0.0) && (progress <=1.0)) {
1365 NSImage* dockIcon = [[NSImage alloc] initWithSize:NSMakeSize(128,128)];
1367 [dockIcon lockFocus];
1368 NSRect progressBox = {{4, 4}, {120, 16}};
1370 [[NSImage imageNamed:@"NSApplicationIcon"] drawAtPoint:NSZeroPoint fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0];
1373 [[NSColor blackColor] setFill];
1374 NSRectFill(progressBox);
1376 [[NSColor whiteColor] set];
1377 NSFrameRect(progressBox);
1380 progressBox = NSInsetRect(progressBox, 1, 1);
1382 progressBox.size.width = progressBox.size.width * progress;
1383 NSGradient *gradient = [[NSGradient alloc] initWithStartingColor:[NSColor darkGrayColor] endingColor:[NSColor lightGrayColor]];
1384 [gradient drawInRect:progressBox angle:90];
1387 [dockIcon unlockFocus];
1389 [NSApp setApplicationIconImage:dockIcon];
1392 m_progressBarVisible = true;
1396 return GHOST_kSuccess;
1400 GHOST_TSuccess GHOST_WindowCocoa::endProgressBar()
1402 if (!m_progressBarVisible) return GHOST_kFailure;
1403 m_progressBarVisible = false;
1405 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1407 NSImage* dockIcon = [[NSImage alloc] initWithSize:NSMakeSize(128,128)];
1408 [dockIcon lockFocus];
1409 [[NSImage imageNamed:@"NSApplicationIcon"] drawAtPoint:NSZeroPoint fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0];
1410 [dockIcon unlockFocus];
1411 [NSApp setApplicationIconImage:dockIcon];
1415 return GHOST_kSuccess;
1420 #pragma mark Cursor handling
1422 void GHOST_WindowCocoa::loadCursor(bool visible, GHOST_TStandardCursor cursor) const
1424 static bool systemCursorVisible = true;
1426 NSCursor *tmpCursor =nil;
1428 if (visible != systemCursorVisible) {
1431 systemCursorVisible = true;
1435 systemCursorVisible = false;
1439 if (cursor == GHOST_kStandardCursorCustom && m_customCursor) {
1440 tmpCursor = m_customCursor;
1444 case GHOST_kStandardCursorDestroy:
1445 tmpCursor = [NSCursor disappearingItemCursor];
1447 case GHOST_kStandardCursorText:
1448 tmpCursor = [NSCursor IBeamCursor];
1450 case GHOST_kStandardCursorCrosshair:
1451 tmpCursor = [NSCursor crosshairCursor];
1453 case GHOST_kStandardCursorUpDown:
1454 tmpCursor = [NSCursor resizeUpDownCursor];
1456 case GHOST_kStandardCursorLeftRight:
1457 tmpCursor = [NSCursor resizeLeftRightCursor];
1459 case GHOST_kStandardCursorTopSide:
1460 tmpCursor = [NSCursor resizeUpCursor];
1462 case GHOST_kStandardCursorBottomSide:
1463 tmpCursor = [NSCursor resizeDownCursor];
1465 case GHOST_kStandardCursorLeftSide:
1466 tmpCursor = [NSCursor resizeLeftCursor];
1468 case GHOST_kStandardCursorRightSide:
1469 tmpCursor = [NSCursor resizeRightCursor];
1471 case GHOST_kStandardCursorRightArrow:
1472 case GHOST_kStandardCursorInfo:
1473 case GHOST_kStandardCursorLeftArrow:
1474 case GHOST_kStandardCursorHelp:
1475 case GHOST_kStandardCursorCycle:
1476 case GHOST_kStandardCursorSpray:
1477 case GHOST_kStandardCursorWait:
1478 case GHOST_kStandardCursorTopLeftCorner:
1479 case GHOST_kStandardCursorTopRightCorner:
1480 case GHOST_kStandardCursorBottomRightCorner:
1481 case GHOST_kStandardCursorBottomLeftCorner:
1482 case GHOST_kStandardCursorCopy:
1483 case GHOST_kStandardCursorDefault:
1485 tmpCursor = [NSCursor arrowCursor];
1494 GHOST_TSuccess GHOST_WindowCocoa::setWindowCursorVisibility(bool visible)
1496 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
1498 if ([m_window isVisible]) {
1499 loadCursor(visible, getCursorShape());
1503 return GHOST_kSuccess;
1507 GHOST_TSuccess GHOST_WindowCocoa::setWindowCursorGrab(GHOST_TGrabCursorMode mode)
1509 GHOST_TSuccess err = GHOST_kSuccess;
1511 if (mode != GHOST_kGrabDisable) {
1512 //No need to perform grab without warp as it is always on in OS X
1513 if (mode != GHOST_kGrabNormal) {
1514 GHOST_TInt32 x_old,y_old;
1515 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1517 m_systemCocoa->getCursorPosition(x_old,y_old);
1518 screenToClientIntern(x_old, y_old, m_cursorGrabInitPos[0], m_cursorGrabInitPos[1]);
1519 //Warp position is stored in client (window base) coordinates
1520 setCursorGrabAccum(0, 0);
1522 if (mode == GHOST_kGrabHide) {
1523 setWindowCursorVisibility(false);
1526 //Make window key if it wasn't to get the mouse move events
1527 [m_window makeKeyWindow];
1529 //Dissociate cursor position even for warp mode, to allow mouse acceleration to work even when warping the cursor
1530 err = CGAssociateMouseAndMouseCursorPosition(false) == kCGErrorSuccess ? GHOST_kSuccess : GHOST_kFailure;
1536 if (m_cursorGrab==GHOST_kGrabHide) {
1537 //No need to set again cursor position, as it has not changed for Cocoa
1538 setWindowCursorVisibility(true);
1541 err = CGAssociateMouseAndMouseCursorPosition(true) == kCGErrorSuccess ? GHOST_kSuccess : GHOST_kFailure;
1542 /* Almost works without but important otherwise the mouse GHOST location can be incorrect on exit */
1543 setCursorGrabAccum(0, 0);
1544 m_cursorGrabBounds.m_l= m_cursorGrabBounds.m_r= -1; /* disable */
1549 GHOST_TSuccess GHOST_WindowCocoa::setWindowCursorShape(GHOST_TStandardCursor shape)
1551 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1553 if (m_customCursor) {
1554 [m_customCursor release];
1555 m_customCursor = nil;
1558 if ([m_window isVisible]) {
1559 loadCursor(getCursorVisibility(), shape);
1563 return GHOST_kSuccess;
1566 /** Reverse the bits in a GHOST_TUns8
1567 static GHOST_TUns8 uns8ReverseBits(GHOST_TUns8 ch)
1569 ch= ((ch >> 1) & 0x55) | ((ch << 1) & 0xAA);
1570 ch= ((ch >> 2) & 0x33) | ((ch << 2) & 0xCC);
1571 ch= ((ch >> 4) & 0x0F) | ((ch << 4) & 0xF0);
1577 /** Reverse the bits in a GHOST_TUns16 */
1578 static GHOST_TUns16 uns16ReverseBits(GHOST_TUns16 shrt)
1580 shrt = ((shrt >> 1) & 0x5555) | ((shrt << 1) & 0xAAAA);
1581 shrt = ((shrt >> 2) & 0x3333) | ((shrt << 2) & 0xCCCC);
1582 shrt = ((shrt >> 4) & 0x0F0F) | ((shrt << 4) & 0xF0F0);
1583 shrt = ((shrt >> 8) & 0x00FF) | ((shrt << 8) & 0xFF00);
1587 GHOST_TSuccess GHOST_WindowCocoa::setWindowCustomCursorShape(GHOST_TUns8 *bitmap, GHOST_TUns8 *mask,
1588 int sizex, int sizey, int hotX, int hotY, int fg_color, int bg_color)
1591 NSPoint hotSpotPoint;
1592 NSBitmapImageRep *cursorImageRep;
1593 NSImage *cursorImage;
1595 GHOST_TUns16 *cursorBitmap;
1598 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1600 if (m_customCursor) {
1601 [m_customCursor release];
1602 m_customCursor = nil;
1606 cursorImageRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:nil
1613 colorSpaceName:NSDeviceWhiteColorSpace
1614 bytesPerRow:(sizex/8 + (sizex%8 >0 ?1:0))
1618 cursorBitmap = (GHOST_TUns16*)[cursorImageRep bitmapData];
1619 nbUns16 = [cursorImageRep bytesPerPlane]/2;
1621 for (y=0; y<nbUns16; y++) {
1622 #if !defined(__LITTLE_ENDIAN__)
1623 cursorBitmap[y] = ~uns16ReverseBits((bitmap[2*y]<<0) | (bitmap[2*y+1]<<8));
1624 cursorBitmap[nbUns16+y] = uns16ReverseBits((mask[2*y]<<0) | (mask[2*y+1]<<8));
1626 cursorBitmap[y] = ~uns16ReverseBits((bitmap[2*y+1]<<0) | (bitmap[2*y]<<8));
1627 cursorBitmap[nbUns16+y] = uns16ReverseBits((mask[2*y+1]<<0) | (mask[2*y]<<8));
1633 imSize.width = sizex;
1634 imSize.height= sizey;
1635 cursorImage = [[NSImage alloc] initWithSize:imSize];
1636 [cursorImage addRepresentation:cursorImageRep];
1638 hotSpotPoint.x = hotX;
1639 hotSpotPoint.y = hotY;
1641 //foreground and background color parameter is not handled for now (10.6)
1642 m_customCursor = [[NSCursor alloc] initWithImage:cursorImage
1643 hotSpot:hotSpotPoint];
1645 [cursorImageRep release];
1646 [cursorImage release];
1648 if ([m_window isVisible]) {
1649 loadCursor(getCursorVisibility(), GHOST_kStandardCursorCustom);
1652 return GHOST_kSuccess;
1655 GHOST_TSuccess GHOST_WindowCocoa::setWindowCustomCursorShape(GHOST_TUns8 bitmap[16][2],
1656 GHOST_TUns8 mask[16][2], int hotX, int hotY)
1658 return setWindowCustomCursorShape((GHOST_TUns8*)bitmap, (GHOST_TUns8*) mask, 16, 16, hotX, hotY, 0, 1);