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