3 * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
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
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.
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.
22 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
23 * All rights reserved.
25 * The Original Code is: all of this file.
27 * Contributor(s): none yet.
29 * ***** END GPL/BL DUAL LICENSE BLOCK *****
35 * Copyright (C) 2001 NaN Technologies B.V.
36 * @author Maarten Gribnau
44 #include "GHOST_WindowCarbon.h"
45 #include "GHOST_Debug.h"
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
52 static const GLint sPreferredFormatWindow[9] = {
54 AGL_DOUBLEBUFFER, GL_TRUE,
60 static const GLint sPreferredFormatFullScreen[7] = {
69 WindowRef ugly_hack=NULL;
71 GHOST_WindowCarbon::GHOST_WindowCarbon(
72 const STR_String& title,
77 GHOST_TWindowState state,
78 GHOST_TDrawingContextType type,
79 const bool stereoVisual
81 GHOST_Window(title, left, top, width, height, state, GHOST_kDrawingContextTypeNone),
86 m_fullScreenDirty(false)
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);
95 m_windowRef = ::NewCWindow(
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
105 m_grafPtr = ::GetWindowPort(m_windowRef);
106 setDrawingContextType(type);
107 updateDrawingContext();
108 activateDrawingContext();
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);
120 Rect bnds = { top, left, top+height, left+width };
121 gen2mac("", title255);
122 m_windowRef = ::NewCWindow(
124 &bnds, // Bounding rectangle of the window
125 title255, // Title of the window
126 0, // Window initially visible
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
132 //GHOST_PRINT("GHOST_WindowCarbon::GHOST_WindowCarbon(): creating full-screen OpenGL context\n");
133 setDrawingContextType(GHOST_kDrawingContextTypeOpenGL);
134 updateDrawingContext();
135 activateDrawingContext();
140 GHOST_WindowCarbon::~GHOST_WindowCarbon()
142 if (m_customCursor) delete m_customCursor;
144 if(ugly_hack==m_windowRef) ugly_hack= NULL;
146 // printf("GHOST_WindowCarbon::~GHOST_WindowCarbon(): removing drawing context\n");
147 if(ugly_hack==NULL) setDrawingContextType(GHOST_kDrawingContextTypeNone);
149 ::DisposeWindow(m_windowRef);
154 bool GHOST_WindowCarbon::getValid() const
158 valid = (m_windowRef != 0) && (m_grafPtr != 0) && ::IsValidWindowPtr(m_windowRef);
167 void GHOST_WindowCarbon::setTitle(const STR_String& title)
169 GHOST_ASSERT(getValid(), "GHOST_WindowCarbon::setTitle(): window invalid")
171 gen2mac(title, title255);
172 ::SetWTitle(m_windowRef, title255);
176 void GHOST_WindowCarbon::getTitle(STR_String& title) const
178 GHOST_ASSERT(getValid(), "GHOST_WindowCarbon::getTitle(): window invalid")
180 ::GetWTitle(m_windowRef, title255);
181 mac2gen(title255, title);
185 void GHOST_WindowCarbon::getWindowBounds(GHOST_Rect& bounds) const
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;
198 void GHOST_WindowCarbon::getClientBounds(GHOST_Rect& bounds) const
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;
208 // Subtract gutter height from bottom
209 #ifdef GHOST_DRAW_CARBON_GUTTER
210 if ((bounds.m_b - bounds.m_t) > s_sizeRectSize)
212 bounds.m_b -= s_sizeRectSize;
216 bounds.m_t = bounds.m_b;
218 #endif //GHOST_DRAW_CARBON_GUTTER
222 GHOST_TSuccess GHOST_WindowCarbon::setClientWidth(GHOST_TUns32 width)
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);
230 return GHOST_kSuccess;
234 GHOST_TSuccess GHOST_WindowCarbon::setClientHeight(GHOST_TUns32 height)
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);
243 #else //GHOST_DRAW_CARBON_GUTTER
244 if (((GHOST_TUns32)cBnds.getHeight()) != height) {
245 ::SizeWindow(m_windowRef, cBnds.getWidth(), height, true);
247 #endif //GHOST_DRAW_CARBON_GUTTER
248 return GHOST_kSuccess;
252 GHOST_TSuccess GHOST_WindowCarbon::setClientSize(GHOST_TUns32 width, GHOST_TUns32 height)
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);
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);
267 #endif //GHOST_DRAW_CARBON_GUTTER
268 return GHOST_kSuccess;
272 GHOST_TWindowState GHOST_WindowCarbon::getState() const
274 GHOST_ASSERT(getValid(), "GHOST_WindowCarbon::getState(): window invalid")
275 GHOST_TWindowState state;
276 if (::IsWindowVisible(m_windowRef)) {
277 state = GHOST_kWindowStateMinimized;
279 else if (::IsWindowInStandardState(m_windowRef, nil, nil)) {
280 state = GHOST_kWindowStateMaximized;
283 state = GHOST_kWindowStateNormal;
289 void GHOST_WindowCarbon::screenToClient(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
291 GHOST_ASSERT(getValid(), "GHOST_WindowCarbon::screenToClient(): window invalid")
297 ::SetPort(m_grafPtr);
298 ::GlobalToLocal(&point);
305 void GHOST_WindowCarbon::clientToScreen(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
307 GHOST_ASSERT(getValid(), "GHOST_WindowCarbon::clientToScreen(): window invalid")
313 ::SetPort(m_grafPtr);
314 ::LocalToGlobal(&point);
321 GHOST_TSuccess GHOST_WindowCarbon::setState(GHOST_TWindowState state)
323 GHOST_ASSERT(getValid(), "GHOST_WindowCarbon::setState(): window invalid")
325 case GHOST_kWindowStateMinimized:
326 ::HideWindow(m_windowRef);
328 case GHOST_kWindowStateMaximized:
329 case GHOST_kWindowStateNormal:
331 ::ShowWindow(m_windowRef);
334 return GHOST_kSuccess;
338 GHOST_TSuccess GHOST_WindowCarbon::setOrder(GHOST_TWindowOrder order)
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);
346 /* doesnt work if you do this with a mouseclick */
347 ::SendBehind(m_windowRef, nil);
349 return GHOST_kSuccess;
353 GHOST_TSuccess GHOST_WindowCarbon::swapBuffers()
355 GHOST_TSuccess succeeded = GHOST_kSuccess;
356 if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) {
358 ::aglSwapBuffers(m_aglCtx);
361 succeeded = GHOST_kFailure;
367 GHOST_TSuccess GHOST_WindowCarbon::updateDrawingContext()
369 GHOST_TSuccess succeeded = GHOST_kSuccess;
370 if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) {
372 ::aglUpdateContext(m_aglCtx);
375 succeeded = GHOST_kFailure;
381 GHOST_TSuccess GHOST_WindowCarbon::activateDrawingContext()
383 GHOST_TSuccess succeeded = GHOST_kSuccess;
384 if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) {
386 ::aglSetCurrentContext(m_aglCtx);
387 #ifdef GHOST_DRAW_CARBON_GUTTER
388 // Restrict drawing to non-gutter area
389 ::aglEnable(m_aglCtx, AGL_BUFFER_RECT);
391 getClientBounds(bnds);
395 bnds.m_t+s_sizeRectSize,
399 GLboolean result = ::aglSetInteger(m_aglCtx, AGL_BUFFER_RECT, b);
400 #endif //GHOST_DRAW_CARBON_GUTTER
403 succeeded = GHOST_kFailure;
410 GHOST_TSuccess GHOST_WindowCarbon::installDrawingContext(GHOST_TDrawingContextType type)
412 GHOST_TSuccess success = GHOST_kFailure;
414 case GHOST_kDrawingContextTypeOpenGL:
416 if (!getValid()) break;
418 AGLPixelFormat pixelFormat;
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;
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;
436 if (success == GHOST_kSuccess) {
437 GHOST_PRINT("GHOST_WindowCarbon::installDrawingContext(): init full-screen OpenGL succeeded\n");
440 GHOST_PRINT("GHOST_WindowCarbon::installDrawingContext(): init full-screen OpenGL failed\n");
444 ::aglDestroyPixelFormat(pixelFormat);
448 case GHOST_kDrawingContextTypeNone:
449 success = GHOST_kSuccess;
459 GHOST_TSuccess GHOST_WindowCarbon::removeDrawingContext()
461 GHOST_TSuccess success = GHOST_kFailure;
462 switch (m_drawingContextType) {
463 case GHOST_kDrawingContextTypeOpenGL:
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;
473 case GHOST_kDrawingContextTypeNone:
474 success = GHOST_kSuccess;
483 GHOST_TSuccess GHOST_WindowCarbon::invalidate()
485 GHOST_ASSERT(getValid(), "GHOST_WindowCarbon::invalidate(): window invalid")
488 ::GetPortBounds(m_grafPtr, &rect);
489 ::InvalWindowRect(m_windowRef, &rect);
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;
502 return GHOST_kSuccess;
506 void GHOST_WindowCarbon::gen2mac(const STR_String& in, Str255 out) const
508 STR_String tempStr = in;
509 int num = tempStr.Length();
510 if (num > 255) num = 255;
511 ::memcpy(out+1, tempStr.Ptr(), num);
516 void GHOST_WindowCarbon::mac2gen(const Str255 in, STR_String& out) const
519 ::memcpy(tmp, in+1, in[0]);
524 void GHOST_WindowCarbon::loadCursor(bool visible, GHOST_TStandardCursor cursor) const
526 static bool systemCursorVisible = true;
528 if (visible != systemCursorVisible) {
531 systemCursorVisible = true;
535 systemCursorVisible = false;
539 if (cursor == GHOST_kStandardCursorCustom && m_customCursor) {
540 ::SetCursor( m_customCursor );
544 #define GCMAP(ghostCursor, carbonCursor) case ghostCursor: carbon_cursor = carbonCursor; break
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);
571 ::SetThemeCursor(carbon_cursor);
576 bool GHOST_WindowCarbon::getFullScreenDirty()
578 return m_fullScreen && m_fullScreenDirty;
582 GHOST_TSuccess GHOST_WindowCarbon::setWindowCursorVisibility(bool visible)
584 if (::FrontWindow() == m_windowRef) {
585 loadCursor(visible, getCursorShape());
588 return GHOST_kSuccess;
591 GHOST_TSuccess GHOST_WindowCarbon::setWindowCursorShape(GHOST_TStandardCursor shape)
593 if (m_customCursor) {
594 delete m_customCursor;
598 if (::FrontWindow() == m_windowRef) {
599 loadCursor(getCursorVisibility(), shape);
602 return GHOST_kSuccess;
605 /** Reverse the bits in a GHOST_TUns8 */
606 static GHOST_TUns8 uns8ReverseBits(GHOST_TUns8 ch)
608 ch= ((ch>>1)&0x55) | ((ch<<1)&0xAA);
609 ch= ((ch>>2)&0x33) | ((ch<<2)&0xCC);
610 ch= ((ch>>4)&0x0F) | ((ch<<4)&0xF0);
614 /** Reverse the bits in a GHOST_TUns16 */
615 static GHOST_TUns16 uns16ReverseBits(GHOST_TUns16 shrt)
617 shrt= ((shrt>>1)&0x5555) | ((shrt<<1)&0xAAAA);
618 shrt= ((shrt>>2)&0x3333) | ((shrt<<2)&0xCCCC);
619 shrt= ((shrt>>4)&0x0F0F) | ((shrt<<4)&0xF0F0);
620 shrt= ((shrt>>8)&0x00FF) | ((shrt<<8)&0xFF00);
624 GHOST_TSuccess GHOST_WindowCarbon::setWindowCustomCursorShape(GHOST_TUns8 *bitmap, GHOST_TUns8 *mask,
625 int sizex, int sizey, int hotX, int hotY, int fg_color, int bg_color)
629 if (m_customCursor) {
630 delete m_customCursor;
634 m_customCursor = new Cursor;
635 if (!m_customCursor) return GHOST_kFailure;
637 for (y=0; y<16; y++) {
638 m_customCursor->data[y] = uns16ReverseBits((bitmap[2*y]<<0) | (bitmap[2*y+1]<<8));
639 m_customCursor->mask[y] = uns16ReverseBits((mask[2*y]<<0) | (mask[2*y+1]<<8));
642 m_customCursor->hotSpot.h = hotX;
643 m_customCursor->hotSpot.v = hotY;
645 if (::FrontWindow() == m_windowRef) {
646 loadCursor(getCursorVisibility(), GHOST_kStandardCursorCustom);
649 return GHOST_kSuccess;
652 GHOST_TSuccess GHOST_WindowCarbon::setWindowCustomCursorShape(GHOST_TUns8 bitmap[16][2],
653 GHOST_TUns8 mask[16][2], int hotX, int hotY)
655 setWindowCustomCursorShape((GHOST_TUns8*)bitmap, (GHOST_TUns8*) mask, 16, 16, hotX, hotY, 0, 1);