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