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.

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