Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

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