Cocoa port start:
[blender.git] / intern / ghost / intern / GHOST_WindowCocoa.mm
1 /**
2  * $Id: GHOST_WindowCocoa.mm 23275 2009-09-16 15:55:00Z campbellbarton $
3  * ***** BEGIN GPL LICENSE BLOCK *****
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software Foundation,
17  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18  *
19  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
20  * All rights reserved.
21  *
22  * The Original Code is: all of this file.
23  *
24  * Contributor(s): none yet.
25  *
26  * ***** END GPL LICENSE BLOCK *****
27  */
28
29 /**
30
31  * $Id: GHOST_WindowCocoa.mm 23275 2009-09-16 15:55:00Z campbellbarton $
32  * Copyright (C) 2001 NaN Technologies B.V.
33  * @author      Maarten Gribnau
34  * @date        May 10, 2001
35  */
36
37 #ifdef HAVE_CONFIG_H
38 #include <config.h>
39 #endif
40
41 #include <Carbon/Carbon.h>
42
43 #include "GHOST_WindowCocoa.h"
44 #include "GHOST_Debug.h"
45
46 AGLContext GHOST_WindowCocoa::s_firstaglCtx = NULL;
47 #ifdef GHOST_DRAW_CARBON_GUTTER
48 const GHOST_TInt32 GHOST_WindowCocoa::s_sizeRectSize = 16;
49 #endif //GHOST_DRAW_CARBON_GUTTER
50
51 static const GLint sPreferredFormatWindow[8] = {
52 AGL_RGBA,
53 AGL_DOUBLEBUFFER,       
54 AGL_ACCELERATED,
55 AGL_DEPTH_SIZE,         32,
56 AGL_NONE,
57 };
58
59 static const GLint sPreferredFormatFullScreen[9] = {
60 AGL_RGBA,
61 AGL_DOUBLEBUFFER,
62 AGL_ACCELERATED,
63 AGL_FULLSCREEN,
64 AGL_DEPTH_SIZE,         32,
65 AGL_NONE,
66 };
67
68
69
70 WindowRef ugly_hack=NULL;
71
72 const EventTypeSpec     kWEvents[] = {
73         { kEventClassWindow, kEventWindowZoom },  /* for new zoom behaviour */ 
74 };
75
76 static OSStatus myWEventHandlerProc(EventHandlerCallRef handler, EventRef event, void* userData) {
77         WindowRef mywindow;
78         GHOST_WindowCocoa *ghost_window;
79         OSStatus err;
80         int theState;
81         
82         if (::GetEventKind(event) == kEventWindowZoom) {
83                 err =  ::GetEventParameter (event,kEventParamDirectObject,typeWindowRef,NULL,sizeof(mywindow),NULL, &mywindow);
84                 ghost_window = (GHOST_WindowCocoa *) GetWRefCon(mywindow);
85                 theState = ghost_window->getMac_windowState();
86                 if (theState == 1) 
87                         ghost_window->setMac_windowState(2);
88                 else if (theState == 2)
89                         ghost_window->setMac_windowState(1);
90
91         }
92         return eventNotHandledErr;
93 }
94
95 GHOST_WindowCocoa::GHOST_WindowCocoa(
96         const STR_String& title,
97         GHOST_TInt32 left,
98         GHOST_TInt32 top,
99         GHOST_TUns32 width,
100         GHOST_TUns32 height,
101         GHOST_TWindowState state,
102         GHOST_TDrawingContextType type,
103         const bool stereoVisual
104 ) :
105         GHOST_Window(title, left, top, width, height, state, GHOST_kDrawingContextTypeNone),
106         m_windowRef(0),
107         m_grafPtr(0),
108         m_aglCtx(0),
109         m_customCursor(0),
110         m_fullScreenDirty(false)
111 {
112     Str255 title255;
113         OSStatus err;
114         
115         //fprintf(stderr," main screen top %i left %i height %i width %i\n", top, left, height, width);
116         
117         if (state >= GHOST_kWindowState8Normal ) {
118                 if(state == GHOST_kWindowState8Normal) state= GHOST_kWindowStateNormal;
119                 else if(state == GHOST_kWindowState8Maximized) state= GHOST_kWindowStateMaximized;
120                 else if(state == GHOST_kWindowState8Minimized) state= GHOST_kWindowStateMinimized;
121                 else if(state == GHOST_kWindowState8FullScreen) state= GHOST_kWindowStateFullScreen;
122                 
123                 // state = state - 8;   this was the simple version of above code, doesnt work in gcc 4.0
124                 
125                 setMac_windowState(1);
126         } else 
127                 setMac_windowState(0);
128
129         if (state != GHOST_kWindowStateFullScreen) {
130         Rect bnds = { top, left, top+height, left+width };
131         // Boolean visible = (state == GHOST_kWindowStateNormal) || (state == GHOST_kWindowStateMaximized); /*unused*/
132         gen2mac(title, title255);
133         
134                 err =  ::CreateNewWindow( kDocumentWindowClass,
135                                                                  kWindowStandardDocumentAttributes+kWindowLiveResizeAttribute,
136                                                                  &bnds,
137                                                                  &m_windowRef);
138                 
139                 if ( err != noErr) {
140                         fprintf(stderr," error creating window %i \n",err);
141                 } else {
142                         
143                         ::SetWRefCon(m_windowRef,(SInt32)this);
144                         setTitle(title);
145                         err = InstallWindowEventHandler (m_windowRef, myWEventHandlerProc, GetEventTypeCount(kWEvents), kWEvents,NULL,NULL); 
146                         if ( err != noErr) {
147                                 fprintf(stderr," error creating handler %i \n",err);
148                         } else {
149                                 //      ::TransitionWindow (m_windowRef,kWindowZoomTransitionEffect,kWindowShowTransitionAction,NULL);
150                                 ::ShowWindow(m_windowRef);
151                                 ::MoveWindow (m_windowRef, left, top,true);
152                                 
153                         }
154                 }
155         if (m_windowRef) {
156             m_grafPtr = ::GetWindowPort(m_windowRef);
157             setDrawingContextType(type);
158             updateDrawingContext();
159             activateDrawingContext();
160         }
161                 if(ugly_hack==NULL) {
162                         ugly_hack= m_windowRef;
163                         // when started from commandline, window remains in the back... also for play anim
164                         ProcessSerialNumber psn;
165                         GetCurrentProcess(&psn);
166                         SetFrontProcess(&psn);
167                 }
168     }
169     else {
170     /*
171         Rect bnds = { top, left, top+height, left+width };
172         gen2mac("", title255);
173         m_windowRef = ::NewCWindow(
174             nil,                                                        // Storage 
175             &bnds,                                                      // Bounding rectangle of the window
176             title255,                                           // Title of the window
177             0,                                                          // Window initially visible
178             plainDBox,                                          // procID
179             (WindowRef)-1L,                                     // Put window before all other windows
180             0,                                                          // Window has minimize box
181             (SInt32)this);                                      // Store a pointer to the class in the refCon
182     */
183         //GHOST_PRINT("GHOST_WindowCocoa::GHOST_WindowCocoa(): creating full-screen OpenGL context\n");
184         setDrawingContextType(GHOST_kDrawingContextTypeOpenGL);;installDrawingContext(GHOST_kDrawingContextTypeOpenGL);
185         updateDrawingContext();
186         activateDrawingContext();        
187
188         m_tablet.Active = GHOST_kTabletModeNone;
189     }
190 }
191
192
193 GHOST_WindowCocoa::~GHOST_WindowCocoa()
194 {
195         if (m_customCursor) delete m_customCursor;
196
197         if(ugly_hack==m_windowRef) ugly_hack= NULL;
198         
199         // printf("GHOST_WindowCocoa::~GHOST_WindowCocoa(): removing drawing context\n");
200         if(ugly_hack==NULL) setDrawingContextType(GHOST_kDrawingContextTypeNone);
201     if (m_windowRef) {
202         ::DisposeWindow(m_windowRef);
203                 m_windowRef = 0;
204         }
205 }
206
207 bool GHOST_WindowCocoa::getValid() const
208 {
209     bool valid;
210     if (!m_fullScreen) {
211         valid = (m_windowRef != 0) && (m_grafPtr != 0) && ::IsValidWindowPtr(m_windowRef);
212     }
213     else {
214         valid = true;
215     }
216     return valid;
217 }
218
219
220 void GHOST_WindowCocoa::setTitle(const STR_String& title)
221 {
222     GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setTitle(): window invalid")
223     Str255 title255;
224     gen2mac(title, title255);
225         ::SetWTitle(m_windowRef, title255);
226 }
227
228
229 void GHOST_WindowCocoa::getTitle(STR_String& title) const
230 {
231     GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getTitle(): window invalid")
232     Str255 title255;
233     ::GetWTitle(m_windowRef, title255);
234     mac2gen(title255, title);
235 }
236
237
238 void GHOST_WindowCocoa::getWindowBounds(GHOST_Rect& bounds) const
239 {
240         OSStatus success;
241         Rect rect;
242         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getWindowBounds(): window invalid")
243         success = ::GetWindowBounds(m_windowRef, kWindowStructureRgn, &rect);
244         bounds.m_b = rect.bottom;
245         bounds.m_l = rect.left;
246         bounds.m_r = rect.right;
247         bounds.m_t = rect.top;
248 }
249
250
251 void GHOST_WindowCocoa::getClientBounds(GHOST_Rect& bounds) const
252 {
253         Rect rect;
254         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getClientBounds(): window invalid")
255         //::GetPortBounds(m_grafPtr, &rect);
256         ::GetWindowBounds(m_windowRef, kWindowContentRgn, &rect);
257
258         bounds.m_b = rect.bottom;
259         bounds.m_l = rect.left;
260         bounds.m_r = rect.right;
261         bounds.m_t = rect.top;
262
263         // Subtract gutter height from bottom
264 #ifdef GHOST_DRAW_CARBON_GUTTER
265         if ((bounds.m_b - bounds.m_t) > s_sizeRectSize)
266         {
267                 bounds.m_b -= s_sizeRectSize;
268         }
269         else
270         {
271                 bounds.m_t = bounds.m_b;
272         }
273 #endif //GHOST_DRAW_CARBON_GUTTER
274 }
275
276
277 GHOST_TSuccess GHOST_WindowCocoa::setClientWidth(GHOST_TUns32 width)
278 {
279         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setClientWidth(): window invalid")
280         GHOST_Rect cBnds, wBnds;
281         getClientBounds(cBnds);
282         if (((GHOST_TUns32)cBnds.getWidth()) != width) {
283                 ::SizeWindow(m_windowRef, width, cBnds.getHeight(), true);
284         }
285         return GHOST_kSuccess;
286 }
287
288
289 GHOST_TSuccess GHOST_WindowCocoa::setClientHeight(GHOST_TUns32 height)
290 {
291         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setClientHeight(): window invalid")
292         GHOST_Rect cBnds, wBnds;
293         getClientBounds(cBnds);
294 #ifdef GHOST_DRAW_CARBON_GUTTER
295         if (((GHOST_TUns32)cBnds.getHeight()) != height+s_sizeRectSize) {
296                 ::SizeWindow(m_windowRef, cBnds.getWidth(), height+s_sizeRectSize, true);
297         }
298 #else //GHOST_DRAW_CARBON_GUTTER
299         if (((GHOST_TUns32)cBnds.getHeight()) != height) {
300                 ::SizeWindow(m_windowRef, cBnds.getWidth(), height, true);
301         }
302 #endif //GHOST_DRAW_CARBON_GUTTER
303         return GHOST_kSuccess;
304 }
305
306
307 GHOST_TSuccess GHOST_WindowCocoa::setClientSize(GHOST_TUns32 width, GHOST_TUns32 height)
308 {
309         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setClientSize(): window invalid")
310         GHOST_Rect cBnds, wBnds;
311         getClientBounds(cBnds);
312 #ifdef GHOST_DRAW_CARBON_GUTTER
313         if ((((GHOST_TUns32)cBnds.getWidth()) != width) ||
314             (((GHOST_TUns32)cBnds.getHeight()) != height+s_sizeRectSize)) {
315                 ::SizeWindow(m_windowRef, width, height+s_sizeRectSize, true);
316         }
317 #else //GHOST_DRAW_CARBON_GUTTER
318         if ((((GHOST_TUns32)cBnds.getWidth()) != width) ||
319             (((GHOST_TUns32)cBnds.getHeight()) != height)) {
320                 ::SizeWindow(m_windowRef, width, height, true);
321         }
322 #endif //GHOST_DRAW_CARBON_GUTTER
323         return GHOST_kSuccess;
324 }
325
326
327 GHOST_TWindowState GHOST_WindowCocoa::getState() const
328 {
329         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getState(): window invalid")
330         GHOST_TWindowState state;
331         if (::IsWindowVisible(m_windowRef) == false) {
332                 state = GHOST_kWindowStateMinimized;
333         }
334         else if (::IsWindowInStandardState(m_windowRef, nil, nil)) {
335                 state = GHOST_kWindowStateMaximized;
336         }
337         else {
338                 state = GHOST_kWindowStateNormal;
339         }
340         return state;
341 }
342
343
344 void GHOST_WindowCocoa::screenToClient(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
345 {
346         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::screenToClient(): window invalid")
347         Point point;
348         point.h = inX;
349         point.v = inY;
350     GrafPtr oldPort;
351     ::GetPort(&oldPort);
352     ::SetPort(m_grafPtr);
353         ::GlobalToLocal(&point);
354     ::SetPort(oldPort);
355         outX = point.h;
356         outY = point.v;
357 }
358
359
360 void GHOST_WindowCocoa::clientToScreen(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
361 {
362         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::clientToScreen(): window invalid")
363         Point point;
364         point.h = inX;
365         point.v = inY;
366     GrafPtr oldPort;
367     ::GetPort(&oldPort);
368     ::SetPort(m_grafPtr);
369         ::LocalToGlobal(&point);
370     ::SetPort(oldPort);
371         outX = point.h;
372         outY = point.v;
373 }
374
375
376 GHOST_TSuccess GHOST_WindowCocoa::setState(GHOST_TWindowState state)
377 {
378         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setState(): window invalid")
379     switch (state) {
380         case GHOST_kWindowStateMinimized:
381             ::HideWindow(m_windowRef);
382             break;
383         case GHOST_kWindowStateModified:
384                 SetWindowModified(m_windowRef, 1);
385                 break;
386         case GHOST_kWindowStateUnModified:
387                 SetWindowModified(m_windowRef, 0);
388                 break;
389         case GHOST_kWindowStateMaximized:
390         case GHOST_kWindowStateNormal:
391         default:
392             ::ShowWindow(m_windowRef);
393             break;
394     }
395     return GHOST_kSuccess;
396 }
397
398
399 GHOST_TSuccess GHOST_WindowCocoa::setOrder(GHOST_TWindowOrder order)
400 {
401         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setOrder(): window invalid")
402     if (order == GHOST_kWindowOrderTop) {
403         //::BringToFront(m_windowRef); is wrong, front window should be active for input too
404                 ::SelectWindow(m_windowRef);
405     }
406     else {
407                 /* doesnt work if you do this with a mouseclick */
408         ::SendBehind(m_windowRef, nil);
409     }
410     return GHOST_kSuccess;
411 }
412
413 /*#define  WAIT_FOR_VSYNC 1*/
414 #ifdef WAIT_FOR_VSYNC
415 #include <OpenGL/OpenGL.h>
416 #endif
417
418 GHOST_TSuccess GHOST_WindowCocoa::swapBuffers()
419 {
420 #ifdef WAIT_FOR_VSYNC
421 /* wait for vsync, to avoid tearing artifacts */
422 long VBL = 1;
423 CGLSetParameter(CGLGetCurrentContext(), kCGLCPSwapInterval, &VBL);
424 #endif
425
426     GHOST_TSuccess succeeded = GHOST_kSuccess;
427     if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) {
428         if (m_aglCtx) {
429             ::aglSwapBuffers(m_aglCtx);
430         }
431         else {
432             succeeded = GHOST_kFailure;
433         }
434     }
435     return succeeded;
436 }
437
438 GHOST_TSuccess GHOST_WindowCocoa::updateDrawingContext()
439 {
440         GHOST_TSuccess succeeded = GHOST_kSuccess;
441         if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) {
442                 if (m_aglCtx) {
443                         ::aglUpdateContext(m_aglCtx);
444                 }
445                 else {
446                         succeeded = GHOST_kFailure;
447                 }
448         }
449         return succeeded;
450 }
451
452 GHOST_TSuccess GHOST_WindowCocoa::activateDrawingContext()
453 {
454         GHOST_TSuccess succeeded = GHOST_kSuccess;
455         if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) {
456                 if (m_aglCtx) {
457                         ::aglSetCurrentContext(m_aglCtx);
458 #ifdef GHOST_DRAW_CARBON_GUTTER
459                         // Restrict drawing to non-gutter area
460                         ::aglEnable(m_aglCtx, AGL_BUFFER_RECT);
461                         GHOST_Rect bnds;
462                         getClientBounds(bnds);
463                         GLint b[4] =
464                         {
465                                 bnds.m_l,
466                                 bnds.m_t+s_sizeRectSize,
467                                 bnds.m_r-bnds.m_l,
468                                 bnds.m_b-bnds.m_t
469                         };
470                         GLboolean result = ::aglSetInteger(m_aglCtx, AGL_BUFFER_RECT, b);
471 #endif //GHOST_DRAW_CARBON_GUTTER
472                 }
473                 else {
474                         succeeded = GHOST_kFailure;
475                 }
476         }
477         return succeeded;
478 }
479
480
481 GHOST_TSuccess GHOST_WindowCocoa::installDrawingContext(GHOST_TDrawingContextType type)
482 {
483         GHOST_TSuccess success = GHOST_kFailure;
484         switch (type) {
485                 case GHOST_kDrawingContextTypeOpenGL:
486                         {
487                         if (!getValid()) break;
488             
489             AGLPixelFormat pixelFormat;
490             if (!m_fullScreen) {
491                 pixelFormat = ::aglChoosePixelFormat(0, 0, sPreferredFormatWindow);
492                 m_aglCtx = ::aglCreateContext(pixelFormat, s_firstaglCtx);
493                 if (!m_aglCtx) break;
494                                 if (!s_firstaglCtx) s_firstaglCtx = m_aglCtx;
495                  success = ::aglSetDrawable(m_aglCtx, m_grafPtr) == GL_TRUE ? GHOST_kSuccess : GHOST_kFailure;
496             }
497             else {
498                 //GHOST_PRINT("GHOST_WindowCocoa::installDrawingContext(): init full-screen OpenGL\n");
499 GDHandle device=::GetMainDevice();pixelFormat=::aglChoosePixelFormat(&device,1,sPreferredFormatFullScreen);
500                 m_aglCtx = ::aglCreateContext(pixelFormat, 0);
501                 if (!m_aglCtx) break;
502                                 if (!s_firstaglCtx) s_firstaglCtx = m_aglCtx;
503                 //GHOST_PRINT("GHOST_WindowCocoa::installDrawingContext(): created OpenGL context\n");
504                 //::CGGetActiveDisplayList(0, NULL, &m_numDisplays)
505                 success = ::aglSetFullScreen(m_aglCtx, m_fullScreenWidth, m_fullScreenHeight, 75, 0) == GL_TRUE ? GHOST_kSuccess : GHOST_kFailure;
506                 /*
507                 if (success == GHOST_kSuccess) {
508                     GHOST_PRINT("GHOST_WindowCocoa::installDrawingContext(): init full-screen OpenGL succeeded\n");
509                 }
510                 else {
511                     GHOST_PRINT("GHOST_WindowCocoa::installDrawingContext(): init full-screen OpenGL failed\n");
512                 }
513                 */
514             }
515             ::aglDestroyPixelFormat(pixelFormat);
516                         }
517                         break;
518                 
519                 case GHOST_kDrawingContextTypeNone:
520                         success = GHOST_kSuccess;
521                         break;
522                 
523                 default:
524                         break;
525         }
526         return success;
527 }
528
529
530 GHOST_TSuccess GHOST_WindowCocoa::removeDrawingContext()
531 {
532         GHOST_TSuccess success = GHOST_kFailure;
533         switch (m_drawingContextType) {
534                 case GHOST_kDrawingContextTypeOpenGL:
535                         if (m_aglCtx) {
536                 aglSetCurrentContext(NULL);
537                 aglSetDrawable(m_aglCtx, NULL);
538                 //aglDestroyContext(m_aglCtx);
539                                 if (s_firstaglCtx == m_aglCtx) s_firstaglCtx = NULL;
540                                 success = ::aglDestroyContext(m_aglCtx) == GL_TRUE ? GHOST_kSuccess : GHOST_kFailure;
541                                 m_aglCtx = 0;
542                         }
543                         break;
544                 case GHOST_kDrawingContextTypeNone:
545                         success = GHOST_kSuccess;
546                         break;
547                 default:
548                         break;
549         }
550         return success;
551 }
552
553
554 GHOST_TSuccess GHOST_WindowCocoa::invalidate()
555 {
556         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::invalidate(): window invalid")
557     if (!m_fullScreen) {
558         Rect rect;
559         ::GetPortBounds(m_grafPtr, &rect);
560         ::InvalWindowRect(m_windowRef, &rect);
561     }
562     else {
563         //EventRef event;
564         //OSStatus status = ::CreateEvent(NULL, kEventClassWindow, kEventWindowUpdate, 0, 0, &event);
565         //GHOST_PRINT("GHOST_WindowCocoa::invalidate(): created event " << status << " \n");
566         //status = ::SetEventParameter(event, kEventParamDirectObject, typeWindowRef, sizeof(WindowRef), this);
567         //GHOST_PRINT("GHOST_WindowCocoa::invalidate(): set event parameter " << status << " \n");
568         //status = ::PostEventToQueue(::GetMainEventQueue(), event, kEventPriorityStandard);
569         //status = ::SendEventToEventTarget(event, ::GetApplicationEventTarget());
570         //GHOST_PRINT("GHOST_WindowCocoa::invalidate(): added event to queue " << status << " \n");
571         m_fullScreenDirty = true;
572     }
573         return GHOST_kSuccess;
574 }
575
576
577 void GHOST_WindowCocoa::gen2mac(const STR_String& in, Str255 out) const
578 {
579         STR_String tempStr  = in;
580         int num = tempStr.Length();
581         if (num > 255) num = 255;
582         ::memcpy(out+1, tempStr.Ptr(), num);
583         out[0] = num;
584 }
585
586
587 void GHOST_WindowCocoa::mac2gen(const Str255 in, STR_String& out) const
588 {
589         char tmp[256];
590         ::memcpy(tmp, in+1, in[0]);
591         tmp[in[0]] = '\0';
592         out = tmp;
593 }
594
595 void GHOST_WindowCocoa::loadCursor(bool visible, GHOST_TStandardCursor cursor) const
596 {
597         static bool systemCursorVisible = true;
598         
599         if (visible != systemCursorVisible) {
600                 if (visible) {
601                         ::ShowCursor();
602                         systemCursorVisible = true;
603                 }
604                 else {
605                         ::HideCursor();
606                         systemCursorVisible = false;
607                 }
608         }
609
610         if (cursor == GHOST_kStandardCursorCustom && m_customCursor) {
611                 ::SetCursor( m_customCursor );
612         } else {
613                 int carbon_cursor;
614         
615 #define GCMAP(ghostCursor, carbonCursor)        case ghostCursor: carbon_cursor = carbonCursor; break
616                 switch (cursor) {
617                 default:
618                 GCMAP( GHOST_kStandardCursorDefault,                            kThemeArrowCursor);
619                 GCMAP( GHOST_kStandardCursorRightArrow,                         kThemeAliasArrowCursor);
620                 GCMAP( GHOST_kStandardCursorLeftArrow,                          kThemeArrowCursor);
621                 GCMAP( GHOST_kStandardCursorInfo,                                       kThemeArrowCursor);
622                 GCMAP( GHOST_kStandardCursorDestroy,                            kThemeArrowCursor);
623                 GCMAP( GHOST_kStandardCursorHelp,                               kThemeArrowCursor);
624                 GCMAP( GHOST_kStandardCursorCycle,                                      kThemeArrowCursor);
625                 GCMAP( GHOST_kStandardCursorSpray,                                      kThemeArrowCursor);
626                 GCMAP( GHOST_kStandardCursorWait,                                       kThemeWatchCursor);
627                 GCMAP( GHOST_kStandardCursorText,                                       kThemeIBeamCursor);
628                 GCMAP( GHOST_kStandardCursorCrosshair,                          kThemeCrossCursor);
629                 GCMAP( GHOST_kStandardCursorUpDown,                                     kThemeClosedHandCursor);
630                 GCMAP( GHOST_kStandardCursorLeftRight,                          kThemeClosedHandCursor);
631                 GCMAP( GHOST_kStandardCursorTopSide,                            kThemeArrowCursor);
632                 GCMAP( GHOST_kStandardCursorBottomSide,                         kThemeArrowCursor);
633                 GCMAP( GHOST_kStandardCursorLeftSide,                           kThemeResizeLeftCursor);
634                 GCMAP( GHOST_kStandardCursorRightSide,                          kThemeResizeRightCursor);
635                 GCMAP( GHOST_kStandardCursorTopLeftCorner,                      kThemeArrowCursor);
636                 GCMAP( GHOST_kStandardCursorTopRightCorner,                     kThemeArrowCursor);
637                 GCMAP( GHOST_kStandardCursorBottomRightCorner,          kThemeArrowCursor);
638                 GCMAP( GHOST_kStandardCursorBottomLeftCorner,           kThemeArrowCursor);
639                 };
640 #undef GCMAP
641
642                 ::SetThemeCursor(carbon_cursor);
643         }
644 }
645
646
647 bool GHOST_WindowCocoa::getFullScreenDirty()
648 {
649     return m_fullScreen && m_fullScreenDirty;
650 }
651
652
653 GHOST_TSuccess GHOST_WindowCocoa::setWindowCursorVisibility(bool visible)
654 {
655         if (::FrontWindow() == m_windowRef) {
656                 loadCursor(visible, getCursorShape());
657         }
658         
659         return GHOST_kSuccess;
660 }
661         
662 GHOST_TSuccess GHOST_WindowCocoa::setWindowCursorShape(GHOST_TStandardCursor shape)
663 {
664         if (m_customCursor) {
665                 delete m_customCursor;
666                 m_customCursor = 0;
667         }
668
669         if (::FrontWindow() == m_windowRef) {
670                 loadCursor(getCursorVisibility(), shape);
671         }
672         
673         return GHOST_kSuccess;
674 }
675
676 #if 0
677 /** Reverse the bits in a GHOST_TUns8 */
678 static GHOST_TUns8 uns8ReverseBits(GHOST_TUns8 ch)
679 {
680         ch= ((ch>>1)&0x55) | ((ch<<1)&0xAA);
681         ch= ((ch>>2)&0x33) | ((ch<<2)&0xCC);
682         ch= ((ch>>4)&0x0F) | ((ch<<4)&0xF0);
683         return ch;
684 }
685 #endif
686
687
688 /** Reverse the bits in a GHOST_TUns16 */
689 static GHOST_TUns16 uns16ReverseBits(GHOST_TUns16 shrt)
690 {
691         shrt= ((shrt>>1)&0x5555) | ((shrt<<1)&0xAAAA);
692         shrt= ((shrt>>2)&0x3333) | ((shrt<<2)&0xCCCC);
693         shrt= ((shrt>>4)&0x0F0F) | ((shrt<<4)&0xF0F0);
694         shrt= ((shrt>>8)&0x00FF) | ((shrt<<8)&0xFF00);
695         return shrt;
696 }
697
698 GHOST_TSuccess GHOST_WindowCocoa::setWindowCustomCursorShape(GHOST_TUns8 *bitmap, GHOST_TUns8 *mask,
699                                         int sizex, int sizey, int hotX, int hotY, int fg_color, int bg_color)
700 {
701         int y;
702         
703         if (m_customCursor) {
704                 delete m_customCursor;
705                 m_customCursor = 0;
706         }
707         
708         m_customCursor = new Cursor;
709         if (!m_customCursor) return GHOST_kFailure;
710         
711         for (y=0; y<16; y++) {
712 #if !defined(__LITTLE_ENDIAN__)
713                 m_customCursor->data[y] = uns16ReverseBits((bitmap[2*y]<<0) | (bitmap[2*y+1]<<8));
714                 m_customCursor->mask[y] = uns16ReverseBits((mask[2*y]<<0) | (mask[2*y+1]<<8));
715 #else
716                 m_customCursor->data[y] = uns16ReverseBits((bitmap[2*y+1]<<0) | (bitmap[2*y]<<8));
717                 m_customCursor->mask[y] = uns16ReverseBits((mask[2*y+1]<<0) | (mask[2*y]<<8));
718 #endif
719                         
720         }
721         
722         m_customCursor->hotSpot.h = hotX;
723         m_customCursor->hotSpot.v = hotY;
724         
725         if (::FrontWindow() == m_windowRef) {
726                 loadCursor(getCursorVisibility(), GHOST_kStandardCursorCustom);
727         }
728         
729         return GHOST_kSuccess;
730 }
731
732 GHOST_TSuccess GHOST_WindowCocoa::setWindowCustomCursorShape(GHOST_TUns8 bitmap[16][2], 
733                                                                                                 GHOST_TUns8 mask[16][2], int hotX, int hotY)
734 {
735         return setWindowCustomCursorShape((GHOST_TUns8*)bitmap, (GHOST_TUns8*) mask, 16, 16, hotX, hotY, 0, 1);
736 }
737
738
739 void GHOST_WindowCocoa::setMac_windowState(short value)
740 {
741         mac_windowState = value;
742 }
743
744 short GHOST_WindowCocoa::getMac_windowState()
745 {
746         return mac_windowState;
747 }