You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

573 lines
16 KiB

  1. unit uglcContextGtk2GLX;
  2. { Package: OpenGLCore
  3. Prefix: glc - OpenGL Core
  4. Beschreibung: diese Unit enthält eine Klassen-Kapselung für OpenGL Kontexte für Linux
  5. Hint: diese Unit sollte niemals direkt genutzt werden (siehe uglcContext) }
  6. interface
  7. uses
  8. SysUtils, Controls, uglcContext, LCLType, XUtil, XLib, gdk2x, gtk2, gdk2, dglOpenGL,
  9. LMessages, uglcContextGtkCustomVisual;
  10. type
  11. EGLXError = class(EGLError);
  12. TRenderControl = class(TCustomVisualControl)
  13. private
  14. fTarget: TWinControl;
  15. protected
  16. procedure WndProc(var Message: TLMessage); override;
  17. public
  18. property Target: TWinControl read fTarget write fTarget;
  19. end;
  20. { TglcContextGtk2GLX }
  21. TglcContextGtk2GLX = class(TglcContext)
  22. private
  23. FVisual: PXVisualInfo;
  24. FDisplay: PDisplay;
  25. FWidget: PGtkWidget;
  26. FContext: GLXContext;
  27. FRenderControl: TRenderControl;
  28. procedure UpdateVisual(const aControl: TWinControl);
  29. protected
  30. procedure OpenContext; override;
  31. public
  32. constructor Create(const aControl: TWinControl; const aPixelFormatSettings: TglcContextPixelFormatSettings); override; overload;
  33. constructor Create(const aControl: TWinControl; const aPixelFormatSettings: TglcContextPixelFormatSettings; const aVersionSettings: TglcContextVersionSettings); override; overload;
  34. destructor Destroy; override;
  35. procedure CloseContext; override;
  36. procedure Activate; override;
  37. procedure Deactivate; override;
  38. function IsActive: boolean; override;
  39. procedure SwapBuffers; override;
  40. procedure SetSwapInterval(const aInterval: GLint); override;
  41. procedure Share(const aContext: TglcContext); override;
  42. class function ChangeDisplaySettings(const aWidth, aHeight,
  43. aBitPerPixel, aFreq: Integer; const aFlags: TglcDisplayFlags): Boolean; override;
  44. class function IsAnyContextActive: boolean; override;
  45. end;
  46. implementation
  47. type
  48. TGLIntArray = packed array of GLInt;
  49. {$region messages -fold}
  50. procedure TRenderControl.WndProc(var Message: TLMessage);
  51. begin
  52. case Message.msg of
  53. //LM_ACTIVATEITEM,
  54. //LM_CHANGED,
  55. //LM_FOCUS,
  56. LM_CLICKED,
  57. //LM_RELEASED,
  58. LM_ENTER,
  59. LM_LEAVE,
  60. //LM_CHECKRESIZE,
  61. //LM_SETEDITABLE,
  62. //LM_MOVEWORD,
  63. //LM_MOVEPAGE,
  64. //LM_MOVETOROW,
  65. //LM_MOVETOCOLUMN,
  66. //LM_KILLCHAR,
  67. //LM_KILLWORD,
  68. //LM_KILLLINE,
  69. //LM_CLOSEQUERY,
  70. //LM_DRAGSTART,
  71. //LM_MONTHCHANGED,
  72. //LM_YEARCHANGED,
  73. //LM_DAYCHANGED,
  74. LM_LBUTTONTRIPLECLK,
  75. LM_LBUTTONQUADCLK,
  76. LM_MBUTTONTRIPLECLK,
  77. LM_MBUTTONQUADCLK,
  78. LM_RBUTTONTRIPLECLK,
  79. LM_RBUTTONQUADCLK,
  80. LM_MOUSEENTER,
  81. LM_MOUSELEAVE,
  82. LM_XBUTTONTRIPLECLK,
  83. LM_XBUTTONQUADCLK,
  84. //SC_SIZE,
  85. //SC_MOVE,
  86. //SC_MINIMIZE,
  87. //SC_MAXIMIZE,
  88. //SC_NEXTWINDOW,
  89. //SC_PREVWINDOW,
  90. //SC_CLOSE,
  91. SC_VSCROLL,
  92. SC_HSCROLL,
  93. SC_MOUSEMENU,
  94. SC_KEYMENU,
  95. //SC_ARRANGE,
  96. //SC_RESTORE,
  97. //SC_TASKLIST,
  98. //SC_SCREENSAVE,
  99. //SC_HOTKEY,
  100. //SC_DEFAULT,
  101. //SC_MONITORPOWER,
  102. //SC_CONTEXTHELP,
  103. //SC_SEPARATOR,
  104. //LM_MOVE,
  105. //LM_SIZE,
  106. LM_ACTIVATE,
  107. LM_SETFOCUS,
  108. LM_KILLFOCUS,
  109. //LM_ENABLE,
  110. //LM_GETTEXTLENGTH,
  111. //LM_SHOWWINDOW,
  112. //LM_CANCELMODE,
  113. //LM_DRAWITEM,
  114. //LM_MEASUREITEM,
  115. //LM_DELETEITEM,
  116. //LM_VKEYTOITEM,
  117. //LM_CHARTOITEM,
  118. //LM_COMPAREITEM,
  119. //LM_WINDOWPOSCHANGING,
  120. //LM_WINDOWPOSCHANGED,
  121. //LM_NOTIFY,
  122. //LM_HELP,
  123. //LM_NOTIFYFORMAT,
  124. //LM_CONTEXTMENU,
  125. //LM_NCCALCSIZE,
  126. //LM_NCHITTEST,
  127. //LM_NCPAINT,
  128. //LM_NCACTIVATE,
  129. //LM_GETDLGCODE,
  130. LM_NCMOUSEMOVE,
  131. LM_NCLBUTTONDOWN,
  132. LM_NCLBUTTONUP,
  133. LM_NCLBUTTONDBLCLK,
  134. LM_KEYDOWN,
  135. LM_KEYUP,
  136. LM_CHAR,
  137. LM_SYSKEYDOWN,
  138. LM_SYSKEYUP,
  139. LM_SYSCHAR,
  140. LM_COMMAND,
  141. LM_SYSCOMMAND,
  142. LM_TIMER,
  143. LM_HSCROLL,
  144. LM_VSCROLL,
  145. //LM_CTLCOLORMSGBOX,
  146. //LM_CTLCOLOREDIT,
  147. //LM_CTLCOLORLISTBOX,
  148. //LM_CTLCOLORBTN,
  149. //LM_CTLCOLORDLG,
  150. //LM_CTLCOLORSCROLLBAR,
  151. //LM_CTLCOLORSTATIC,
  152. LM_MOUSEMOVE,
  153. LM_LBUTTONDOWN,
  154. LM_LBUTTONUP,
  155. LM_LBUTTONDBLCLK,
  156. LM_RBUTTONDOWN,
  157. LM_RBUTTONUP,
  158. LM_RBUTTONDBLCLK,
  159. LM_MBUTTONDOWN,
  160. LM_MBUTTONUP,
  161. LM_MBUTTONDBLCLK,
  162. LM_MOUSEWHEEL,
  163. LM_XBUTTONDOWN,
  164. LM_XBUTTONUP,
  165. LM_XBUTTONDBLCLK,
  166. //LM_PARENTNOTIFY,
  167. //LM_CAPTURECHANGED,
  168. //LM_DROPFILES,
  169. //LM_SELCHANGE,
  170. LM_CUT,
  171. LM_COPY,
  172. LM_PASTE,
  173. //LM_CLEAR,
  174. //LM_CONFIGUREEVENT,
  175. //LM_EXIT,
  176. //LM_QUIT,
  177. //LM_NULL,
  178. //LM_PAINT,
  179. //LM_ERASEBKGND,
  180. //LM_SETCURSOR,
  181. //LM_SETFONT:
  182. //CM_ACTIVATE,
  183. //CM_DEACTIVATE,
  184. //CM_FOCUSCHANGED,
  185. //CM_PARENTFONTCHANGED,
  186. //CM_PARENTCOLORCHANGED,
  187. //CM_HITTEST,
  188. //CM_VISIBLECHANGED,
  189. //CM_ENABLEDCHANGED,
  190. //CM_COLORCHANGED,
  191. //CM_FONTCHANGED,
  192. //CM_CURSORCHANGED,
  193. //CM_TEXTCHANGED,
  194. CM_MOUSEENTER,
  195. CM_MOUSELEAVE,
  196. //CM_MENUCHANGED,
  197. //CM_APPSYSCOMMAND,
  198. //CM_BUTTONPRESSED,
  199. //CM_SHOWINGCHANGED,
  200. //CM_ENTER,
  201. //CM_EXIT,
  202. //CM_DESIGNHITTEST,
  203. //CM_ICONCHANGED,
  204. //CM_WANTSPECIALKEY,
  205. //CM_RELEASE,
  206. //CM_FONTCHANGE,
  207. //CM_TABSTOPCHANGED,
  208. //CM_UIACTIVATE,
  209. //CM_CONTROLLISTCHANGE,
  210. //CM_GETDATALINK,
  211. //CM_CHILDKEY,
  212. //CM_HINTSHOW,
  213. //CM_SYSFONTCHANGED,
  214. //CM_CONTROLCHANGE,
  215. //CM_CHANGED,
  216. //CM_BORDERCHANGED,
  217. //CM_BIDIMODECHANGED,
  218. //CM_PARENTBIDIMODECHANGED,
  219. //CM_ALLCHILDRENFLIPPED,
  220. //CM_ACTIONUPDATE,
  221. //CM_ACTIONEXECUTE,
  222. //CM_HINTSHOWPAUSE,
  223. //CM_DOCKNOTIFICATION,
  224. CM_MOUSEWHEEL,
  225. //CM_APPSHOWBTNGLYPHCHANGED,
  226. //CM_APPSHOWMENUGLYPHCHANGED,
  227. //CN_BASE,
  228. //CN_CHARTOITEM,
  229. //CN_COMMAND,
  230. //CN_COMPAREITEM,
  231. //CN_CTLCOLORBTN,
  232. //CN_CTLCOLORDLG,
  233. //CN_CTLCOLOREDIT,
  234. //CN_CTLCOLORLISTBOX,
  235. //CN_CTLCOLORMSGBOX,
  236. //CN_CTLCOLORSCROLLBAR,
  237. //CN_CTLCOLORSTATIC,
  238. //CN_DELETEITEM,
  239. //CN_DRAWITEM,
  240. CN_HSCROLL,
  241. //CN_MEASUREITEM,
  242. //CN_PARENTNOTIFY,
  243. //CN_VKEYTOITEM,
  244. CN_VSCROLL,
  245. CN_KEYDOWN,
  246. CN_KEYUP,
  247. CN_CHAR,
  248. CN_SYSKEYUP,
  249. CN_SYSKEYDOWN,
  250. CN_SYSCHAR,
  251. CN_NOTIFY:
  252. begin
  253. if Assigned(fTarget) then
  254. Message.Result := fTarget.Perform(Message.msg, Message.wParam, Message.lParam);
  255. end;
  256. end;
  257. inherited WndProc(Message);
  258. end;
  259. {$endregion}
  260. function CreateOpenGLContextAttrList(UseFB: boolean; pf: TglcContextPixelFormatSettings): TGLIntArray;
  261. var
  262. p: integer;
  263. procedure Add(i: integer);
  264. begin
  265. SetLength(Result, p+1);
  266. Result[p]:=i;
  267. inc(p);
  268. end;
  269. procedure CreateList;
  270. begin
  271. if UseFB then begin Add(GLX_X_RENDERABLE); Add(1); end;
  272. if pf.DoubleBuffered then begin
  273. if UseFB then begin
  274. Add(GLX_DOUBLEBUFFER); Add(1);
  275. end else
  276. Add(GLX_DOUBLEBUFFER);
  277. end;
  278. if not UseFB and (pf.ColorBits>24) then Add(GLX_RGBA);
  279. if UseFB then begin
  280. Add(GLX_DRAWABLE_TYPE);
  281. Add(GLX_WINDOW_BIT);
  282. end;
  283. Add(GLX_RED_SIZE); Add(8);
  284. Add(GLX_GREEN_SIZE); Add(8);
  285. Add(GLX_BLUE_SIZE); Add(8);
  286. if pf.ColorBits>24 then
  287. Add(GLX_ALPHA_SIZE); Add(8);
  288. Add(GLX_DEPTH_SIZE); Add(pf.DepthBits);
  289. Add(GLX_STENCIL_SIZE); Add(pf.StencilBits);
  290. Add(GLX_AUX_BUFFERS); Add(pf.AUXBuffers);
  291. if pf.MultiSampling > 1 then begin
  292. Add(GLX_SAMPLE_BUFFERS_ARB); Add(1);
  293. Add(GLX_SAMPLES_ARB); Add(pf.MultiSampling);
  294. end;
  295. Add(0); { 0 = X.None (be careful: GLX_NONE is something different) }
  296. end;
  297. begin
  298. SetLength(Result, 0);
  299. p:=0;
  300. CreateList;
  301. end;
  302. function FBglXChooseVisual(dpy:PDisplay; screen:longint; attrib_list:Plongint):PXVisualInfo;
  303. type
  304. PGLXFBConfig = ^GLXFBConfig;
  305. var
  306. FBConfigsCount: integer;
  307. FBConfigs: PGLXFBConfig;
  308. FBConfig: GLXFBConfig;
  309. begin
  310. Result:= nil;
  311. FBConfigsCount:=0;
  312. FBConfigs:= glXChooseFBConfig(dpy, screen, attrib_list, @FBConfigsCount);
  313. if FBConfigsCount = 0 then
  314. exit;
  315. { just choose the first FB config from the FBConfigs list.
  316. More involved selection possible. }
  317. FBConfig := FBConfigs^;
  318. Result:=glXGetVisualFromFBConfig(dpy, FBConfig);
  319. end;
  320. { TglcContextGtk2GLX }
  321. procedure TglcContextGtk2GLX.UpdateVisual(const aControl: TWinControl);
  322. var
  323. attrList: TGLIntArray;
  324. drawable: PGdkDrawable;
  325. begin
  326. if not Assigned(aControl) then
  327. raise EArgumentException.Create('aControl is not assigned');
  328. {
  329. Temporary (realized) widget to get to display
  330. }
  331. FWidget:= {%H-}PGtkWidget(PtrUInt(aControl.Handle));
  332. gtk_widget_realize(FWidget);
  333. drawable:= GTK_WIDGET(FWidget)^.window;
  334. FDisplay:= GDK_WINDOW_XDISPLAY(drawable);
  335. {
  336. Find a suitable visual from PixelFormat using GLX 1.3 FBConfigs or
  337. old-style Visuals
  338. }
  339. if Assigned(glXChooseFBConfig) then begin
  340. attrList := CreateOpenGLContextAttrList(true, fPixelFormatSettings);
  341. FVisual := FBglXChooseVisual(FDisplay, DefaultScreen(FDisplay), @attrList[0]);
  342. if not Assigned(FVisual) and (fPixelFormatSettings.MultiSampling > 1) then begin
  343. fPixelFormatSettings.MultiSampling := 1;
  344. attrList := CreateOpenGLContextAttrList(true, fPixelFormatSettings);
  345. FVisual := FBglXChooseVisual(FDisplay, DefaultScreen(FDisplay), @attrList[0]);
  346. end;
  347. end;
  348. if not Assigned(FVisual) then begin
  349. attrList := CreateOpenGLContextAttrList(false, fPixelFormatSettings);
  350. FVisual := glXChooseVisual(FDisplay, DefaultScreen(FDisplay), @attrList[0]);
  351. if not Assigned(FVisual) and (fPixelFormatSettings.MultiSampling > 1) then begin
  352. fPixelFormatSettings.MultiSampling := 1;
  353. attrList := CreateOpenGLContextAttrList(false, fPixelFormatSettings);
  354. FVisual := glXChooseVisual(FDisplay, DefaultScreen(FDisplay), @attrList[0]);
  355. end;
  356. end;
  357. if not Assigned(FVisual) then
  358. raise EGLXError.Create('choose visual failed');
  359. {
  360. Most widgets inherit the drawable of their parent. In contrast to Windows, descending from
  361. TWinControl does not mean it's actually always a window of its own.
  362. Famous example: TPanel is just a frame painted on a canvas.
  363. Also, the LCL does somethin weird to colormaps in window creation, so we have
  364. to use a custom widget here to have full control about visual selection.
  365. }
  366. FRenderControl:= TRenderControl.Create(aControl, FVisual^.visual^.visualid);
  367. try
  368. FRenderControl.Parent := aControl;
  369. FRenderControl.HandleNeeded;
  370. FRenderControl.Target := aControl;
  371. except
  372. FreeAndNil(FRenderControl);
  373. raise;
  374. end;
  375. {
  376. Real Widget handle, unrealized!!!
  377. }
  378. FWidget:= FRenderControl.Widget;
  379. gtk_widget_realize(FWidget);
  380. drawable:= GTK_WIDGET(FWidget)^.window;
  381. FDisplay:= GDK_WINDOW_XDISPLAY(drawable);
  382. // FRenderControl.Align:= alClient breaks the context or something
  383. FRenderControl.BoundsRect := aControl.ClientRect;
  384. FRenderControl.Anchors := [akLeft, akTop, akRight, akBottom];
  385. end;
  386. procedure TglcContextGtk2GLX.OpenContext;
  387. var
  388. Attribs: array of GLint;
  389. tmpContext: GLXContext;
  390. glxID: GLXDrawable;
  391. begin
  392. inherited OpenContext;
  393. if not Assigned(FVisual) then
  394. raise EGLXError.Create('Failed to find Visual');
  395. tmpContext := glXCreateContext(FDisplay, FVisual, nil, true);
  396. if fUseVersion and
  397. (fVersionSettings.Major <> GLC_CONTEXT_VERSION_UNKNOWN) and
  398. (fVersionSettings.Minor <> GLC_CONTEXT_VERSION_UNKNOWN) then
  399. begin
  400. // Set attributes to describe our requested context
  401. SetLength(Attribs, 5);
  402. Attribs[0] := WGL_CONTEXT_MAJOR_VERSION_ARB;
  403. Attribs[1] := fVersionSettings.Major;
  404. Attribs[2] := WGL_CONTEXT_MINOR_VERSION_ARB;
  405. Attribs[3] := fVersionSettings.Minor;
  406. // Add context flag for forward compatible context
  407. // Forward compatible means no more support for legacy functions like
  408. // immediate mode (glvertex, glrotate, gltranslate, etc.)
  409. if fVersionSettings.ForwardCompatible then begin
  410. SetLength(Attribs, Length(Attribs)+2);
  411. Attribs[4] := WGL_CONTEXT_FLAGS_ARB;
  412. Attribs[5] := WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB;
  413. end;
  414. // Attribute flags must be finalized with a zero
  415. SetLength(Attribs, 1);
  416. Attribs[High(Attribs)] := 0;
  417. glxID := GDK_DRAWABLE_XID(GTK_WIDGET(FWidget)^.window);
  418. glXMakeCurrent(FDisplay, glxID, tmpContext);
  419. ReadImplementationProperties;
  420. if not Assigned(glXCreateContextAttribsARB) or not GLX_ARB_create_context then begin
  421. glXDestroyContext(FDisplay, tmpContext);
  422. raise Exception.Create('GLX_ARB_create_context not supported');
  423. end;
  424. FContext := glXCreateContextAttribsARB(FDisplay, FVisual, nil, true, @Attribs[0]);
  425. glXDestroyContext(FDisplay, tmpContext);
  426. end else
  427. FContext := tmpContext;
  428. if (FContext = nil) then
  429. raise EGLXError.Create('Failed to create Context');
  430. end;
  431. constructor TglcContextGtk2GLX.Create(const aControl: TWinControl;
  432. const aPixelFormatSettings: TglcContextPixelFormatSettings);
  433. begin
  434. inherited Create(aControl, aPixelFormatSettings);
  435. UpdateVisual(aControl);
  436. end;
  437. constructor TglcContextGtk2GLX.Create(const aControl: TWinControl;
  438. const aPixelFormatSettings: TglcContextPixelFormatSettings;
  439. const aVersionSettings: TglcContextVersionSettings);
  440. begin
  441. inherited Create(aControl, aPixelFormatSettings, aVersionSettings);
  442. UpdateVisual(aControl);
  443. end;
  444. destructor TglcContextGtk2GLX.Destroy;
  445. begin
  446. FreeAndNil(FRenderControl);
  447. XFree(FVisual);
  448. inherited Destroy;
  449. end;
  450. procedure TglcContextGtk2GLX.CloseContext;
  451. begin
  452. if not Assigned(FWidget) then exit;
  453. if Assigned(FContext) then
  454. glXDestroyContext(FDisplay, FContext);
  455. FreeAndNil(FRenderControl);
  456. end;
  457. procedure TglcContextGtk2GLX.Activate;
  458. var
  459. glxID: GLXDrawable;
  460. begin
  461. if not Assigned(FWidget) then exit;
  462. // make sure the widget is realized
  463. gtk_widget_realize(FWidget);
  464. if not GTK_WIDGET_REALIZED(FWidget) then exit;
  465. // make current
  466. glxID := GDK_DRAWABLE_XID(GTK_WIDGET(FWidget)^.window);
  467. glXMakeCurrent(FDisplay, glxID, FContext);
  468. end;
  469. procedure TglcContextGtk2GLX.Deactivate;
  470. var
  471. glxID: GLXDrawable;
  472. begin
  473. if not Assigned(FWidget) then exit;
  474. glxID := GDK_DRAWABLE_XID(GTK_WIDGET(FWidget)^.window);
  475. glXMakeCurrent(FDisplay, glxID, nil);
  476. end;
  477. function TglcContextGtk2GLX.IsActive: boolean;
  478. var
  479. glxID: GLXDrawable;
  480. begin
  481. glxID := GDK_DRAWABLE_XID(GTK_WIDGET(FWidget)^.window);
  482. Result:= (FContext = glXGetCurrentContext()) and
  483. Assigned(FWidget) and
  484. (glxID = glXGetCurrentDrawable());
  485. end;
  486. procedure TglcContextGtk2GLX.SwapBuffers;
  487. var
  488. glxID: GLXDrawable;
  489. begin
  490. if not Assigned(FWidget) then exit;
  491. glxID := GDK_DRAWABLE_XID(GTK_WIDGET(FWidget)^.window);
  492. glXSwapBuffers(FDisplay, glxID);
  493. end;
  494. procedure TglcContextGtk2GLX.SetSwapInterval(const aInterval: GLint);
  495. var
  496. drawable: PGdkDrawable;
  497. begin
  498. drawable:= GTK_WIDGET(FWidget)^.window;
  499. if GLX_EXT_swap_control then
  500. glXSwapIntervalEXT(FDisplay, GDK_WINDOW_XWINDOW(drawable), aInterval);
  501. end;
  502. procedure TglcContextGtk2GLX.Share(const aContext: TglcContext);
  503. begin
  504. raise Exception.Create('not yet implemented');
  505. end;
  506. class function TglcContextGtk2GLX.{%H-}ChangeDisplaySettings(const aWidth, aHeight,
  507. aBitPerPixel, aFreq: Integer; const aFlags: TglcDisplayFlags): Boolean;
  508. begin
  509. raise Exception.Create('not yet implemented');
  510. end;
  511. class function TglcContextGtk2GLX.IsAnyContextActive: boolean;
  512. begin
  513. Result:= (glXGetCurrentContext()<>nil) and (glXGetCurrentDrawable()<>0);
  514. end;
  515. end.