1 unit uglcContextGtk2GLX;
\r
3 { Package: OpenGLCore
\r
4 Prefix: glc - OpenGL Core
\r
5 Beschreibung: diese Unit enthält eine Klassen-Kapselung für OpenGL Kontexte für Linux
\r
6 Hint: diese Unit sollte niemals direkt genutzt werden (siehe uglcContext) }
\r
11 SysUtils, Controls, uglcContext, LCLType, XUtil, XLib, gdk2x, gtk2, gdk2, dglOpenGL,
\r
12 LMessages, uglcContextGtkCustomVisual;
\r
15 EGLXError = class(EGLError);
\r
17 TRenderControl = class(TCustomVisualControl)
\r
19 fTarget: TWinControl;
\r
21 procedure WndProc(var Message: TLMessage); override;
\r
23 property Target: TWinControl read fTarget write fTarget;
\r
26 { TglcContextGtk2GLX }
\r
28 TglcContextGtk2GLX = class(TglcContext)
\r
30 FVisual: PXVisualInfo;
\r
32 FWidget: PGtkWidget;
\r
33 FContext: GLXContext;
\r
34 FRenderControl: TRenderControl;
\r
35 procedure UpdateVisual(const aControl: TWinControl);
\r
37 procedure OpenContext; override;
\r
39 constructor Create(const aControl: TWinControl; const aPixelFormatSettings: TglcContextPixelFormatSettings); override; overload;
\r
40 constructor Create(const aControl: TWinControl; const aPixelFormatSettings: TglcContextPixelFormatSettings; const aVersionSettings: TglcContextVersionSettings); override; overload;
\r
41 destructor Destroy; override;
\r
43 procedure CloseContext; override;
\r
44 procedure Activate; override;
\r
45 procedure Deactivate; override;
\r
46 function IsActive: boolean; override;
\r
47 procedure SwapBuffers; override;
\r
48 procedure SetSwapInterval(const aInterval: GLint); override;
\r
49 procedure Share(const aContext: TglcContext); override;
\r
51 class function ChangeDisplaySettings(const aWidth, aHeight,
\r
52 aBitPerPixel, aFreq: Integer; const aFlags: TglcDisplayFlags): Boolean; override;
\r
53 class function IsAnyContextActive: boolean; override;
\r
59 TGLIntArray = packed array of GLInt;
\r
61 {$region messages -fold}
\r
62 procedure TRenderControl.WndProc(var Message: TLMessage);
\r
86 LM_LBUTTONTRIPLECLK,
\r
88 LM_MBUTTONTRIPLECLK,
\r
90 LM_RBUTTONTRIPLECLK,
\r
94 LM_XBUTTONTRIPLECLK,
\r
124 //LM_GETTEXTLENGTH,
\r
133 //LM_WINDOWPOSCHANGING,
\r
134 //LM_WINDOWPOSCHANGED,
\r
147 LM_NCLBUTTONDBLCLK,
\r
159 //LM_CTLCOLORMSGBOX,
\r
161 //LM_CTLCOLORLISTBOX,
\r
164 //LM_CTLCOLORSCROLLBAR,
\r
165 //LM_CTLCOLORSTATIC,
\r
181 //LM_CAPTURECHANGED,
\r
188 //LM_CONFIGUREEVENT,
\r
200 //CM_PARENTFONTCHANGED,
\r
201 //CM_PARENTCOLORCHANGED,
\r
203 //CM_VISIBLECHANGED,
\r
204 //CM_ENABLEDCHANGED,
\r
207 //CM_CURSORCHANGED,
\r
212 //CM_APPSYSCOMMAND,
\r
213 //CM_BUTTONPRESSED,
\r
214 //CM_SHOWINGCHANGED,
\r
217 //CM_DESIGNHITTEST,
\r
219 //CM_WANTSPECIALKEY,
\r
222 //CM_TABSTOPCHANGED,
\r
224 //CM_CONTROLLISTCHANGE,
\r
228 //CM_SYSFONTCHANGED,
\r
229 //CM_CONTROLCHANGE,
\r
231 //CM_BORDERCHANGED,
\r
232 //CM_BIDIMODECHANGED,
\r
233 //CM_PARENTBIDIMODECHANGED,
\r
234 //CM_ALLCHILDRENFLIPPED,
\r
236 //CM_ACTIONEXECUTE,
\r
237 //CM_HINTSHOWPAUSE,
\r
238 //CM_DOCKNOTIFICATION,
\r
240 //CM_APPSHOWBTNGLYPHCHANGED,
\r
241 //CM_APPSHOWMENUGLYPHCHANGED,
\r
250 //CN_CTLCOLORLISTBOX,
\r
251 //CN_CTLCOLORMSGBOX,
\r
252 //CN_CTLCOLORSCROLLBAR,
\r
253 //CN_CTLCOLORSTATIC,
\r
269 if Assigned(fTarget) then
\r
270 Message.Result := fTarget.Perform(Message.msg, Message.wParam, Message.lParam);
\r
273 inherited WndProc(Message);
\r
278 function CreateOpenGLContextAttrList(UseFB: boolean; pf: TglcContextPixelFormatSettings): TGLIntArray;
\r
282 procedure Add(i: integer);
\r
284 SetLength(Result, p+1);
\r
289 procedure CreateList;
\r
291 if UseFB then begin Add(GLX_X_RENDERABLE); Add(1); end;
\r
292 if pf.DoubleBuffered then begin
\r
293 if UseFB then begin
\r
294 Add(GLX_DOUBLEBUFFER); Add(1);
\r
296 Add(GLX_DOUBLEBUFFER);
\r
298 if not UseFB and (pf.ColorBits>24) then Add(GLX_RGBA);
\r
299 if UseFB then begin
\r
300 Add(GLX_DRAWABLE_TYPE);
\r
301 Add(GLX_WINDOW_BIT);
\r
303 Add(GLX_RED_SIZE); Add(8);
\r
304 Add(GLX_GREEN_SIZE); Add(8);
\r
305 Add(GLX_BLUE_SIZE); Add(8);
\r
306 if pf.ColorBits>24 then
\r
307 Add(GLX_ALPHA_SIZE); Add(8);
\r
308 Add(GLX_DEPTH_SIZE); Add(pf.DepthBits);
\r
309 Add(GLX_STENCIL_SIZE); Add(pf.StencilBits);
\r
310 Add(GLX_AUX_BUFFERS); Add(pf.AUXBuffers);
\r
312 if pf.MultiSampling > 1 then begin
\r
313 Add(GLX_SAMPLE_BUFFERS_ARB); Add(1);
\r
314 Add(GLX_SAMPLES_ARB); Add(pf.MultiSampling);
\r
317 Add(0); { 0 = X.None (be careful: GLX_NONE is something different) }
\r
321 SetLength(Result, 0);
\r
326 function FBglXChooseVisual(dpy:PDisplay; screen:longint; attrib_list:Plongint):PXVisualInfo;
\r
328 PGLXFBConfig = ^GLXFBConfig;
\r
330 FBConfigsCount: integer;
\r
331 FBConfigs: PGLXFBConfig;
\r
332 FBConfig: GLXFBConfig;
\r
336 FBConfigs:= glXChooseFBConfig(dpy, screen, attrib_list, @FBConfigsCount);
\r
337 if FBConfigsCount = 0 then
\r
340 { just choose the first FB config from the FBConfigs list.
\r
341 More involved selection possible. }
\r
342 FBConfig := FBConfigs^;
\r
343 Result:=glXGetVisualFromFBConfig(dpy, FBConfig);
\r
347 { TglcContextGtk2GLX }
\r
349 procedure TglcContextGtk2GLX.UpdateVisual(const aControl: TWinControl);
\r
351 attrList: TGLIntArray;
\r
352 drawable: PGdkDrawable;
\r
354 if not Assigned(aControl) then
\r
355 raise EArgumentException.Create('aControl is not assigned');
\r
358 Temporary (realized) widget to get to display
\r
360 FWidget:= {%H-}PGtkWidget(PtrUInt(aControl.Handle));
\r
361 gtk_widget_realize(FWidget);
\r
362 drawable:= GTK_WIDGET(FWidget)^.window;
\r
364 FDisplay:= GDK_WINDOW_XDISPLAY(drawable);
\r
367 Find a suitable visual from PixelFormat using GLX 1.3 FBConfigs or
\r
370 if Assigned(glXChooseFBConfig) then begin
\r
371 attrList := CreateOpenGLContextAttrList(true, fPixelFormatSettings);
\r
372 FVisual := FBglXChooseVisual(FDisplay, DefaultScreen(FDisplay), @attrList[0]);
\r
373 if not Assigned(FVisual) and (fPixelFormatSettings.MultiSampling > 1) then begin
\r
374 fPixelFormatSettings.MultiSampling := 1;
\r
375 attrList := CreateOpenGLContextAttrList(true, fPixelFormatSettings);
\r
376 FVisual := FBglXChooseVisual(FDisplay, DefaultScreen(FDisplay), @attrList[0]);
\r
379 if not Assigned(FVisual) then begin
\r
380 attrList := CreateOpenGLContextAttrList(false, fPixelFormatSettings);
\r
381 FVisual := glXChooseVisual(FDisplay, DefaultScreen(FDisplay), @attrList[0]);
\r
382 if not Assigned(FVisual) and (fPixelFormatSettings.MultiSampling > 1) then begin
\r
383 fPixelFormatSettings.MultiSampling := 1;
\r
384 attrList := CreateOpenGLContextAttrList(false, fPixelFormatSettings);
\r
385 FVisual := glXChooseVisual(FDisplay, DefaultScreen(FDisplay), @attrList[0]);
\r
388 if not Assigned(FVisual) then
\r
389 raise EGLXError.Create('choose visual failed');
\r
392 Most widgets inherit the drawable of their parent. In contrast to Windows, descending from
\r
393 TWinControl does not mean it's actually always a window of its own.
\r
394 Famous example: TPanel is just a frame painted on a canvas.
\r
395 Also, the LCL does somethin weird to colormaps in window creation, so we have
\r
396 to use a custom widget here to have full control about visual selection.
\r
398 FRenderControl:= TRenderControl.Create(aControl, FVisual^.visual^.visualid);
\r
400 FRenderControl.Parent := aControl;
\r
401 FRenderControl.HandleNeeded;
\r
402 FRenderControl.Target := aControl;
\r
404 FreeAndNil(FRenderControl);
\r
409 Real Widget handle, unrealized!!!
\r
411 FWidget:= FRenderControl.Widget;
\r
412 gtk_widget_realize(FWidget);
\r
413 drawable:= GTK_WIDGET(FWidget)^.window;
\r
414 FDisplay:= GDK_WINDOW_XDISPLAY(drawable);
\r
416 // FRenderControl.Align:= alClient breaks the context or something
\r
417 FRenderControl.BoundsRect := aControl.ClientRect;
\r
418 FRenderControl.Anchors := [akLeft, akTop, akRight, akBottom];
\r
421 procedure TglcContextGtk2GLX.OpenContext;
\r
423 Attribs: array of GLint;
\r
424 tmpContext: GLXContext;
\r
425 glxID: GLXDrawable;
\r
427 inherited OpenContext;
\r
429 if not Assigned(FVisual) then
\r
430 raise EGLXError.Create('Failed to find Visual');
\r
432 tmpContext := glXCreateContext(FDisplay, FVisual, nil, true);
\r
434 (fVersionSettings.Major <> GLC_CONTEXT_VERSION_UNKNOWN) and
\r
435 (fVersionSettings.Minor <> GLC_CONTEXT_VERSION_UNKNOWN) then
\r
437 // Set attributes to describe our requested context
\r
438 SetLength(Attribs, 5);
\r
439 Attribs[0] := WGL_CONTEXT_MAJOR_VERSION_ARB;
\r
440 Attribs[1] := fVersionSettings.Major;
\r
441 Attribs[2] := WGL_CONTEXT_MINOR_VERSION_ARB;
\r
442 Attribs[3] := fVersionSettings.Minor;
\r
444 // Add context flag for forward compatible context
\r
445 // Forward compatible means no more support for legacy functions like
\r
446 // immediate mode (glvertex, glrotate, gltranslate, etc.)
\r
447 if fVersionSettings.ForwardCompatible then begin
\r
448 SetLength(Attribs, Length(Attribs)+2);
\r
449 Attribs[4] := WGL_CONTEXT_FLAGS_ARB;
\r
450 Attribs[5] := WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB;
\r
453 // Attribute flags must be finalized with a zero
\r
454 SetLength(Attribs, 1);
\r
455 Attribs[High(Attribs)] := 0;
\r
457 glxID := GDK_DRAWABLE_XID(GTK_WIDGET(FWidget)^.window);
\r
458 glXMakeCurrent(FDisplay, glxID, tmpContext);
\r
459 ReadImplementationProperties;
\r
460 if not Assigned(glXCreateContextAttribsARB) or not GLX_ARB_create_context then begin
\r
461 glXDestroyContext(FDisplay, tmpContext);
\r
462 raise Exception.Create('GLX_ARB_create_context not supported');
\r
464 FContext := glXCreateContextAttribsARB(FDisplay, FVisual, nil, true, @Attribs[0]);
\r
466 glXDestroyContext(FDisplay, tmpContext);
\r
468 FContext := tmpContext;
\r
470 if (FContext = nil) then
\r
471 raise EGLXError.Create('Failed to create Context');
\r
474 constructor TglcContextGtk2GLX.Create(const aControl: TWinControl;
\r
475 const aPixelFormatSettings: TglcContextPixelFormatSettings);
\r
477 inherited Create(aControl, aPixelFormatSettings);
\r
478 UpdateVisual(aControl);
\r
481 constructor TglcContextGtk2GLX.Create(const aControl: TWinControl;
\r
482 const aPixelFormatSettings: TglcContextPixelFormatSettings;
\r
483 const aVersionSettings: TglcContextVersionSettings);
\r
485 inherited Create(aControl, aPixelFormatSettings, aVersionSettings);
\r
486 UpdateVisual(aControl);
\r
489 destructor TglcContextGtk2GLX.Destroy;
\r
491 FreeAndNil(FRenderControl);
\r
496 procedure TglcContextGtk2GLX.CloseContext;
\r
498 if not Assigned(FWidget) then exit;
\r
499 if Assigned(FContext) then
\r
500 glXDestroyContext(FDisplay, FContext);
\r
501 FreeAndNil(FRenderControl);
\r
504 procedure TglcContextGtk2GLX.Activate;
\r
506 glxID: GLXDrawable;
\r
508 if not Assigned(FWidget) then exit;
\r
509 // make sure the widget is realized
\r
510 gtk_widget_realize(FWidget);
\r
511 if not GTK_WIDGET_REALIZED(FWidget) then exit;
\r
514 glxID := GDK_DRAWABLE_XID(GTK_WIDGET(FWidget)^.window);
\r
515 glXMakeCurrent(FDisplay, glxID, FContext);
\r
518 procedure TglcContextGtk2GLX.Deactivate;
\r
520 glxID: GLXDrawable;
\r
522 if not Assigned(FWidget) then exit;
\r
523 glxID := GDK_DRAWABLE_XID(GTK_WIDGET(FWidget)^.window);
\r
524 glXMakeCurrent(FDisplay, glxID, nil);
\r
527 function TglcContextGtk2GLX.IsActive: boolean;
\r
529 glxID: GLXDrawable;
\r
531 glxID := GDK_DRAWABLE_XID(GTK_WIDGET(FWidget)^.window);
\r
532 Result:= (FContext = glXGetCurrentContext()) and
\r
533 Assigned(FWidget) and
\r
534 (glxID = glXGetCurrentDrawable());
\r
537 procedure TglcContextGtk2GLX.SwapBuffers;
\r
539 glxID: GLXDrawable;
\r
541 if not Assigned(FWidget) then exit;
\r
542 glxID := GDK_DRAWABLE_XID(GTK_WIDGET(FWidget)^.window);
\r
543 glXSwapBuffers(FDisplay, glxID);
\r
546 procedure TglcContextGtk2GLX.SetSwapInterval(const aInterval: GLint);
\r
548 drawable: PGdkDrawable;
\r
550 drawable:= GTK_WIDGET(FWidget)^.window;
\r
551 if GLX_EXT_swap_control then
\r
552 glXSwapIntervalEXT(FDisplay, GDK_WINDOW_XWINDOW(drawable), aInterval);
\r
555 procedure TglcContextGtk2GLX.Share(const aContext: TglcContext);
\r
557 raise Exception.Create('not yet implemented');
\r
560 class function TglcContextGtk2GLX.{%H-}ChangeDisplaySettings(const aWidth, aHeight,
\r
561 aBitPerPixel, aFreq: Integer; const aFlags: TglcDisplayFlags): Boolean;
\r
563 raise Exception.Create('not yet implemented');
\r
566 class function TglcContextGtk2GLX.IsAnyContextActive: boolean;
\r
568 Result:= (glXGetCurrentContext()<>nil) and (glXGetCurrentDrawable()<>0);
\r