]> git.saurik.com Git - wxWidgets.git/blob - src/osx/carbon/glcanvas.cpp
8268a38ae6856a9c850a75cdc33e4de6ad60d65a
[wxWidgets.git] / src / osx / carbon / glcanvas.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/osx/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 NULL;
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 NULL;
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 GLint bufnummer = win.GetAglBufferName();
261 aglSetInteger(m_glContext, AGL_BUFFER_NAME, &bufnummer);
262 //win.SetLastContext(m_glContext);
263
264 const_cast<wxGLCanvas&>(win).SetViewport();
265
266
267 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
268 if ( UMAGetSystemVersion() >= 0x1050 )
269 {
270 aglSetWindowRef(m_glContext, win.MacGetTopLevelWindowRef());
271 }
272 else
273 #endif
274 {
275 AGLDrawable drawable = (AGLDrawable)GetWindowPort(
276 MAC_WXHWND(win.MacGetTopLevelWindowRef()));
277
278 if ( !aglSetDrawable(m_glContext, drawable) )
279 {
280 wxLogAGLError("aglSetDrawable");
281 return false;
282 }
283 }
284
285 return WXGLSetCurrentContext(m_glContext);
286 }
287
288 // ----------------------------------------------------------------------------
289 // wxGLCanvas
290 // ----------------------------------------------------------------------------
291
292 /*
293
294 sharing contexts under AGL is not straightforward, to quote from
295
296 http://lists.apple.com/archives/mac-opengl/2003/Jan/msg00402.html :
297
298 In Carbon OpenGL (AGL) you would use call aglSetInteger to setup a
299 buffer name and attached each context to that same name. From AGL
300 you can do:
301
302 GLint id = 1;
303
304 ctx1 = aglCreateContext...
305 aglSetInteger(ctx1, AGL_BUFFER_NAME, &id); // create name
306 aglAttachDrawable (ctx1,...); // create surface with associated with
307 name (first time)
308 ctx2 = aglCreateContext...
309 aglSetInteger(ctx2, AGL_BUFFER_NAME, &id); // uses previously created name
310 aglAttachDrawable (ctx2, ...); // uses existing surface with existing name
311
312 AGL Docs say:
313 AGL_BUFFER_NAME
314 params contains one value: a non-negative integer name of the surface to be
315 associated to be with the current context. If this value is non-zero, and a
316 surface of this name is not associated to this drawable, a new surface with
317 this name is created and associated with the context when
318 aglSetDrawable is called subsequently. If this is a previously allocated
319 buffer name within the namespace of the current window (e.g., drawable),
320 that previously allocated surface is associated with the context (e.g., no
321 new surface is created) and the subsequent call to aglSetDrawable will
322 attach that surface. This allows multiple contexts to be attached to a single
323 surface. Using the default buffer name zero, returns to one surface per
324 context behaviour.
325 */
326
327 /*
328 so what I'm doing is to have a dummy aglContext attached to a wxGLCanvas,
329 assign it a buffer number
330 */
331
332
333 bool wxGLCanvas::Create(wxWindow *parent,
334 wxWindowID id,
335 const wxPoint& pos,
336 const wxSize& size,
337 long style,
338 const wxString& name,
339 const int *attribList,
340 const wxPalette& WXUNUSED(palette))
341 {
342 m_needsUpdate = false;
343 m_macCanvasIsShown = false;
344
345 m_glFormat = WXGLChoosePixelFormat(attribList);
346 if ( !m_glFormat )
347 return false;
348
349 if ( !wxWindow::Create(parent, id, pos, size, style, name) )
350 return false;
351
352 m_dummyContext = WXGLCreateContext(m_glFormat, NULL);
353
354 static GLint gCurrentBufferName = 1;
355 m_bufferName = gCurrentBufferName++;
356 aglSetInteger (m_dummyContext, AGL_BUFFER_NAME, &m_bufferName);
357
358 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
359 if ( UMAGetSystemVersion() >= 0x1050 )
360 {
361 aglSetWindowRef(m_dummyContext, MacGetTopLevelWindowRef());
362 }
363 else
364 #endif
365 {
366 AGLDrawable drawable = (AGLDrawable)GetWindowPort(MAC_WXHWND(MacGetTopLevelWindowRef()));
367 aglSetDrawable(m_dummyContext, drawable);
368 }
369
370 m_macCanvasIsShown = true;
371
372 return true;
373 }
374
375 wxGLCanvas::~wxGLCanvas()
376 {
377 if ( m_glFormat )
378 WXGLDestroyPixelFormat(m_glFormat);
379
380 if ( m_dummyContext )
381 WXGLDestroyContext(m_dummyContext);
382 }
383
384 bool wxGLCanvas::SwapBuffers()
385 {
386 WXGLContext context = WXGLGetCurrentContext();
387 wxCHECK_MSG(context, false, wxT("should have current context"));
388
389 aglSwapBuffers(context);
390 return true;
391 }
392
393 void wxGLCanvas::SetViewport()
394 {
395 if ( !m_needsUpdate )
396 return;
397
398 m_needsUpdate = false;
399
400 // AGLContext context = aglGetCurrentContext();
401 // if ( !context )
402 // return;
403
404 // viewport is initially set to entire port, adjust it to just this window
405 int x = 0,
406 y = 0;
407 MacClientToRootWindow(&x , &y);
408
409 int width, height;
410 GetClientSize(&width, &height);
411
412 Rect bounds;
413 GetWindowPortBounds(MAC_WXHWND(MacGetTopLevelWindowRef()) , &bounds);
414
415 GLint parms[4];
416 parms[0] = x;
417 parms[1] = bounds.bottom - bounds.top - ( y + height );
418 parms[2] = width;
419 parms[3] = height;
420
421 // move the buffer rect out of sight if we're hidden
422 if ( !m_macCanvasIsShown )
423 parms[0] += 20000;
424
425 if ( !aglSetInteger(m_dummyContext, AGL_BUFFER_RECT, parms) )
426 wxLogAGLError("aglSetInteger(AGL_BUFFER_RECT)");
427
428 if ( !aglEnable(m_dummyContext, AGL_BUFFER_RECT) )
429 wxLogAGLError("aglEnable(AGL_BUFFER_RECT)");
430
431 if ( !aglUpdateContext(m_dummyContext) )
432 wxLogAGLError("aglUpdateContext");
433 }
434
435 void wxGLCanvas::OnSize(wxSizeEvent& event)
436 {
437 MacUpdateView();
438 event.Skip();
439 }
440
441 void wxGLCanvas::MacUpdateView()
442 {
443 m_needsUpdate = true;
444 Refresh(false);
445 }
446
447 void wxGLCanvas::MacSuperChangedPosition()
448 {
449 MacUpdateView();
450 SetViewport();
451 wxWindow::MacSuperChangedPosition();
452 }
453
454 void wxGLCanvas::MacTopLevelWindowChangedPosition()
455 {
456 MacUpdateView();
457 wxWindow::MacTopLevelWindowChangedPosition();
458 }
459
460 void wxGLCanvas::MacVisibilityChanged()
461 {
462 if ( IsShownOnScreen() != m_macCanvasIsShown )
463 {
464 m_macCanvasIsShown = !m_macCanvasIsShown;
465 MacUpdateView();
466 }
467
468 wxWindowMac::MacVisibilityChanged();
469 }
470
471 #endif // wxUSE_GLCANVAS