Initial revision
[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 #include "GHOST_WindowCarbon.h"
41
42 #include "GHOST_Debug.h"
43
44 AGLContext GHOST_WindowCarbon::s_firstaglCtx = NULL;
45
46 static const GLint sPreferredFormatWindow[7] = {
47 AGL_RGBA,                       GL_TRUE,
48 AGL_DOUBLEBUFFER,       GL_TRUE,
49 AGL_DEPTH_SIZE,         16,
50 AGL_NONE,
51 };
52
53 static const GLint sPreferredFormatFullScreen[7] = {
54 AGL_RGBA,
55 AGL_DOUBLEBUFFER,
56 AGL_ACCELERATED,
57 AGL_FULLSCREEN,
58 AGL_DEPTH_SIZE,         16,
59 AGL_NONE,
60 };
61
62 GHOST_WindowCarbon::GHOST_WindowCarbon(
63         const STR_String& title,
64         GHOST_TInt32 left,
65         GHOST_TInt32 top,
66         GHOST_TUns32 width,
67         GHOST_TUns32 height,
68         GHOST_TWindowState state,
69         GHOST_TDrawingContextType type,
70         const bool stereoVisual
71 ) :
72         GHOST_Window(title, left, top, width, height, state, GHOST_kDrawingContextTypeNone),
73         m_windowRef(0),
74         m_grafPtr(0),
75         m_aglCtx(0),
76         m_customCursor(0),
77         m_fullScreenDirty(false)
78 {
79     Str255 title255;
80
81         if (state != GHOST_kWindowStateFullScreen) {
82         Rect bnds = { top, left, top+height, left+width };
83         Boolean visible = (state == GHOST_kWindowStateNormal) || (state == GHOST_kWindowStateMaximized);
84         gen2mac(title, title255);
85         
86         m_windowRef = ::NewCWindow(
87             nil,                                                        // Storage 
88             &bnds,                                                      // Bounding rectangle of the window
89             title255,                                           // Title of the window
90             visible,                                            // Window initially visible
91             kWindowFullZoomGrowDocumentProc, //kWindowGrowDocumentProc,         // procID
92             (WindowRef)-1L,                                     // Put window before all other windows
93             true,                                                       // Window has minimize box
94             (SInt32)this);                                      // Store a pointer to the class in the refCon
95         if (m_windowRef) {
96             m_grafPtr = ::GetWindowPort(m_windowRef);
97             setDrawingContextType(type);
98             updateDrawingContext();
99             activateDrawingContext();
100         }
101     }
102     else {
103     /*
104         Rect bnds = { top, left, top+height, left+width };
105         gen2mac("", title255);
106         m_windowRef = ::NewCWindow(
107             nil,                                                        // Storage 
108             &bnds,                                                      // Bounding rectangle of the window
109             title255,                                           // Title of the window
110             0,                                                          // Window initially visible
111             plainDBox,                                          // procID
112             (WindowRef)-1L,                                     // Put window before all other windows
113             0,                                                          // Window has minimize box
114             (SInt32)this);                                      // Store a pointer to the class in the refCon
115     */
116         //GHOST_PRINT("GHOST_WindowCarbon::GHOST_WindowCarbon(): creating full-screen OpenGL context\n");
117         setDrawingContextType(GHOST_kDrawingContextTypeOpenGL);
118         updateDrawingContext();
119         activateDrawingContext();        
120     }
121 }
122
123
124 GHOST_WindowCarbon::~GHOST_WindowCarbon()
125 {
126         if (m_customCursor) delete m_customCursor;
127
128     //GHOST_PRINT("GHOST_WindowCarbon::~GHOST_WindowCarbon(): removing drawing context\n");
129         setDrawingContextType(GHOST_kDrawingContextTypeNone);
130     if (m_windowRef) {
131         ::DisposeWindow(m_windowRef);
132                 m_windowRef = 0;
133         }
134 }
135
136 bool GHOST_WindowCarbon::getValid() const
137 {
138     bool valid;
139     if (!m_fullScreen) {
140         valid = (m_windowRef != 0) && (m_grafPtr != 0) && ::IsValidWindowPtr(m_windowRef);
141     }
142     else {
143         valid = true;
144     }
145     return valid;
146 }
147
148
149 void GHOST_WindowCarbon::setTitle(const STR_String& title)
150 {
151     GHOST_ASSERT(getValid(), "GHOST_WindowCarbon::setTitle(): window invalid")
152     Str255 title255;
153     gen2mac(title, title255);
154         ::SetWTitle(m_windowRef, title255);
155 }
156
157
158 void GHOST_WindowCarbon::getTitle(STR_String& title) const
159 {
160     GHOST_ASSERT(getValid(), "GHOST_WindowCarbon::getTitle(): window invalid")
161     Str255 title255;
162     ::GetWTitle(m_windowRef, title255);
163     mac2gen(title255, title);
164 }
165
166
167 void GHOST_WindowCarbon::getWindowBounds(GHOST_Rect& bounds) const
168 {
169         OSStatus success;
170         Rect rect;
171         GHOST_ASSERT(getValid(), "GHOST_WindowCarbon::getWindowBounds(): window invalid")
172         success = ::GetWindowBounds(m_windowRef, kWindowStructureRgn, &rect);
173         bounds.m_b = rect.bottom;
174         bounds.m_l = rect.left;
175         bounds.m_r = rect.right;
176         bounds.m_t = rect.top;
177 }
178
179
180 void GHOST_WindowCarbon::getClientBounds(GHOST_Rect& bounds) const
181 {
182         Rect rect;
183         GHOST_ASSERT(getValid(), "GHOST_WindowCarbon::getClientBounds(): window invalid")
184         ::GetPortBounds(m_grafPtr, &rect);
185         bounds.m_b = rect.bottom;
186         bounds.m_l = rect.left;
187         bounds.m_r = rect.right;
188         bounds.m_t = rect.top;
189 }
190
191
192 GHOST_TSuccess GHOST_WindowCarbon::setClientWidth(GHOST_TUns32 width)
193 {
194         GHOST_ASSERT(getValid(), "GHOST_WindowCarbon::setClientWidth(): window invalid")
195         GHOST_Rect cBnds, wBnds;
196         getClientBounds(cBnds);
197         if (((GHOST_TUns32)cBnds.getWidth()) != width) {
198                 ::SizeWindow(m_windowRef, width, cBnds.getHeight(), true);
199         }
200         return GHOST_kSuccess;
201 }
202
203
204 GHOST_TSuccess GHOST_WindowCarbon::setClientHeight(GHOST_TUns32 height)
205 {
206         GHOST_ASSERT(getValid(), "GHOST_WindowCarbon::setClientHeight(): window invalid")
207         GHOST_Rect cBnds, wBnds;
208         getClientBounds(cBnds);
209         if (((GHOST_TUns32)cBnds.getHeight()) != height) {
210                 ::SizeWindow(m_windowRef, cBnds.getWidth(), height, true);
211         }
212         return GHOST_kSuccess;
213 }
214
215
216 GHOST_TSuccess GHOST_WindowCarbon::setClientSize(GHOST_TUns32 width, GHOST_TUns32 height)
217 {
218         GHOST_ASSERT(getValid(), "GHOST_WindowCarbon::setClientSize(): window invalid")
219         GHOST_Rect cBnds, wBnds;
220         getClientBounds(cBnds);
221         if ((((GHOST_TUns32)cBnds.getWidth()) != width) ||
222             (((GHOST_TUns32)cBnds.getHeight()) != height)) {
223                 ::SizeWindow(m_windowRef, width, height, true);
224         }
225         return GHOST_kSuccess;
226 }
227
228
229 GHOST_TWindowState GHOST_WindowCarbon::getState() const
230 {
231         GHOST_ASSERT(getValid(), "GHOST_WindowCarbon::getState(): window invalid")
232         GHOST_TWindowState state;
233         if (::IsWindowVisible(m_windowRef)) {
234                 state = GHOST_kWindowStateMinimized;
235         }
236         else if (::IsWindowInStandardState(m_windowRef, nil, nil)) {
237                 state = GHOST_kWindowStateMaximized;
238         }
239         else {
240                 state = GHOST_kWindowStateNormal;
241         }
242         return state;
243 }
244
245
246 void GHOST_WindowCarbon::screenToClient(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
247 {
248         GHOST_ASSERT(getValid(), "GHOST_WindowCarbon::screenToClient(): window invalid")
249         Point point;
250         point.h = inX;
251         point.v = inY;
252     GrafPtr oldPort;
253     ::GetPort(&oldPort);
254     ::SetPort(m_grafPtr);
255         ::GlobalToLocal(&point);
256     ::SetPort(oldPort);
257         outX = point.h;
258         outY = point.v;
259 }
260
261
262 void GHOST_WindowCarbon::clientToScreen(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
263 {
264         GHOST_ASSERT(getValid(), "GHOST_WindowCarbon::clientToScreen(): window invalid")
265         Point point;
266         point.h = inX;
267         point.v = inY;
268     GrafPtr oldPort;
269     ::GetPort(&oldPort);
270     ::SetPort(m_grafPtr);
271         ::LocalToGlobal(&point);
272     ::SetPort(oldPort);
273         outX = point.h;
274         outY = point.v;
275 }
276
277
278 GHOST_TSuccess GHOST_WindowCarbon::setState(GHOST_TWindowState state)
279 {
280         GHOST_ASSERT(getValid(), "GHOST_WindowCarbon::setState(): window invalid")
281     switch (state) {
282         case GHOST_kWindowStateMinimized:
283             ::HideWindow(m_windowRef);
284             break;
285         case GHOST_kWindowStateMaximized:
286         case GHOST_kWindowStateNormal:
287         default:
288             ::ShowWindow(m_windowRef);
289             break;
290     }
291     return GHOST_kSuccess;
292 }
293
294
295 GHOST_TSuccess GHOST_WindowCarbon::setOrder(GHOST_TWindowOrder order)
296 {
297         GHOST_ASSERT(getValid(), "GHOST_WindowCarbon::setOrder(): window invalid")
298     if (order == GHOST_kWindowOrderTop) {
299         ::BringToFront(m_windowRef);
300     }
301     else {
302         ::SendBehind(m_windowRef, nil);
303     }
304     return GHOST_kSuccess;
305 }
306
307
308 GHOST_TSuccess GHOST_WindowCarbon::swapBuffers()
309 {
310     GHOST_TSuccess succeeded = GHOST_kSuccess;
311     if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) {
312         if (m_aglCtx) {
313             ::aglSwapBuffers(m_aglCtx);
314         }
315         else {
316             succeeded = GHOST_kFailure;
317         }
318     }
319     return succeeded;
320 }
321
322 GHOST_TSuccess GHOST_WindowCarbon::updateDrawingContext()
323 {
324         GHOST_TSuccess succeeded = GHOST_kSuccess;
325         if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) {
326                 if (m_aglCtx) {
327                         ::aglUpdateContext(m_aglCtx);
328                 }
329                 else {
330                         succeeded = GHOST_kFailure;
331                 }
332         }
333         return succeeded;
334 }
335
336 GHOST_TSuccess GHOST_WindowCarbon::activateDrawingContext()
337 {
338         GHOST_TSuccess succeeded = GHOST_kSuccess;
339         if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) {
340                 if (m_aglCtx) {
341                         ::aglSetCurrentContext(m_aglCtx);
342                 }
343                 else {
344                         succeeded = GHOST_kFailure;
345                 }
346         }
347         return succeeded;
348 }
349
350
351 GHOST_TSuccess GHOST_WindowCarbon::installDrawingContext(GHOST_TDrawingContextType type)
352 {
353         GHOST_TSuccess success = GHOST_kFailure;
354         switch (type) {
355                 case GHOST_kDrawingContextTypeOpenGL:
356                         {
357                         if (!getValid()) break;
358             
359             AGLPixelFormat pixelFormat;
360             if (!m_fullScreen) {
361                 pixelFormat = ::aglChoosePixelFormat(0, 0, sPreferredFormatWindow);
362                 m_aglCtx = ::aglCreateContext(pixelFormat, s_firstaglCtx);
363                 if (!m_aglCtx) break;
364                                 if (!s_firstaglCtx) s_firstaglCtx = m_aglCtx;
365                  success = ::aglSetDrawable(m_aglCtx, m_grafPtr) == GL_TRUE ? GHOST_kSuccess : GHOST_kFailure;
366             }
367             else {
368                 //GHOST_PRINT("GHOST_WindowCarbon::installDrawingContext(): init full-screen OpenGL\n");
369                 pixelFormat = ::aglChoosePixelFormat(0, 0, sPreferredFormatFullScreen);
370                 m_aglCtx = ::aglCreateContext(pixelFormat, 0);
371                 if (!m_aglCtx) break;
372                                 if (!s_firstaglCtx) s_firstaglCtx = m_aglCtx;
373                 //GHOST_PRINT("GHOST_WindowCarbon::installDrawingContext(): created OpenGL context\n");
374                 //::CGGetActiveDisplayList(0, NULL, &m_numDisplays)
375                 success = ::aglSetFullScreen(m_aglCtx, m_fullScreenWidth, m_fullScreenHeight, 75, 0) == GL_TRUE ? GHOST_kSuccess : GHOST_kFailure;
376                 /*
377                 if (success == GHOST_kSuccess) {
378                     GHOST_PRINT("GHOST_WindowCarbon::installDrawingContext(): init full-screen OpenGL succeeded\n");
379                 }
380                 else {
381                     GHOST_PRINT("GHOST_WindowCarbon::installDrawingContext(): init full-screen OpenGL failed\n");
382                 }
383                 */
384             }
385             ::aglDestroyPixelFormat(pixelFormat);
386                         }
387                         break;
388                 
389                 case GHOST_kDrawingContextTypeNone:
390                         success = GHOST_kSuccess;
391                         break;
392                 
393                 default:
394                         break;
395         }
396         return success;
397 }
398
399
400 GHOST_TSuccess GHOST_WindowCarbon::removeDrawingContext()
401 {
402         GHOST_TSuccess success = GHOST_kFailure;
403         switch (m_drawingContextType) {
404                 case GHOST_kDrawingContextTypeOpenGL:
405                         if (m_aglCtx) {
406                 aglSetCurrentContext(NULL);
407                 aglSetDrawable(m_aglCtx, NULL);
408                 //aglDestroyContext(m_aglCtx);
409                                 if (s_firstaglCtx == m_aglCtx) s_firstaglCtx = NULL;
410                                 success = ::aglDestroyContext(m_aglCtx) == GL_TRUE ? GHOST_kSuccess : GHOST_kFailure;
411                                 m_aglCtx = 0;
412                         }
413                         break;
414                 case GHOST_kDrawingContextTypeNone:
415                         success = GHOST_kSuccess;
416                         break;
417                 default:
418                         break;
419         }
420         return success;
421 }
422
423
424 GHOST_TSuccess GHOST_WindowCarbon::invalidate()
425 {
426         GHOST_ASSERT(getValid(), "GHOST_WindowCarbon::invalidate(): window invalid")
427     if (!m_fullScreen) {
428         Rect rect;
429         ::GetPortBounds(m_grafPtr, &rect);
430         ::InvalWindowRect(m_windowRef, &rect);
431     }
432     else {
433         //EventRef event;
434         //OSStatus status = ::CreateEvent(NULL, kEventClassWindow, kEventWindowUpdate, 0, 0, &event);
435         //GHOST_PRINT("GHOST_WindowCarbon::invalidate(): created event " << status << " \n");
436         //status = ::SetEventParameter(event, kEventParamDirectObject, typeWindowRef, sizeof(WindowRef), this);
437         //GHOST_PRINT("GHOST_WindowCarbon::invalidate(): set event parameter " << status << " \n");
438         //status = ::PostEventToQueue(::GetMainEventQueue(), event, kEventPriorityStandard);
439         //status = ::SendEventToEventTarget(event, ::GetApplicationEventTarget());
440         //GHOST_PRINT("GHOST_WindowCarbon::invalidate(): added event to queue " << status << " \n");
441         m_fullScreenDirty = true;
442     }
443         return GHOST_kSuccess;
444 }
445
446
447 void GHOST_WindowCarbon::gen2mac(const STR_String& in, Str255 out) const
448 {
449         STR_String tempStr  = in;
450         int num = tempStr.Length();
451         if (num > 255) num = 255;
452         ::memcpy(out+1, tempStr.Ptr(), num);
453         out[0] = num;
454 }
455
456
457 void GHOST_WindowCarbon::mac2gen(const Str255 in, STR_String& out) const
458 {
459         char tmp[256];
460         ::memcpy(tmp, in+1, in[0]);
461         tmp[in[0]] = '\0';
462         out = tmp;
463 }
464
465 void GHOST_WindowCarbon::loadCursor(bool visible, GHOST_TStandardCursor cursor) const
466 {
467         static bool systemCursorVisible = true;
468         
469         if (visible != systemCursorVisible) {
470                 if (visible) {
471                         ::ShowCursor();
472                         systemCursorVisible = true;
473                 }
474                 else {
475                         ::HideCursor();
476                         systemCursorVisible = false;
477                 }
478         }
479
480         if (cursor == GHOST_kStandardCursorCustom && m_customCursor) {
481                 ::SetCursor( m_customCursor );
482         } else {
483                 int carbon_cursor;
484         
485 #define GCMAP(ghostCursor, carbonCursor)        case ghostCursor: carbon_cursor = carbonCursor; break
486                 switch (cursor) {
487                 default:
488                 GCMAP( GHOST_kStandardCursorDefault,                            kThemeArrowCursor);
489                 GCMAP( GHOST_kStandardCursorRightArrow,                         kThemeArrowCursor);
490                 GCMAP( GHOST_kStandardCursorLeftArrow,                          kThemeArrowCursor);
491                 GCMAP( GHOST_kStandardCursorInfo,                                       kThemeArrowCursor);
492                 GCMAP( GHOST_kStandardCursorDestroy,                            kThemeArrowCursor);
493                 GCMAP( GHOST_kStandardCursorHelp,                               kThemeArrowCursor);
494                 GCMAP( GHOST_kStandardCursorCycle,                                      kThemeArrowCursor);
495                 GCMAP( GHOST_kStandardCursorSpray,                                      kThemeArrowCursor);
496                 GCMAP( GHOST_kStandardCursorWait,                                       kThemeWatchCursor);
497                 GCMAP( GHOST_kStandardCursorText,                                       kThemeIBeamCursor);
498                 GCMAP( GHOST_kStandardCursorCrosshair,                          kThemeCrossCursor);
499                 GCMAP( GHOST_kStandardCursorUpDown,                                     kThemeArrowCursor);
500                 GCMAP( GHOST_kStandardCursorLeftRight,                          kThemeResizeLeftRightCursor);
501                 GCMAP( GHOST_kStandardCursorTopSide,                            kThemeArrowCursor);
502                 GCMAP( GHOST_kStandardCursorBottomSide,                         kThemeArrowCursor);
503                 GCMAP( GHOST_kStandardCursorLeftSide,                           kThemeResizeLeftCursor);
504                 GCMAP( GHOST_kStandardCursorRightSide,                          kThemeResizeRightCursor);
505                 GCMAP( GHOST_kStandardCursorTopLeftCorner,                      kThemeArrowCursor);
506                 GCMAP( GHOST_kStandardCursorTopRightCorner,                     kThemeArrowCursor);
507                 GCMAP( GHOST_kStandardCursorBottomRightCorner,          kThemeArrowCursor);
508                 GCMAP( GHOST_kStandardCursorBottomLeftCorner,           kThemeArrowCursor);
509                 };
510 #undef GCMAP
511
512                 ::SetThemeCursor(carbon_cursor);
513         }
514 }
515
516
517 bool GHOST_WindowCarbon::getFullScreenDirty()
518 {
519     return m_fullScreen && m_fullScreenDirty;
520 }
521
522
523 GHOST_TSuccess GHOST_WindowCarbon::setWindowCursorVisibility(bool visible)
524 {
525         if (::FrontWindow() == m_windowRef) {
526                 loadCursor(visible, getCursorShape());
527         }
528         
529         return GHOST_kSuccess;
530 }
531         
532 GHOST_TSuccess GHOST_WindowCarbon::setWindowCursorShape(GHOST_TStandardCursor shape)
533 {
534         if (m_customCursor) {
535                 delete m_customCursor;
536                 m_customCursor = 0;
537         }
538
539         if (::FrontWindow() == m_windowRef) {
540                 loadCursor(getCursorVisibility(), shape);
541         }
542         
543         return GHOST_kSuccess;
544 }
545
546 /** Reverse the bits in a GHOST_TUns16 */
547 static GHOST_TUns16 uns16ReverseBits(GHOST_TUns16 shrt)
548 {
549         shrt= ((shrt>>1)&0x5555) | ((shrt<<1)&0xAAAA);
550         shrt= ((shrt>>2)&0x3333) | ((shrt<<2)&0xCCCC);
551         shrt= ((shrt>>4)&0x0F0F) | ((shrt<<4)&0xF0F0);
552         shrt= ((shrt>>8)&0x00FF) | ((shrt<<8)&0xFF00);
553         return shrt;
554 }
555
556 GHOST_TSuccess GHOST_WindowCarbon::setWindowCustomCursorShape(GHOST_TUns8 bitmap[16][2], GHOST_TUns8 mask[16][2], int hotX, int hotY)
557 {
558         int y;
559         
560         if (m_customCursor) {
561                 delete m_customCursor;
562                 m_customCursor = 0;
563         }
564         
565         m_customCursor = new Cursor;
566         if (!m_customCursor) return GHOST_kFailure;
567         
568         for (y=0; y<16; y++) {
569                 m_customCursor->data[y] = uns16ReverseBits((bitmap[y][0]<<0) | (bitmap[y][1]<<8));
570                 m_customCursor->mask[y] = uns16ReverseBits((mask[y][0]<<0) | (mask[y][1]<<8));
571         }
572         
573         m_customCursor->hotSpot.h = hotX;
574         m_customCursor->hotSpot.v = hotY;
575         
576         if (::FrontWindow() == m_windowRef) {
577                 loadCursor(getCursorVisibility(), GHOST_kStandardCursorCustom);
578         }
579         
580         return GHOST_kSuccess;
581 }