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