//****************************************************************************** // 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 #include #include #include // 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"