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