]> git.saurik.com Git - wxWidgets.git/blob - src/osx/carbon/glcanvas.cpp
9a6226dd9a74499565ff277d3465fd3112e82bac
[wxWidgets.git] / src / osx / carbon / glcanvas.cpp
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
36 #include "wx/osx/uma.h"
37
38 #include "wx/osx/private.h"
39 #include <AGL/agl.h>
40
41 // ----------------------------------------------------------------------------
42 // helper functions
43 // ----------------------------------------------------------------------------
44
45 static 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 // ----------------------------------------------------------------------------
58 // low level implementation routines
59 // ----------------------------------------------------------------------------
60
61 WXGLContext WXGLCreateContext( WXGLPixelFormat pixelFormat, WXGLContext shareContext )
62 {
63 WXGLContext context = aglCreateContext(pixelFormat, shareContext);
64 if ( !context )
65 wxLogAGLError("aglCreateContext");
66 return context ;
67 }
68
69 void WXGLDestroyContext( WXGLContext context )
70 {
71 if ( context )
72 {
73 if ( !aglDestroyContext(context) )
74 {
75 wxLogAGLError("aglDestroyContext");
76 }
77 }
78 }
79
80 WXGLContext WXGLGetCurrentContext()
81 {
82 return aglGetCurrentContext();
83 }
84
85 bool WXGLSetCurrentContext(WXGLContext context)
86 {
87 if ( !aglSetCurrentContext(context) )
88 {
89 wxLogAGLError("aglSetCurrentContext");
90 return false;
91 }
92
93 return true;
94 }
95
96 void WXGLDestroyPixelFormat( WXGLPixelFormat pixelFormat )
97 {
98 if ( pixelFormat )
99 {
100 aglDestroyPixelFormat(pixelFormat);
101 }
102 }
103
104 WXGLPixelFormat WXGLChoosePixelFormat(const int *attribList)
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
251 // ----------------------------------------------------------------------------
252 // wxGLContext
253 // ----------------------------------------------------------------------------
254
255 bool 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);
266
267 const_cast<wxGLCanvas&>(win).SetViewport();
268
269 if ( !aglSetDrawable(m_glContext, drawable) )
270 {
271 wxLogAGLError("aglSetDrawable");
272 return false;
273 }
274
275 return WXGLSetCurrentContext(m_glContext);
276 }
277
278 // ----------------------------------------------------------------------------
279 // wxGLCanvas
280 // ----------------------------------------------------------------------------
281
282 /*
283
284 sharing contexts under AGL is not straightforward, to quote from
285
286 http://lists.apple.com/archives/mac-opengl/2003/Jan/msg00402.html :
287
288 In Carbon OpenGL (AGL) you would use call aglSetInteger to setup a
289 buffer name and attached each context to that same name. From AGL
290 you can do:
291
292 GLint id = 1;
293
294 ctx1 = aglCreateContext...
295 aglSetInteger(ctx1, AGL_BUFFER_NAME, &id); // create name
296 aglAttachDrawable (ctx1,...); // create surface with associated with
297 name (first time)
298 ctx2 = aglCreateContext...
299 aglSetInteger(ctx2, AGL_BUFFER_NAME, &id); // uses previously created name
300 aglAttachDrawable (ctx2, ...); // uses existing surface with existing name
301
302 AGL Docs say:
303 AGL_BUFFER_NAME
304 params contains one value: a non-negative integer name of the surface to be
305 associated to be with the current context. If this value is non-zero, and a
306 surface of this name is not associated to this drawable, a new surface with
307 this name is created and associated with the context when
308 aglSetDrawable is called subsequently. If this is a previously allocated
309 buffer name within the namespace of the current window (e.g., drawable),
310 that previously allocated surface is associated with the context (e.g., no
311 new surface is created) and the subsequent call to aglSetDrawable will
312 attach that surface. This allows multiple contexts to be attached to a single
313 surface. Using the default buffer name zero, returns to one surface per
314 context behavior.
315 */
316
317 /*
318 so what I'm doing is to have a dummy aglContext attached to a wxGLCanvas,
319 assign it a buffer number
320 */
321
322
323 bool 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
335 m_glFormat = WXGLChoosePixelFormat(attribList);
336 if ( !m_glFormat )
337 return false;
338
339 if ( !wxWindow::Create(parent, id, pos, size, style, name) )
340 return false;
341
342 m_dummyContext = WXGLCreateContext(m_glFormat, NULL);
343
344 static GLint gCurrentBufferName = 1;
345 m_bufferName = gCurrentBufferName++;
346 aglSetInteger (m_dummyContext, AGL_BUFFER_NAME, &m_bufferName);
347
348 AGLDrawable drawable = (AGLDrawable)GetWindowPort(MAC_WXHWND(MacGetTopLevelWindowRef()));
349 aglSetDrawable(m_dummyContext, drawable);
350
351 m_macCanvasIsShown = true;
352
353 return true;
354 }
355
356 wxGLCanvas::~wxGLCanvas()
357 {
358 if ( m_glFormat )
359 WXGLDestroyPixelFormat(m_glFormat);
360
361 if ( m_dummyContext )
362 WXGLDestroyContext(m_dummyContext);
363 }
364
365 bool 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
374 void wxGLCanvas::SetViewport()
375 {
376 if ( !m_needsUpdate )
377 return;
378
379 m_needsUpdate = false;
380
381 // AGLContext context = aglGetCurrentContext();
382 // if ( !context )
383 // return;
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
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;
401
402 // move the buffer rect out of sight if we're hidden
403 if ( !m_macCanvasIsShown )
404 parms[0] += 20000;
405
406 if ( !aglSetInteger(m_dummyContext, AGL_BUFFER_RECT, parms) )
407 wxLogAGLError("aglSetInteger(AGL_BUFFER_RECT)");
408
409 if ( !aglEnable(m_dummyContext, AGL_BUFFER_RECT) )
410 wxLogAGLError("aglEnable(AGL_BUFFER_RECT)");
411
412 if ( !aglUpdateContext(m_dummyContext) )
413 wxLogAGLError("aglUpdateContext");
414 }
415
416 void wxGLCanvas::OnSize(wxSizeEvent& event)
417 {
418 MacUpdateView();
419 event.Skip();
420 }
421
422 void wxGLCanvas::MacUpdateView()
423 {
424 m_needsUpdate = true;
425 Refresh(false);
426 }
427
428 void wxGLCanvas::MacSuperChangedPosition()
429 {
430 MacUpdateView();
431 SetViewport();
432 wxWindow::MacSuperChangedPosition();
433 }
434
435 void wxGLCanvas::MacTopLevelWindowChangedPosition()
436 {
437 MacUpdateView();
438 wxWindow::MacTopLevelWindowChangedPosition();
439 }
440
441 void wxGLCanvas::MacVisibilityChanged()
442 {
443 if ( IsShownOnScreen() != m_macCanvasIsShown )
444 {
445 m_macCanvasIsShown = !m_macCanvasIsShown;
446 MacUpdateView();
447 }
448
449 wxWindowMac::MacVisibilityChanged();
450 }
451
452 #endif // wxUSE_GLCANVAS