LazOpenGLContextEx: TOpenGLControl с Qt-бэкендом на QOpenGLWidget

Форк штатного пакета LazOpenGLContext (Lazarus components/opengl).
Qt5/Qt6-бэкенд (glqtnativecontext.pas) использует собственный контекст
настоящего QOpenGLWidget вместо ручного GLX на winId(): EGL на Wayland,
GLX на X11, WGL/CGL на Windows/macOS под ws=qt6.

QOpenGLWidget-наследник с paintGL-хуком живёт в маленькой C++-либе
csrc/qlclglwidget.cpp (QLCLOpenGLWidget из libQt6Pas — не QOpenGLWidget,
а голый QWidget под внешний GLX). Сборка: csrc/Makefile
(linux/macos/windows-кросс mingw-w64/windows-native MSYS2, install,
install-app) + build-msvc.bat для MSVC-Qt. Либа грузится в рантайме
рядом с бинарником приложения.

Бэкенды gtk2/gtk3/win32/cocoa скопированы из стока без изменений
(юниты переименованы с суффиксом Ex). Детали и нюансы — в README.md.

Проверено: EWSDR, спектр/водопад через GL в нативном Wayland (KDE, qt6),
живой эфир QO-100.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-03 15:00:26 +03:00
co-authored by Claude Fable 5
commit ac46e3d13a
14 changed files with 4041 additions and 0 deletions
+124
View File
@@ -0,0 +1,124 @@
//******************************************************************************
// qlclglwidget - a real QOpenGLWidget subclass with a Pascal paintGL hook.
//
// libQt6Pas's QLCLOpenGLWidget is NOT a QOpenGLWidget: it is a plain QWidget
// with WA_NativeWindow/paintEngine()=nullptr, designed as a target for an
// external GLX context (X11 only). This tiny library provides the missing
// piece: a genuine QOpenGLWidget whose context is created and managed by Qt
// itself (EGL on Wayland, GLX on X11, WGL on Windows, CGL on macOS), plus
// flat C exports for the methods the Lazarus side needs.
//
// Build: make (Linux .so / macOS .dylib)
// make windows (cross-build .dll with mingw-w64, see Makefile)
// make install (into /usr/local/lib)
//******************************************************************************
#include <QOpenGLWidget>
#include <QOpenGLContext>
#include <QOpenGLFunctions>
#include <QSurfaceFormat>
// mirrors QHook/QOverrideHook from libQt6Pas pascalbind.h:
// an FPC "procedure of object; cdecl" method pointer passed by value
typedef struct {
void *func;
void *data;
} QGLOverrideHook;
class QLCLGLWidget : public QOpenGLWidget {
public:
QGLOverrideHook paintGLHook;
// The widget renders into an RGBA FBO that Qt composites with alpha into
// the window. Legacy GL code written for GLX/WGL windows leaves arbitrary
// alpha in the framebuffer (there it was simply ignored), which makes the
// widget translucent here. Unless the user explicitly asked for an alpha
// channel, force alpha to 1 after each paint.
bool forceOpaque;
explicit QLCLGLWidget(QWidget *parent = nullptr,
Qt::WindowFlags flags = Qt::WindowFlags())
: QOpenGLWidget(parent, flags) {
paintGLHook.func = nullptr;
paintGLHook.data = nullptr;
forceOpaque = true;
}
protected:
// Qt makes the context current and binds the widget's FBO before this call
void paintGL() override {
if (paintGLHook.func) {
typedef void (*func_type)(void *data);
(*(func_type)paintGLHook.func)(paintGLHook.data);
if (forceOpaque)
fillAlpha();
} else {
QOpenGLWidget::paintGL();
}
}
private:
void fillAlpha() {
QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
GLboolean mask[4];
GLfloat clearColor[4];
f->glGetBooleanv(GL_COLOR_WRITEMASK, mask);
f->glGetFloatv(GL_COLOR_CLEAR_VALUE, clearColor);
GLboolean scissor = f->glIsEnabled(GL_SCISSOR_TEST);
if (scissor)
f->glDisable(GL_SCISSOR_TEST);
f->glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
f->glClearColor(0.f, 0.f, 0.f, 1.f);
f->glClear(GL_COLOR_BUFFER_BIT);
f->glColorMask(mask[0], mask[1], mask[2], mask[3]);
f->glClearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]);
if (scissor)
f->glEnable(GL_SCISSOR_TEST);
}
};
extern "C" {
Q_DECL_EXPORT void *QLCLGLWidget_Create(void *parent, unsigned int flags) {
return (void *)new QLCLGLWidget((QWidget *)parent, (Qt::WindowFlags)flags);
}
Q_DECL_EXPORT void QLCLGLWidget_Destroy(void *handle) {
delete (QLCLGLWidget *)handle;
}
Q_DECL_EXPORT void QLCLGLWidget_override_paintGL(void *handle,
QGLOverrideHook hook) {
((QLCLGLWidget *)handle)->paintGLHook = hook;
}
Q_DECL_EXPORT void QLCLGLWidget_makeCurrent(void *handle) {
((QLCLGLWidget *)handle)->makeCurrent();
}
Q_DECL_EXPORT void QLCLGLWidget_doneCurrent(void *handle) {
((QLCLGLWidget *)handle)->doneCurrent();
}
Q_DECL_EXPORT bool QLCLGLWidget_isValid(void *handle) {
return ((QLCLGLWidget *)handle)->isValid();
}
// fmt is a QSurfaceFormatH created via libQt6Pas QSurfaceFormat_Create
Q_DECL_EXPORT void QLCLGLWidget_setFormat(void *handle, void *fmt) {
((QLCLGLWidget *)handle)->setFormat(*(const QSurfaceFormat *)fmt);
}
// enabled by default; pass false when the control requests AlphaBits > 0
Q_DECL_EXPORT void QLCLGLWidget_setForceOpaque(void *handle, bool enable) {
((QLCLGLWidget *)handle)->forceOpaque = enable;
}
Q_DECL_EXPORT unsigned int QLCLGLWidget_defaultFramebufferObject(void *handle) {
return ((QLCLGLWidget *)handle)->defaultFramebufferObject();
}
Q_DECL_EXPORT double QLCLGLWidget_devicePixelRatioF(void *handle) {
return ((QLCLGLWidget *)handle)->devicePixelRatioF();
}
} // extern "C"