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