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 <OpenGL/gl.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"
48 #pragma mark Cocoa window delegate object
49 /* live resize ugly patch
52 typedef struct bContext bContext;
54 extern int wm_window_timer(const bContext *C);
55 extern void wm_window_process_events(const bContext *C);
56 extern void wm_event_do_handlers(bContext *C);
57 extern void wm_event_do_notifiers(bContext *C);
58 extern void wm_draw_update(bContext *C);
60 @interface CocoaWindowDelegate : NSObject
61 #ifdef MAC_OS_X_VERSION_10_6
65 GHOST_SystemCocoa *systemCocoa;
66 GHOST_WindowCocoa *associatedWindow;
69 - (void)setSystemAndWindowCocoa:(GHOST_SystemCocoa *)sysCocoa windowCocoa:(GHOST_WindowCocoa *)winCocoa;
70 - (void)windowWillClose:(NSNotification *)notification;
71 - (void)windowDidBecomeKey:(NSNotification *)notification;
72 - (void)windowDidResignKey:(NSNotification *)notification;
73 - (void)windowDidExpose:(NSNotification *)notification;
74 - (void)windowDidResize:(NSNotification *)notification;
75 - (void)windowDidMove:(NSNotification *)notification;
76 - (void)windowWillMove:(NSNotification *)notification;
79 @implementation CocoaWindowDelegate : NSObject
80 - (void)setSystemAndWindowCocoa:(GHOST_SystemCocoa *)sysCocoa windowCocoa:(GHOST_WindowCocoa *)winCocoa
82 systemCocoa = sysCocoa;
83 associatedWindow = winCocoa;
86 - (void)windowWillClose:(NSNotification *)notification
88 systemCocoa->handleWindowEvent(GHOST_kEventWindowClose, associatedWindow);
91 - (void)windowDidBecomeKey:(NSNotification *)notification
93 systemCocoa->handleWindowEvent(GHOST_kEventWindowActivate, associatedWindow);
96 - (void)windowDidResignKey:(NSNotification *)notification
98 systemCocoa->handleWindowEvent(GHOST_kEventWindowDeactivate, associatedWindow);
101 - (void)windowDidExpose:(NSNotification *)notification
103 systemCocoa->handleWindowEvent(GHOST_kEventWindowUpdate, associatedWindow);
106 - (void)windowDidMove:(NSNotification *)notification
108 systemCocoa->handleWindowEvent(GHOST_kEventWindowMove, associatedWindow);
111 - (void)windowWillMove:(NSNotification *)notification
113 systemCocoa->handleWindowEvent(GHOST_kEventWindowMove, associatedWindow);
116 - (void)windowDidResize:(NSNotification *)notification
118 #ifdef MAC_OS_X_VERSION_10_6
119 //if (![[notification object] inLiveResize]) {
120 //Send event only once, at end of resize operation (when user has released mouse button)
122 systemCocoa->handleWindowEvent(GHOST_kEventWindowSize, associatedWindow);
123 #ifdef MAC_OS_X_VERSION_10_6
126 /* Live resize ugly patch. Needed because live resize runs in a modal loop, not letting main loop run
127 if ([[notification object] inLiveResize]) {
128 systemCocoa->dispatchEvents();
129 wm_window_timer(ghostC);
130 wm_event_do_handlers(ghostC);
131 wm_event_do_notifiers(ghostC);
132 wm_draw_update(ghostC);
137 #pragma mark NSWindow subclass
138 //We need to subclass it to tell that even borderless (fullscreen), it can become key (receive user events)
139 @interface CocoaWindow: NSWindow
141 GHOST_SystemCocoa *systemCocoa;
142 GHOST_WindowCocoa *associatedWindow;
143 GHOST_TDragnDropTypes m_draggedObjectType;
145 - (void)setSystemAndWindowCocoa:(GHOST_SystemCocoa *)sysCocoa windowCocoa:(GHOST_WindowCocoa *)winCocoa;
147 @implementation CocoaWindow
148 - (void)setSystemAndWindowCocoa:(GHOST_SystemCocoa *)sysCocoa windowCocoa:(GHOST_WindowCocoa *)winCocoa
150 systemCocoa = sysCocoa;
151 associatedWindow = winCocoa;
154 -(BOOL)canBecomeKeyWindow
159 //The drag'n'drop dragging destination methods
160 - (NSDragOperation)draggingEntered:(id < NSDraggingInfo >)sender
162 NSPoint mouseLocation = [sender draggingLocation];
163 NSPasteboard *draggingPBoard = [sender draggingPasteboard];
165 if ([[draggingPBoard types] containsObject:NSTIFFPboardType]) m_draggedObjectType = GHOST_kDragnDropTypeBitmap;
166 else if ([[draggingPBoard types] containsObject:NSFilenamesPboardType]) m_draggedObjectType = GHOST_kDragnDropTypeFilenames;
167 else if ([[draggingPBoard types] containsObject:NSStringPboardType]) m_draggedObjectType = GHOST_kDragnDropTypeString;
168 else return NSDragOperationNone;
170 associatedWindow->setAcceptDragOperation(FALSE); //Drag operation needs to be accepted explicitly by the event manager
171 systemCocoa->handleDraggingEvent(GHOST_kEventDraggingEntered, m_draggedObjectType, associatedWindow, mouseLocation.x, mouseLocation.y, nil);
172 return NSDragOperationCopy;
175 - (BOOL)wantsPeriodicDraggingUpdates
177 return NO; //No need to overflow blender event queue. Events shall be sent only on changes
180 - (NSDragOperation)draggingUpdated:(id < NSDraggingInfo >)sender
182 NSPoint mouseLocation = [sender draggingLocation];
184 systemCocoa->handleDraggingEvent(GHOST_kEventDraggingUpdated, m_draggedObjectType, associatedWindow, mouseLocation.x, mouseLocation.y, nil);
185 return NSDragOperationCopy;
188 - (void)draggingExited:(id < NSDraggingInfo >)sender
190 systemCocoa->handleDraggingEvent(GHOST_kEventDraggingExited, m_draggedObjectType, associatedWindow, 0, 0, nil);
191 m_draggedObjectType = GHOST_kDragnDropTypeUnknown;
194 - (BOOL)prepareForDragOperation:(id < NSDraggingInfo >)sender
196 if (associatedWindow->canAcceptDragOperation())
202 - (BOOL)performDragOperation:(id < NSDraggingInfo >)sender
204 NSPoint mouseLocation = [sender draggingLocation];
205 NSPasteboard *draggingPBoard = [sender draggingPasteboard];
208 switch (m_draggedObjectType) {
209 case GHOST_kDragnDropTypeBitmap:
210 data = [draggingPBoard dataForType:NSTIFFPboardType];
212 case GHOST_kDragnDropTypeFilenames:
213 data = [draggingPBoard propertyListForType:NSFilenamesPboardType];
215 case GHOST_kDragnDropTypeString:
216 data = [draggingPBoard stringForType:NSStringPboardType];
222 systemCocoa->handleDraggingEvent(GHOST_kEventDraggingDropDone, m_draggedObjectType, associatedWindow, mouseLocation.x, mouseLocation.y, (void*)data);
230 #pragma mark NSOpenGLView subclass
231 //We need to subclass it in order to give Cocoa the feeling key events are trapped
232 @interface CocoaOpenGLView : NSOpenGLView
236 @implementation CocoaOpenGLView
238 - (BOOL)acceptsFirstResponder
243 //The trick to prevent Cocoa from complaining (beeping)
244 - (void)keyDown:(NSEvent *)theEvent
247 #if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4
248 //Cmd+key are handled differently before 10.5
249 - (BOOL)performKeyEquivalent:(NSEvent *)theEvent
251 NSString *chars = [theEvent charactersIgnoringModifiers];
253 if ([chars length] <1)
256 //Let cocoa handle menu shortcuts
257 switch ([chars characterAtIndex:0]) {
281 #pragma mark initialization / finalization
283 NSOpenGLContext* GHOST_WindowCocoa::s_firstOpenGLcontext = nil;
285 GHOST_WindowCocoa::GHOST_WindowCocoa(
286 GHOST_SystemCocoa *systemCocoa,
287 const STR_String& title,
292 GHOST_TWindowState state,
293 GHOST_TDrawingContextType type,
294 const bool stereoVisual, const GHOST_TUns16 numOfAASamples
296 GHOST_Window(title, left, top, width, height, state, GHOST_kDrawingContextTypeNone, stereoVisual, numOfAASamples),
299 NSOpenGLPixelFormatAttribute pixelFormatAttrsWindow[40];
300 NSOpenGLPixelFormat *pixelFormat = nil;
303 m_systemCocoa = systemCocoa;
304 m_fullScreen = false;
306 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
313 rect.origin.x = left;
315 rect.size.width = width;
316 rect.size.height = height;
318 m_window = [[CocoaWindow alloc] initWithContentRect:rect
319 styleMask:NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask | NSMiniaturizableWindowMask
320 backing:NSBackingStoreBuffered defer:NO];
321 if (m_window == nil) {
326 [m_window setSystemAndWindowCocoa:systemCocoa windowCocoa:this];
328 //Forbid to resize the window below the blender defined minimum one
330 minSize.height = 240;
331 [m_window setContentMinSize:minSize];
336 // Pixel Format Attributes for the windowed NSOpenGLContext
338 pixelFormatAttrsWindow[i++] = NSOpenGLPFADoubleBuffer;
339 pixelFormatAttrsWindow[i++] = NSOpenGLPFAAccelerated;
340 //pixelFormatAttrsWindow[i++] = NSOpenGLPFAAllowOfflineRenderers,; // Removed to allow 10.4 builds, and 2 GPUs rendering is not used anyway
342 pixelFormatAttrsWindow[i++] = NSOpenGLPFADepthSize;
343 pixelFormatAttrsWindow[i++] = (NSOpenGLPixelFormatAttribute) 32;
345 if (stereoVisual) pixelFormatAttrsWindow[i++] = NSOpenGLPFAStereo;
347 if (numOfAASamples>0) {
348 // Multisample anti-aliasing
349 pixelFormatAttrsWindow[i++] = NSOpenGLPFAMultisample;
351 pixelFormatAttrsWindow[i++] = NSOpenGLPFASampleBuffers;
352 pixelFormatAttrsWindow[i++] = (NSOpenGLPixelFormatAttribute) 1;
354 pixelFormatAttrsWindow[i++] = NSOpenGLPFASamples;
355 pixelFormatAttrsWindow[i++] = (NSOpenGLPixelFormatAttribute) numOfAASamples;
357 pixelFormatAttrsWindow[i++] = NSOpenGLPFANoRecovery;
360 pixelFormatAttrsWindow[i] = (NSOpenGLPixelFormatAttribute) 0;
362 pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:pixelFormatAttrsWindow];
365 //Fall back to no multisampling if Antialiasing init failed
366 if (pixelFormat == nil) {
368 pixelFormatAttrsWindow[i++] = NSOpenGLPFADoubleBuffer;
369 pixelFormatAttrsWindow[i++] = NSOpenGLPFAAccelerated;
370 //pixelFormatAttrsWindow[i++] = NSOpenGLPFAAllowOfflineRenderers,; // Removed to allow 10.4 builds, and 2 GPUs rendering is not used anyway
372 pixelFormatAttrsWindow[i++] = NSOpenGLPFADepthSize;
373 pixelFormatAttrsWindow[i++] = (NSOpenGLPixelFormatAttribute) 32;
375 if (stereoVisual) pixelFormatAttrsWindow[i++] = NSOpenGLPFAStereo;
377 pixelFormatAttrsWindow[i] = (NSOpenGLPixelFormatAttribute) 0;
379 pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:pixelFormatAttrsWindow];
383 if (numOfAASamples>0) { //Set m_numOfAASamples to the actual value
385 [pixelFormat getValues:&gli forAttribute:NSOpenGLPFASamples forVirtualScreen:0];
386 if (m_numOfAASamples != (GHOST_TUns16)gli) {
387 m_numOfAASamples = (GHOST_TUns16)gli;
388 printf("GHOST_Window could be created with anti-aliasing of only %i samples\n",m_numOfAASamples);
392 //Creates the OpenGL View inside the window
393 m_openGLView = [[CocoaOpenGLView alloc] initWithFrame:rect
394 pixelFormat:pixelFormat];
396 [pixelFormat release];
398 m_openGLContext = [m_openGLView openGLContext]; //This context will be replaced by the proper one just after
400 [m_window setContentView:m_openGLView];
401 [m_window setInitialFirstResponder:m_openGLView];
403 [m_window setReleasedWhenClosed:NO]; //To avoid bad pointer exception in case of user closing the window
405 [m_window makeKeyAndOrderFront:nil];
407 setDrawingContextType(type);
408 updateDrawingContext();
409 activateDrawingContext();
411 m_tablet.Active = GHOST_kTabletModeNone;
413 CocoaWindowDelegate *windowDelegate = [[CocoaWindowDelegate alloc] init];
414 [windowDelegate setSystemAndWindowCocoa:systemCocoa windowCocoa:this];
415 [m_window setDelegate:windowDelegate];
417 [m_window setAcceptsMouseMovedEvents:YES];
419 [m_window registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType,
420 NSStringPboardType, NSTIFFPboardType, nil]];
422 if (state == GHOST_kWindowStateFullScreen)
423 setState(GHOST_kWindowStateFullScreen);
429 GHOST_WindowCocoa::~GHOST_WindowCocoa()
431 if (m_customCursor) delete m_customCursor;
433 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
434 [m_openGLView release];
438 [[m_window delegate] release];
443 //Check for other blender opened windows and make the frontmost key
444 NSArray *windowsList = [NSApp orderedWindows];
445 if ([windowsList count]) {
446 [[windowsList objectAtIndex:0] makeKeyAndOrderFront:nil];
451 #pragma mark accessors
453 bool GHOST_WindowCocoa::getValid() const
455 return (m_window != 0);
459 void GHOST_WindowCocoa::setTitle(const STR_String& title)
461 GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setTitle(): window invalid")
462 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
464 NSString *windowTitle = [[NSString alloc] initWithUTF8String:title];
466 //Set associated file if applicable
467 if ([windowTitle hasPrefix:@"Blender"])
469 NSRange fileStrRange;
470 NSString *associatedFileName;
473 fileStrRange.location = [windowTitle rangeOfString:@"["].location+1;
474 len = [windowTitle rangeOfString:@"]"].location - fileStrRange.location;
478 fileStrRange.length = len;
479 associatedFileName = [windowTitle substringWithRange:fileStrRange];
481 [m_window setRepresentedFilename:associatedFileName];
483 @catch (NSException * e) {
484 printf("\nInvalid file path given in window title");
486 [m_window setTitle:[associatedFileName lastPathComponent]];
489 [m_window setTitle:windowTitle];
490 [m_window setRepresentedFilename:@""];
494 [m_window setTitle:windowTitle];
495 [m_window setRepresentedFilename:@""];
499 [windowTitle release];
504 void GHOST_WindowCocoa::getTitle(STR_String& title) const
506 GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getTitle(): window invalid")
508 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
510 NSString *windowTitle = [m_window title];
512 if (windowTitle != nil) {
513 title = [windowTitle UTF8String];
520 void GHOST_WindowCocoa::getWindowBounds(GHOST_Rect& bounds) const
523 GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getWindowBounds(): window invalid")
525 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
527 NSRect screenSize = [[m_window screen] visibleFrame];
529 rect = [m_window frame];
531 bounds.m_b = screenSize.size.height - (rect.origin.y -screenSize.origin.y);
532 bounds.m_l = rect.origin.x -screenSize.origin.x;
533 bounds.m_r = rect.origin.x-screenSize.origin.x + rect.size.width;
534 bounds.m_t = screenSize.size.height - (rect.origin.y + rect.size.height -screenSize.origin.y);
540 void GHOST_WindowCocoa::getClientBounds(GHOST_Rect& bounds) const
543 GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getClientBounds(): window invalid")
545 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
549 NSRect screenSize = [[m_window screen] visibleFrame];
551 //Max window contents as screen size (excluding title bar...)
552 NSRect contentRect = [CocoaWindow contentRectForFrameRect:screenSize
553 styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)];
555 rect = [m_window contentRectForFrameRect:[m_window frame]];
557 bounds.m_b = contentRect.size.height - (rect.origin.y -contentRect.origin.y);
558 bounds.m_l = rect.origin.x -contentRect.origin.x;
559 bounds.m_r = rect.origin.x-contentRect.origin.x + rect.size.width;
560 bounds.m_t = contentRect.size.height - (rect.origin.y + rect.size.height -contentRect.origin.y);
563 NSRect screenSize = [[m_window screen] frame];
565 bounds.m_b = screenSize.origin.y + screenSize.size.height;
566 bounds.m_l = screenSize.origin.x;
567 bounds.m_r = screenSize.origin.x + screenSize.size.width;
568 bounds.m_t = screenSize.origin.y;
574 GHOST_TSuccess GHOST_WindowCocoa::setClientWidth(GHOST_TUns32 width)
576 GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setClientWidth(): window invalid")
577 GHOST_Rect cBnds, wBnds;
578 getClientBounds(cBnds);
579 if (((GHOST_TUns32)cBnds.getWidth()) != width) {
582 size.height=cBnds.getHeight();
583 [m_window setContentSize:size];
585 return GHOST_kSuccess;
589 GHOST_TSuccess GHOST_WindowCocoa::setClientHeight(GHOST_TUns32 height)
591 GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setClientHeight(): window invalid")
592 GHOST_Rect cBnds, wBnds;
593 getClientBounds(cBnds);
594 if (((GHOST_TUns32)cBnds.getHeight()) != height) {
596 size.width=cBnds.getWidth();
598 [m_window setContentSize:size];
600 return GHOST_kSuccess;
604 GHOST_TSuccess GHOST_WindowCocoa::setClientSize(GHOST_TUns32 width, GHOST_TUns32 height)
606 GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setClientSize(): window invalid")
607 GHOST_Rect cBnds, wBnds;
608 getClientBounds(cBnds);
609 if ((((GHOST_TUns32)cBnds.getWidth()) != width) ||
610 (((GHOST_TUns32)cBnds.getHeight()) != height)) {
614 [m_window setContentSize:size];
616 return GHOST_kSuccess;
620 GHOST_TWindowState GHOST_WindowCocoa::getState() const
622 GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getState(): window invalid")
623 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
624 GHOST_TWindowState state;
626 state = GHOST_kWindowStateFullScreen;
628 else if ([m_window isMiniaturized]) {
629 state = GHOST_kWindowStateMinimized;
631 else if ([m_window isZoomed]) {
632 state = GHOST_kWindowStateMaximized;
635 state = GHOST_kWindowStateNormal;
642 void GHOST_WindowCocoa::screenToClient(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
644 GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::screenToClient(): window invalid")
652 baseCoord = [m_window convertScreenToBase:screenCoord];
659 void GHOST_WindowCocoa::clientToScreen(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
661 GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::clientToScreen(): window invalid")
669 screenCoord = [m_window convertBaseToScreen:baseCoord];
671 outX = screenCoord.x;
672 outY = screenCoord.y;
676 NSScreen* GHOST_WindowCocoa::getScreen()
678 return [m_window screen];
683 * @note Fullscreen switch is not actual fullscreen with display capture. As this capture removes all OS X window manager features.
684 * Instead, the menu bar and the dock are hidden, and the window is made borderless and enlarged.
685 * Thus, process switch, exposé, spaces, ... still work in fullscreen mode
687 GHOST_TSuccess GHOST_WindowCocoa::setState(GHOST_TWindowState state)
689 GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setState(): window invalid")
691 case GHOST_kWindowStateMinimized:
692 [m_window miniaturize:nil];
694 case GHOST_kWindowStateMaximized:
698 case GHOST_kWindowStateFullScreen:
701 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
703 //This status change needs to be done before Cocoa call to enter fullscreen mode
704 //to give window delegate hint not to forward its deactivation to ghost wm that doesn't know view/window difference
707 #ifdef MAC_OS_X_VERSION_10_6
708 //10.6 provides Cocoa functions to autoshow menu bar, and to change a window style
709 //Hide menu & dock if needed
710 if ([[m_window screen] isEqual:[NSScreen mainScreen]])
712 [NSApp setPresentationOptions:(NSApplicationPresentationHideDock | NSApplicationPresentationAutoHideMenuBar)];
714 //Make window borderless and enlarge it
715 [m_window setStyleMask:NSBorderlessWindowMask];
716 [m_window setFrame:[[m_window screen] frame] display:YES];
717 [m_window makeFirstResponder:m_openGLView];
719 //With 10.5, we need to create a new window to change its style to borderless
720 //Hide menu & dock if needed
721 if ([[m_window screen] isEqual:[NSScreen mainScreen]])
723 //Cocoa function in 10.5 does not allow to set the menu bar in auto-show mode [NSMenu setMenuBarVisible:NO];
724 //One of the very few 64bit compatible Carbon function
725 SetSystemUIMode(kUIModeAllHidden,kUIOptionAutoShowMenuBar);
727 //Create a fullscreen borderless window
728 CocoaWindow *tmpWindow = [[CocoaWindow alloc]
729 initWithContentRect:[[m_window screen] frame]
730 styleMask:NSBorderlessWindowMask
731 backing:NSBackingStoreBuffered
733 //Copy current window parameters
734 [tmpWindow setTitle:[m_window title]];
735 [tmpWindow setRepresentedFilename:[m_window representedFilename]];
736 [tmpWindow setReleasedWhenClosed:NO];
737 [tmpWindow setAcceptsMouseMovedEvents:YES];
738 [tmpWindow setDelegate:[m_window delegate]];
740 //Assign the openGL view to the new window
741 [tmpWindow setContentView:m_openGLView];
743 //Show the new window
744 [tmpWindow makeKeyAndOrderFront:nil];
745 //Close and release old window
746 [m_window setDelegate:nil]; // To avoid the notification of "window closed" event
749 m_window = tmpWindow;
752 //Tell WM of view new size
753 m_systemCocoa->handleWindowEvent(GHOST_kEventWindowSize, this);
758 case GHOST_kWindowStateNormal:
762 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
763 m_fullScreen = false;
766 #ifdef MAC_OS_X_VERSION_10_6
767 //Show again menu & dock if needed
768 if ([[m_window screen] isEqual:[NSScreen mainScreen]])
770 [NSApp setPresentationOptions:NSApplicationPresentationDefault];
772 //Make window normal and resize it
773 [m_window setStyleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)];
774 [m_window setFrame:[[m_window screen] visibleFrame] display:YES];
775 //TODO for 10.6 only : window title is forgotten after the style change
776 [m_window makeFirstResponder:m_openGLView];
778 //With 10.5, we need to create a new window to change its style to borderless
779 //Show menu & dock if needed
780 if ([[m_window screen] isEqual:[NSScreen mainScreen]])
782 //Cocoa function in 10.5 does not allow to set the menu bar in auto-show mode [NSMenu setMenuBarVisible:YES];
783 SetSystemUIMode(kUIModeNormal, 0); //One of the very few 64bit compatible Carbon function
785 //Create a fullscreen borderless window
786 CocoaWindow *tmpWindow = [[CocoaWindow alloc]
787 initWithContentRect:[[m_window screen] frame]
788 styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)
789 backing:NSBackingStoreBuffered
791 //Copy current window parameters
792 [tmpWindow setTitle:[m_window title]];
793 [tmpWindow setRepresentedFilename:[m_window representedFilename]];
794 [tmpWindow setReleasedWhenClosed:NO];
795 [tmpWindow setAcceptsMouseMovedEvents:YES];
796 [tmpWindow setDelegate:[m_window delegate]];
798 //Assign the openGL view to the new window
799 [tmpWindow setContentView:m_openGLView];
801 //Show the new window
802 [tmpWindow makeKeyAndOrderFront:nil];
803 //Close and release old window
804 [m_window setDelegate:nil]; // To avoid the notification of "window closed" event
807 m_window = tmpWindow;
810 //Tell WM of view new size
811 m_systemCocoa->handleWindowEvent(GHOST_kEventWindowSize, this);
815 else if ([m_window isMiniaturized])
816 [m_window deminiaturize:nil];
817 else if ([m_window isZoomed])
821 return GHOST_kSuccess;
824 GHOST_TSuccess GHOST_WindowCocoa::setModifiedState(bool isUnsavedChanges)
826 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
828 [m_window setDocumentEdited:isUnsavedChanges];
831 return GHOST_Window::setModifiedState(isUnsavedChanges);
836 GHOST_TSuccess GHOST_WindowCocoa::setOrder(GHOST_TWindowOrder order)
838 GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setOrder(): window invalid")
839 if (order == GHOST_kWindowOrderTop) {
840 [m_window makeKeyAndOrderFront:nil];
843 NSArray *windowsList;
845 [m_window orderBack:nil];
847 //Check for other blender opened windows and make the frontmost key
848 windowsList = [NSApp orderedWindows];
849 if ([windowsList count]) {
850 [[windowsList objectAtIndex:0] makeKeyAndOrderFront:nil];
853 return GHOST_kSuccess;
856 #pragma mark Drawing context
858 /*#define WAIT_FOR_VSYNC 1*/
860 GHOST_TSuccess GHOST_WindowCocoa::swapBuffers()
862 if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) {
863 if (m_openGLContext != nil) {
864 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
865 [m_openGLContext flushBuffer];
867 return GHOST_kSuccess;
870 return GHOST_kFailure;
873 GHOST_TSuccess GHOST_WindowCocoa::updateDrawingContext()
875 if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) {
876 if (m_openGLContext != nil) {
877 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
878 [m_openGLContext update];
880 return GHOST_kSuccess;
883 return GHOST_kFailure;
886 GHOST_TSuccess GHOST_WindowCocoa::activateDrawingContext()
888 if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) {
889 if (m_openGLContext != nil) {
890 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
891 [m_openGLContext makeCurrentContext];
893 // Disable AA by default
894 if (m_numOfAASamples > 0) glDisable(GL_MULTISAMPLE_ARB);
896 return GHOST_kSuccess;
899 return GHOST_kFailure;
903 GHOST_TSuccess GHOST_WindowCocoa::installDrawingContext(GHOST_TDrawingContextType type)
905 GHOST_TSuccess success = GHOST_kFailure;
907 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
909 NSOpenGLPixelFormat *pixelFormat;
910 NSOpenGLContext *tmpOpenGLContext;
912 /***** Multithreaded opengl code : uncomment for enabling
913 CGLContextObj cglCtx;
917 case GHOST_kDrawingContextTypeOpenGL:
918 if (!getValid()) break;
920 pixelFormat = [m_openGLView pixelFormat];
921 tmpOpenGLContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat
922 shareContext:s_firstOpenGLcontext];
923 if (tmpOpenGLContext == nil) {
924 success = GHOST_kFailure;
928 //Switch openGL to multhreaded mode
929 /******* Multithreaded opengl code : uncomment for enabling
930 cglCtx = (CGLContextObj)[tmpOpenGLContext CGLContextObj];
931 if (CGLEnable(cglCtx, kCGLCEMPEngine) == kCGLNoError)
932 printf("\nSwitched openGL to multithreaded mode");
935 if (!s_firstOpenGLcontext) s_firstOpenGLcontext = tmpOpenGLContext;
936 #ifdef WAIT_FOR_VSYNC
939 /* wait for vsync, to avoid tearing artifacts */
940 [tmpOpenGLContext setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
943 [m_openGLView setOpenGLContext:tmpOpenGLContext];
944 [tmpOpenGLContext setView:m_openGLView];
946 m_openGLContext = tmpOpenGLContext;
949 case GHOST_kDrawingContextTypeNone:
950 success = GHOST_kSuccess;
961 GHOST_TSuccess GHOST_WindowCocoa::removeDrawingContext()
963 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
964 switch (m_drawingContextType) {
965 case GHOST_kDrawingContextTypeOpenGL:
968 [m_openGLView clearGLContext];
969 if (s_firstOpenGLcontext == m_openGLContext) s_firstOpenGLcontext = nil;
970 m_openGLContext = nil;
973 return GHOST_kSuccess;
974 case GHOST_kDrawingContextTypeNone:
976 return GHOST_kSuccess;
980 return GHOST_kFailure;
985 GHOST_TSuccess GHOST_WindowCocoa::invalidate()
987 GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::invalidate(): window invalid")
988 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
989 [m_openGLView setNeedsDisplay:YES];
991 return GHOST_kSuccess;
994 #pragma mark Cursor handling
996 void GHOST_WindowCocoa::loadCursor(bool visible, GHOST_TStandardCursor cursor) const
998 static bool systemCursorVisible = true;
1000 NSAutoreleasePool *pool =[[NSAutoreleasePool alloc] init];
1002 NSCursor *tmpCursor =nil;
1004 if (visible != systemCursorVisible) {
1007 systemCursorVisible = true;
1011 systemCursorVisible = false;
1015 if (cursor == GHOST_kStandardCursorCustom && m_customCursor) {
1016 tmpCursor = m_customCursor;
1019 case GHOST_kStandardCursorDestroy:
1020 tmpCursor = [NSCursor disappearingItemCursor];
1022 case GHOST_kStandardCursorText:
1023 tmpCursor = [NSCursor IBeamCursor];
1025 case GHOST_kStandardCursorCrosshair:
1026 tmpCursor = [NSCursor crosshairCursor];
1028 case GHOST_kStandardCursorUpDown:
1029 tmpCursor = [NSCursor resizeUpDownCursor];
1031 case GHOST_kStandardCursorLeftRight:
1032 tmpCursor = [NSCursor resizeLeftRightCursor];
1034 case GHOST_kStandardCursorTopSide:
1035 tmpCursor = [NSCursor resizeUpCursor];
1037 case GHOST_kStandardCursorBottomSide:
1038 tmpCursor = [NSCursor resizeDownCursor];
1040 case GHOST_kStandardCursorLeftSide:
1041 tmpCursor = [NSCursor resizeLeftCursor];
1043 case GHOST_kStandardCursorRightSide:
1044 tmpCursor = [NSCursor resizeRightCursor];
1046 case GHOST_kStandardCursorRightArrow:
1047 case GHOST_kStandardCursorInfo:
1048 case GHOST_kStandardCursorLeftArrow:
1049 case GHOST_kStandardCursorHelp:
1050 case GHOST_kStandardCursorCycle:
1051 case GHOST_kStandardCursorSpray:
1052 case GHOST_kStandardCursorWait:
1053 case GHOST_kStandardCursorTopLeftCorner:
1054 case GHOST_kStandardCursorTopRightCorner:
1055 case GHOST_kStandardCursorBottomRightCorner:
1056 case GHOST_kStandardCursorBottomLeftCorner:
1057 case GHOST_kStandardCursorCopy:
1058 case GHOST_kStandardCursorDefault:
1060 tmpCursor = [NSCursor arrowCursor];
1070 GHOST_TSuccess GHOST_WindowCocoa::setWindowCursorVisibility(bool visible)
1072 if ([m_window isVisible]) {
1073 loadCursor(visible, getCursorShape());
1076 return GHOST_kSuccess;
1080 GHOST_TSuccess GHOST_WindowCocoa::setWindowCursorGrab(GHOST_TGrabCursorMode mode)
1082 GHOST_TSuccess err = GHOST_kSuccess;
1084 if (mode != GHOST_kGrabDisable)
1086 //No need to perform grab without warp as it is always on in OS X
1087 if(mode != GHOST_kGrabNormal) {
1088 GHOST_TInt32 x_old,y_old;
1090 m_systemCocoa->getCursorPosition(x_old,y_old);
1091 screenToClient(x_old, y_old, m_cursorGrabInitPos[0], m_cursorGrabInitPos[1]);
1092 //Warp position is stored in client (window base) coordinates
1093 setCursorGrabAccum(0, 0);
1095 if(mode == GHOST_kGrabHide) {
1096 setWindowCursorVisibility(false);
1099 //Dissociate cursor position even for warp mode, to allow mouse acceleration to work even when warping the cursor
1100 err = CGAssociateMouseAndMouseCursorPosition(false) == kCGErrorSuccess ? GHOST_kSuccess : GHOST_kFailure;
1104 if(m_cursorGrab==GHOST_kGrabHide)
1106 //No need to set again cursor position, as it has not changed for Cocoa
1107 setWindowCursorVisibility(true);
1110 err = CGAssociateMouseAndMouseCursorPosition(true) == kCGErrorSuccess ? GHOST_kSuccess : GHOST_kFailure;
1111 /* Almost works without but important otherwise the mouse GHOST location can be incorrect on exit */
1112 setCursorGrabAccum(0, 0);
1113 m_cursorGrabBounds.m_l= m_cursorGrabBounds.m_r= -1; /* disable */
1118 GHOST_TSuccess GHOST_WindowCocoa::setWindowCursorShape(GHOST_TStandardCursor shape)
1120 if (m_customCursor) {
1121 [m_customCursor release];
1122 m_customCursor = nil;
1125 if ([m_window isVisible]) {
1126 loadCursor(getCursorVisibility(), shape);
1129 return GHOST_kSuccess;
1132 /** Reverse the bits in a GHOST_TUns8
1133 static GHOST_TUns8 uns8ReverseBits(GHOST_TUns8 ch)
1135 ch= ((ch>>1)&0x55) | ((ch<<1)&0xAA);
1136 ch= ((ch>>2)&0x33) | ((ch<<2)&0xCC);
1137 ch= ((ch>>4)&0x0F) | ((ch<<4)&0xF0);
1143 /** Reverse the bits in a GHOST_TUns16 */
1144 static GHOST_TUns16 uns16ReverseBits(GHOST_TUns16 shrt)
1146 shrt= ((shrt>>1)&0x5555) | ((shrt<<1)&0xAAAA);
1147 shrt= ((shrt>>2)&0x3333) | ((shrt<<2)&0xCCCC);
1148 shrt= ((shrt>>4)&0x0F0F) | ((shrt<<4)&0xF0F0);
1149 shrt= ((shrt>>8)&0x00FF) | ((shrt<<8)&0xFF00);
1153 GHOST_TSuccess GHOST_WindowCocoa::setWindowCustomCursorShape(GHOST_TUns8 *bitmap, GHOST_TUns8 *mask,
1154 int sizex, int sizey, int hotX, int hotY, int fg_color, int bg_color)
1157 NSPoint hotSpotPoint;
1158 NSBitmapImageRep *cursorImageRep;
1159 NSImage *cursorImage;
1161 GHOST_TUns16 *cursorBitmap;
1164 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1166 if (m_customCursor) {
1167 [m_customCursor release];
1168 m_customCursor = nil;
1172 cursorImageRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:nil
1179 colorSpaceName:NSDeviceWhiteColorSpace
1180 bytesPerRow:(sizex/8 + (sizex%8 >0 ?1:0))
1184 cursorBitmap = (GHOST_TUns16*)[cursorImageRep bitmapData];
1185 nbUns16 = [cursorImageRep bytesPerPlane]/2;
1187 for (y=0; y<nbUns16; y++) {
1188 #if !defined(__LITTLE_ENDIAN__)
1189 cursorBitmap[y] = ~uns16ReverseBits((bitmap[2*y]<<0) | (bitmap[2*y+1]<<8));
1190 cursorBitmap[nbUns16+y] = uns16ReverseBits((mask[2*y]<<0) | (mask[2*y+1]<<8));
1192 cursorBitmap[y] = ~uns16ReverseBits((bitmap[2*y+1]<<0) | (bitmap[2*y]<<8));
1193 cursorBitmap[nbUns16+y] = uns16ReverseBits((mask[2*y+1]<<0) | (mask[2*y]<<8));
1199 imSize.width = sizex;
1200 imSize.height= sizey;
1201 cursorImage = [[NSImage alloc] initWithSize:imSize];
1202 [cursorImage addRepresentation:cursorImageRep];
1204 hotSpotPoint.x = hotX;
1205 hotSpotPoint.y = hotY;
1207 //foreground and background color parameter is not handled for now (10.6)
1208 m_customCursor = [[NSCursor alloc] initWithImage:cursorImage
1209 hotSpot:hotSpotPoint];
1211 [cursorImageRep release];
1212 [cursorImage release];
1214 if ([m_window isVisible]) {
1215 loadCursor(getCursorVisibility(), GHOST_kStandardCursorCustom);
1218 return GHOST_kSuccess;
1221 GHOST_TSuccess GHOST_WindowCocoa::setWindowCustomCursorShape(GHOST_TUns8 bitmap[16][2],
1222 GHOST_TUns8 mask[16][2], int hotX, int hotY)
1224 return setWindowCustomCursorShape((GHOST_TUns8*)bitmap, (GHOST_TUns8*) mask, 16, 16, hotX, hotY, 0, 1);