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