mirror of
https://git.vladimir.cc/vladimir/lazopenglcontextex.git
synced 2026-08-25 17:37:31 +00:00
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:
@@ -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"
|
||||
Reference in New Issue
Block a user