]> git.saurik.com Git - wxWidgets.git/blame - src/osx/carbon/glcanvas.cpp
Review/simplify/cleanup MDI classes for all platforms and introduce base
[wxWidgets.git] / src / osx / carbon / glcanvas.cpp
CommitLineData
489468fe
SC
1///////////////////////////////////////////////////////////////////////////////
2// Name: src/mac/carbon/glcanvas.cpp
3// Purpose: wxGLCanvas, for using OpenGL with wxWidgets under Macintosh
4// Author: Stefan Csomor
5// Modified by:
6// Created: 1998-01-01
7// RCS-ID: $Id$
8// Copyright: (c) Stefan Csomor
9// Licence: wxWindows licence
10///////////////////////////////////////////////////////////////////////////////
11
12// ============================================================================
13// declarations
14// ============================================================================
15
16// ----------------------------------------------------------------------------
17// headers
18// ----------------------------------------------------------------------------
19
20#include "wx/wxprec.h"
21
22#if defined(__BORLANDC__)
23 #pragma hdrstop
24#endif
25
26#if wxUSE_GLCANVAS
27
28#include "wx/glcanvas.h"
29
30#ifndef WX_PRECOMP
31 #include "wx/frame.h"
32 #include "wx/log.h"
33 #include "wx/settings.h"
34#endif
35
1f0c8f31 36#include "wx/osx/uma.h"
489468fe 37
1f0c8f31 38#include "wx/osx/private.h"
524c47aa 39#include <AGL/agl.h>
489468fe
SC
40
41// ----------------------------------------------------------------------------
42// helper functions
43// ----------------------------------------------------------------------------
44
45static void wxLogAGLError(const char *func)
46{
47 const int err = aglGetError();
48
49 wxLogError(_("OpenGL function \"%s\" failed: %s (error %d)"),
50 func, aglErrorString(err), err);
51}
52
53// ============================================================================
54// implementation
55// ============================================================================
56
57// ----------------------------------------------------------------------------
524c47aa 58// low level implementation routines
489468fe
SC
59// ----------------------------------------------------------------------------
60
524c47aa 61WXGLContext WXGLCreateContext( WXGLPixelFormat pixelFormat, WXGLContext shareContext )
489468fe 62{
524c47aa
SC
63 WXGLContext context = aglCreateContext(pixelFormat, shareContext);
64 if ( !context )
489468fe 65 wxLogAGLError("aglCreateContext");
524c47aa 66 return context ;
489468fe
SC
67}
68
524c47aa 69void WXGLDestroyContext( WXGLContext context )
489468fe 70{
524c47aa 71 if ( context )
489468fe 72 {
524c47aa
SC
73 if ( !aglDestroyContext(context) )
74 {
489468fe 75 wxLogAGLError("aglDestroyContext");
524c47aa 76 }
489468fe
SC
77 }
78}
79
524c47aa 80void WXGLSwapBuffers( WXGLContext context )
489468fe 81{
524c47aa 82 aglSwapBuffers(context);
489468fe
SC
83}
84
524c47aa 85WXGLContext WXGLGetCurrentContext()
489468fe 86{
524c47aa 87 return aglGetCurrentContext();
489468fe
SC
88}
89
524c47aa 90void WXGLDestroyPixelFormat( WXGLPixelFormat pixelFormat )
489468fe 91{
524c47aa
SC
92 if ( pixelFormat )
93 {
94 aglDestroyPixelFormat(pixelFormat);
95 }
489468fe
SC
96}
97
524c47aa 98WXGLPixelFormat WXGLChoosePixelFormat(const int *attribList)
489468fe
SC
99{
100 GLint data[512];
101 const GLint defaultAttribs[] =
102 {
103 AGL_RGBA,
104 AGL_DOUBLEBUFFER,
105 AGL_MINIMUM_POLICY, // never choose less than requested
106 AGL_DEPTH_SIZE, 1, // use largest available depth buffer
107 AGL_RED_SIZE, 1,
108 AGL_GREEN_SIZE, 1,
109 AGL_BLUE_SIZE, 1,
110 AGL_ALPHA_SIZE, 0,
111 AGL_NONE
112 };
113
114 const GLint *attribs;
115 if ( !attribList )
116 {
117 attribs = defaultAttribs;
118 }
119 else
120 {
121 unsigned p = 0;
122 data[p++] = AGL_MINIMUM_POLICY; // make _SIZE tags behave more like GLX
123
124 for ( unsigned arg = 0; attribList[arg] !=0 && p < WXSIZEOF(data); )
125 {
126 switch ( attribList[arg++] )
127 {
128 case WX_GL_RGBA:
129 data[p++] = AGL_RGBA;
130 break;
131
132 case WX_GL_BUFFER_SIZE:
133 data[p++] = AGL_BUFFER_SIZE;
134 data[p++] = attribList[arg++];
135 break;
136
137 case WX_GL_LEVEL:
138 data[p++]=AGL_LEVEL;
139 data[p++]=attribList[arg++];
140 break;
141
142 case WX_GL_DOUBLEBUFFER:
143 data[p++] = AGL_DOUBLEBUFFER;
144 break;
145
146 case WX_GL_STEREO:
147 data[p++] = AGL_STEREO;
148 break;
149
150 case WX_GL_AUX_BUFFERS:
151 data[p++] = AGL_AUX_BUFFERS;
152 data[p++] = attribList[arg++];
153 break;
154
155 case WX_GL_MIN_RED:
156 data[p++] = AGL_RED_SIZE;
157 data[p++] = attribList[arg++];
158 break;
159
160 case WX_GL_MIN_GREEN:
161 data[p++] = AGL_GREEN_SIZE;
162 data[p++] = attribList[arg++];
163 break;
164
165 case WX_GL_MIN_BLUE:
166 data[p++] = AGL_BLUE_SIZE;
167 data[p++] = attribList[arg++];
168 break;
169
170 case WX_GL_MIN_ALPHA:
171 data[p++] = AGL_ALPHA_SIZE;
172 data[p++] = attribList[arg++];
173 break;
174
175 case WX_GL_DEPTH_SIZE:
176 data[p++] = AGL_DEPTH_SIZE;
177 data[p++] = attribList[arg++];
178 break;
179
180 case WX_GL_STENCIL_SIZE:
181 data[p++] = AGL_STENCIL_SIZE;
182 data[p++] = attribList[arg++];
183 break;
184
185 case WX_GL_MIN_ACCUM_RED:
186 data[p++] = AGL_ACCUM_RED_SIZE;
187 data[p++] = attribList[arg++];
188 break;
189
190 case WX_GL_MIN_ACCUM_GREEN:
191 data[p++] = AGL_ACCUM_GREEN_SIZE;
192 data[p++] = attribList[arg++];
193 break;
194
195 case WX_GL_MIN_ACCUM_BLUE:
196 data[p++] = AGL_ACCUM_BLUE_SIZE;
197 data[p++] = attribList[arg++];
198 break;
199
200 case WX_GL_MIN_ACCUM_ALPHA:
201 data[p++] = AGL_ACCUM_ALPHA_SIZE;
202 data[p++] = attribList[arg++];
203 break;
204
205 case WX_GL_SAMPLE_BUFFERS:
206 if ( !wxGLCanvas::IsAGLMultiSampleAvailable() )
207 {
208 if ( !attribList[arg++] )
209 break;
210
211 return false;
212 }
213
214 data[p++] = AGL_SAMPLE_BUFFERS_ARB;
215 if ( (data[p++] = attribList[arg++]) == true )
216 {
217 // don't use software fallback
218 data[p++] = AGL_NO_RECOVERY;
219 }
220 break;
221
222 case WX_GL_SAMPLES:
223 if ( !wxGLCanvas::IsAGLMultiSampleAvailable() )
224 {
225 if ( !attribList[arg++] )
226 break;
227
228 return false;
229 }
230
231 data[p++] = AGL_SAMPLES_ARB;
232 data[p++] = attribList[arg++];
233 break;
234 }
235 }
236
237 data[p] = AGL_NONE;
238
239 attribs = data;
240 }
241
242 return aglChoosePixelFormat(NULL, 0, attribs);
243}
244
524c47aa
SC
245// ----------------------------------------------------------------------------
246// wxGLContext
247// ----------------------------------------------------------------------------
248
249bool wxGLContext::SetCurrent(const wxGLCanvas& win) const
250{
251 if ( !m_glContext )
252 return false;
253
254 AGLDrawable drawable = (AGLDrawable)GetWindowPort(
255 MAC_WXHWND(win.MacGetTopLevelWindowRef()));
256
257 GLint bufnummer = win.GetAglBufferName();
258 aglSetInteger(m_glContext, AGL_BUFFER_NAME, &bufnummer);
259 //win.SetLastContext(m_glContext);
260
5c33522f 261 const_cast<wxGLCanvas&>(win).SetViewport();
524c47aa
SC
262
263 if ( !aglSetDrawable(m_glContext, drawable) )
264 {
265 wxLogAGLError("aglSetDrawable");
266 return false;
267 }
268
269 if ( !aglSetCurrentContext(m_glContext) )
270 {
271 wxLogAGLError("aglSetCurrentContext");
272 return false;
273 }
274 return true;
275}
276
277// ----------------------------------------------------------------------------
278// wxGLCanvas
279// ----------------------------------------------------------------------------
280
281/*
282
283sharing contexts under AGL is not straightforward, to quote from
284
285http://lists.apple.com/archives/mac-opengl/2003/Jan/msg00402.html :
286
287In Carbon OpenGL (AGL) you would use call aglSetInteger to setup a
288buffer name and attached each context to that same name. From AGL
289you can do:
290
291GLint id = 1;
292
293ctx1 = aglCreateContext...
294aglSetInteger(ctx1, AGL_BUFFER_NAME, &id); // create name
295aglAttachDrawable (ctx1,...); // create surface with associated with
296name (first time)
297ctx2 = aglCreateContext...
298aglSetInteger(ctx2, AGL_BUFFER_NAME, &id); // uses previously created name
299aglAttachDrawable (ctx2, ...); // uses existing surface with existing name
300
301AGL Docs say:
302AGL_BUFFER_NAME
303params contains one value: a non-negative integer name of the surface to be
304associated to be with the current context. If this value is non-zero, and a
305surface of this name is not associated to this drawable, a new surface with
306this name is created and associated with the context when
307aglSetDrawable is called subsequently. If this is a previously allocated
308buffer name within the namespace of the current window (e.g., drawable),
309that previously allocated surface is associated with the context (e.g., no
310new surface is created) and the subsequent call to aglSetDrawable will
311attach that surface. This allows multiple contexts to be attached to a single
312surface. Using the default buffer name zero, returns to one surface per
313context behavior.
314*/
315
316/*
317so what I'm doing is to have a dummy aglContext attached to a wxGLCanvas,
318assign it a buffer number
319*/
320
321
489468fe
SC
322bool wxGLCanvas::Create(wxWindow *parent,
323 wxWindowID id,
324 const wxPoint& pos,
325 const wxSize& size,
326 long style,
327 const wxString& name,
328 const int *attribList,
329 const wxPalette& WXUNUSED(palette))
330{
331 m_needsUpdate = false;
332 m_macCanvasIsShown = false;
333
524c47aa
SC
334 m_glFormat = WXGLChoosePixelFormat(attribList);
335 if ( !m_glFormat )
489468fe
SC
336 return false;
337
338 if ( !wxWindow::Create(parent, id, pos, size, style, name) )
339 return false;
340
524c47aa 341 m_dummyContext = WXGLCreateContext(m_glFormat, NULL);
489468fe 342
524c47aa
SC
343 static GLint gCurrentBufferName = 1;
344 m_bufferName = gCurrentBufferName++;
345 aglSetInteger (m_dummyContext, AGL_BUFFER_NAME, &m_bufferName);
346
347 AGLDrawable drawable = (AGLDrawable)GetWindowPort(MAC_WXHWND(MacGetTopLevelWindowRef()));
348 aglSetDrawable(m_dummyContext, drawable);
489468fe 349
524c47aa 350 m_macCanvasIsShown = true;
489468fe
SC
351
352 return true;
353}
354
524c47aa 355wxGLCanvas::~wxGLCanvas()
489468fe 356{
524c47aa
SC
357 if ( m_glFormat )
358 WXGLDestroyPixelFormat(m_glFormat);
359
360 if ( m_dummyContext )
361 WXGLDestroyContext(m_dummyContext);
489468fe
SC
362}
363
364void wxGLCanvas::SetViewport()
365{
366 if ( !m_needsUpdate )
367 return;
368
369 m_needsUpdate = false;
370
524c47aa
SC
371// AGLContext context = aglGetCurrentContext();
372// if ( !context )
373// return;
489468fe
SC
374
375 // viewport is initially set to entire port, adjust it to just this window
376 int x = 0,
377 y = 0;
378 MacClientToRootWindow(&x , &y);
379
380 int width, height;
381 GetClientSize(&width, &height);
382
383 Rect bounds;
384 GetWindowPortBounds(MAC_WXHWND(MacGetTopLevelWindowRef()) , &bounds);
385
489468fe
SC
386 GLint parms[4];
387 parms[0] = x;
388 parms[1] = bounds.bottom - bounds.top - ( y + height );
389 parms[2] = width;
390 parms[3] = height;
489468fe
SC
391
392 // move the buffer rect out of sight if we're hidden
393 if ( !m_macCanvasIsShown )
394 parms[0] += 20000;
395
524c47aa 396 if ( !aglSetInteger(m_dummyContext, AGL_BUFFER_RECT, parms) )
489468fe
SC
397 wxLogAGLError("aglSetInteger(AGL_BUFFER_RECT)");
398
524c47aa 399 if ( !aglEnable(m_dummyContext, AGL_BUFFER_RECT) )
489468fe
SC
400 wxLogAGLError("aglEnable(AGL_BUFFER_RECT)");
401
524c47aa 402 if ( !aglUpdateContext(m_dummyContext) )
489468fe
SC
403 wxLogAGLError("aglUpdateContext");
404}
405
406void wxGLCanvas::OnSize(wxSizeEvent& event)
407{
408 MacUpdateView();
409 event.Skip();
410}
411
412void wxGLCanvas::MacUpdateView()
413{
414 m_needsUpdate = true;
415 Refresh(false);
416}
417
418void wxGLCanvas::MacSuperChangedPosition()
419{
420 MacUpdateView();
524c47aa 421 SetViewport();
489468fe
SC
422 wxWindow::MacSuperChangedPosition();
423}
424
425void wxGLCanvas::MacTopLevelWindowChangedPosition()
426{
427 MacUpdateView();
428 wxWindow::MacTopLevelWindowChangedPosition();
429}
430
431void wxGLCanvas::MacVisibilityChanged()
432{
433 if ( IsShownOnScreen() != m_macCanvasIsShown )
434 {
435 m_macCanvasIsShown = !m_macCanvasIsShown;
436 MacUpdateView();
437 }
438
439 wxWindowMac::MacVisibilityChanged();
440}
441
489468fe 442#endif // wxUSE_GLCANVAS