replace wx_{const,static,reinterpret}_cast with their standard C++ equivalents
[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 void WXGLDestroyPixelFormat( WXGLPixelFormat pixelFormat )
91 {
92 if ( pixelFormat )
93 {
94 aglDestroyPixelFormat(pixelFormat);
95 }
96 }
97
98 WXGLPixelFormat WXGLChoosePixelFormat(const int *attribList)
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
245 // ----------------------------------------------------------------------------
246 // wxGLContext
247 // ----------------------------------------------------------------------------
248
249 bool 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
261 const_cast<wxGLCanvas&>(win).SetViewport();
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
283 sharing contexts under AGL is not straightforward, to quote from
284
285 http://lists.apple.com/archives/mac-opengl/2003/Jan/msg00402.html :
286
287 In Carbon OpenGL (AGL) you would use call aglSetInteger to setup a
288 buffer name and attached each context to that same name. From AGL
289 you can do:
290
291 GLint id = 1;
292
293 ctx1 = aglCreateContext...
294 aglSetInteger(ctx1, AGL_BUFFER_NAME, &id); // create name
295 aglAttachDrawable (ctx1,...); // create surface with associated with
296 name (first time)
297 ctx2 = aglCreateContext...
298 aglSetInteger(ctx2, AGL_BUFFER_NAME, &id); // uses previously created name
299 aglAttachDrawable (ctx2, ...); // uses existing surface with existing name
300
301 AGL Docs say:
302 AGL_BUFFER_NAME
303 params contains one value: a non-negative integer name of the surface to be
304 associated to be with the current context. If this value is non-zero, and a
305 surface of this name is not associated to this drawable, a new surface with
306 this name is created and associated with the context when
307 aglSetDrawable is called subsequently. If this is a previously allocated
308 buffer name within the namespace of the current window (e.g., drawable),
309 that previously allocated surface is associated with the context (e.g., no
310 new surface is created) and the subsequent call to aglSetDrawable will
311 attach that surface. This allows multiple contexts to be attached to a single
312 surface. Using the default buffer name zero, returns to one surface per
313 context behavior.
314 */
315
316 /*
317 so what I'm doing is to have a dummy aglContext attached to a wxGLCanvas,
318 assign it a buffer number
319 */
320
321
322 bool 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
334 m_glFormat = WXGLChoosePixelFormat(attribList);
335 if ( !m_glFormat )
336 return false;
337
338 if ( !wxWindow::Create(parent, id, pos, size, style, name) )
339 return false;
340
341 m_dummyContext = WXGLCreateContext(m_glFormat, NULL);
342
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);
349
350 m_macCanvasIsShown = true;
351
352 return true;
353 }
354
355 wxGLCanvas::~wxGLCanvas()
356 {
357 if ( m_glFormat )
358 WXGLDestroyPixelFormat(m_glFormat);
359
360 if ( m_dummyContext )
361 WXGLDestroyContext(m_dummyContext);
362 }
363
364 void wxGLCanvas::SetViewport()
365 {
366 if ( !m_needsUpdate )
367 return;
368
369 m_needsUpdate = false;
370
371 // AGLContext context = aglGetCurrentContext();
372 // if ( !context )
373 // return;
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
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;
391
392 // move the buffer rect out of sight if we're hidden
393 if ( !m_macCanvasIsShown )
394 parms[0] += 20000;
395
396 if ( !aglSetInteger(m_dummyContext, AGL_BUFFER_RECT, parms) )
397 wxLogAGLError("aglSetInteger(AGL_BUFFER_RECT)");
398
399 if ( !aglEnable(m_dummyContext, AGL_BUFFER_RECT) )
400 wxLogAGLError("aglEnable(AGL_BUFFER_RECT)");
401
402 if ( !aglUpdateContext(m_dummyContext) )
403 wxLogAGLError("aglUpdateContext");
404 }
405
406 void wxGLCanvas::OnSize(wxSizeEvent& event)
407 {
408 MacUpdateView();
409 event.Skip();
410 }
411
412 void wxGLCanvas::MacUpdateView()
413 {
414 m_needsUpdate = true;
415 Refresh(false);
416 }
417
418 void wxGLCanvas::MacSuperChangedPosition()
419 {
420 MacUpdateView();
421 SetViewport();
422 wxWindow::MacSuperChangedPosition();
423 }
424
425 void wxGLCanvas::MacTopLevelWindowChangedPosition()
426 {
427 MacUpdateView();
428 wxWindow::MacTopLevelWindowChangedPosition();
429 }
430
431 void wxGLCanvas::MacVisibilityChanged()
432 {
433 if ( IsShownOnScreen() != m_macCanvasIsShown )
434 {
435 m_macCanvasIsShown = !m_macCanvasIsShown;
436 MacUpdateView();
437 }
438
439 wxWindowMac::MacVisibilityChanged();
440 }
441
442 #endif // wxUSE_GLCANVAS