mirror of
https://git.vladimir.cc/vladimir/lazopenglcontextex.git
synced 2026-08-26 01:37:37 +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:
+144
@@ -0,0 +1,144 @@
|
||||
# ----------------------------------------------------------------------------
|
||||
# libqlclglwidget — QOpenGLWidget helper library for LazOpenGLContextEx
|
||||
#
|
||||
# Build variants:
|
||||
# make / make linux native Linux build -> ../libqlclglwidget.so
|
||||
# make macos native macOS build (on a Mac) -> ../libqlclglwidget.dylib
|
||||
# make windows qlclglwidget.dll with a MinGW toolchain:
|
||||
# * cross-build from Linux (mingw-w64), OR
|
||||
# * natively on Windows with a Qt MinGW kit that has
|
||||
# no pkg-config — override the variables:
|
||||
# make windows MINGW_CXX=g++ \
|
||||
# WIN_QT_INC=C:/Qt/6.x.x/mingw_64/include \
|
||||
# WIN_QT_LIB=C:/Qt/6.x.x/mingw_64/lib
|
||||
# make windows-native natively on Windows in an MSYS2 MinGW64 shell
|
||||
# (uses pkg-config; auto-selected by plain `make` there)
|
||||
# build-msvc.bat natively on Windows with MSVC (see the .bat file);
|
||||
# required when the target Qt6/libQt6Pas is an MSVC
|
||||
# build — MinGW and MSVC C++ ABIs are incompatible
|
||||
#
|
||||
# make install install the host-platform artifact into $(PREFIX)/lib
|
||||
# (DESTDIR supported; run ldconfig yourself on Linux)
|
||||
# make install-app APPDIR=/path/to/app/dir
|
||||
# copy every built artifact next to the app binary
|
||||
# (the Pascal loader looks there first)
|
||||
# make clean
|
||||
#
|
||||
# Windows cross-build requirements (on the Linux build machine):
|
||||
# - mingw-w64 toolchain: x86_64-w64-mingw32-g++
|
||||
# - a *MinGW* build of Qt6 (headers + import libs), one of:
|
||||
# * Arch AUR: mingw-w64-qt6-base (installs to /usr/x86_64-w64-mingw32)
|
||||
# * MXE (https://mxe.cc) with qt6 (set MINGW_PREFIX to the MXE usr dir)
|
||||
# * a Qt "MinGW" kit copied from a Windows Qt install
|
||||
# (set WIN_QT_INC / WIN_QT_LIB manually)
|
||||
#
|
||||
# Windows native (MSYS2 MinGW64 shell) requirements:
|
||||
# pacman -S mingw-w64-x86_64-gcc mingw-w64-x86_64-qt6-base pkgconf make
|
||||
#
|
||||
# The MinGW-built DLL works only with MinGW Qt6 DLLs on the target machine,
|
||||
# the MSVC-built DLL only with MSVC Qt6 DLLs (incompatible C++ ABIs).
|
||||
#
|
||||
# macOS notes: needs Xcode command line tools + Qt6 with pkg-config files,
|
||||
# e.g. Homebrew: brew install qt pkgconf
|
||||
# export PKG_CONFIG_PATH="$(brew --prefix qt)/libexec/lib/pkgconfig"
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
NAME := qlclglwidget
|
||||
SRC := qlclglwidget.cpp
|
||||
OUTDIR := ..
|
||||
|
||||
PREFIX ?= /usr/local
|
||||
DESTDIR ?=
|
||||
|
||||
CXXFLAGS ?= -O2
|
||||
CXXSTD := -std=c++17
|
||||
|
||||
PKG_CONFIG ?= pkg-config
|
||||
QT_PKGS := Qt6Widgets Qt6OpenGLWidgets
|
||||
|
||||
# Windows cross toolchain / Qt location (override as needed)
|
||||
MINGW_CXX ?= x86_64-w64-mingw32-g++
|
||||
MINGW_PREFIX ?= /usr/x86_64-w64-mingw32
|
||||
WIN_QT_INC ?= $(MINGW_PREFIX)/include/qt6
|
||||
WIN_QT_LIB ?= $(MINGW_PREFIX)/lib
|
||||
WIN_QT_LIBS ?= -lQt6Widgets -lQt6OpenGLWidgets -lQt6Gui -lQt6Core
|
||||
|
||||
LINUX_OUT := $(OUTDIR)/lib$(NAME).so
|
||||
MACOS_OUT := $(OUTDIR)/lib$(NAME).dylib
|
||||
WIN_OUT := $(OUTDIR)/$(NAME).dll
|
||||
|
||||
UNAME := $(shell uname -s)
|
||||
ifeq ($(UNAME),Darwin)
|
||||
HOST_OUT := $(MACOS_OUT)
|
||||
HOST_TARGET := macos
|
||||
else ifneq (,$(findstring MINGW,$(UNAME))$(findstring MSYS,$(UNAME)))
|
||||
# MSYS2 MinGW64 shell on Windows
|
||||
HOST_OUT := $(WIN_OUT)
|
||||
HOST_TARGET := windows-native
|
||||
else
|
||||
HOST_OUT := $(LINUX_OUT)
|
||||
HOST_TARGET := linux
|
||||
endif
|
||||
|
||||
.PHONY: all linux macos windows windows-native install install-app clean
|
||||
|
||||
all: $(HOST_TARGET)
|
||||
|
||||
linux: $(LINUX_OUT)
|
||||
|
||||
$(LINUX_OUT): $(SRC)
|
||||
$(CXX) $(CXXFLAGS) $(CXXSTD) -shared -fPIC \
|
||||
$(shell $(PKG_CONFIG) --cflags $(QT_PKGS)) \
|
||||
-o $@ $(SRC) \
|
||||
$(shell $(PKG_CONFIG) --libs $(QT_PKGS))
|
||||
|
||||
macos: $(MACOS_OUT)
|
||||
|
||||
$(MACOS_OUT): $(SRC)
|
||||
$(CXX) $(CXXFLAGS) $(CXXSTD) -dynamiclib -fPIC \
|
||||
$(shell $(PKG_CONFIG) --cflags $(QT_PKGS)) \
|
||||
-o $@ $(SRC) \
|
||||
$(shell $(PKG_CONFIG) --libs $(QT_PKGS)) \
|
||||
-Wl,-install_name,@rpath/lib$(NAME).dylib
|
||||
|
||||
# MinGW build: cross from Linux, or native on Windows with a Qt MinGW kit
|
||||
# (no pkg-config needed — plain include/lib paths)
|
||||
windows: $(WIN_OUT)
|
||||
|
||||
$(WIN_OUT): $(SRC)
|
||||
$(MINGW_CXX) $(CXXFLAGS) $(CXXSTD) -shared \
|
||||
-I$(WIN_QT_INC) \
|
||||
-I$(WIN_QT_INC)/QtCore \
|
||||
-I$(WIN_QT_INC)/QtGui \
|
||||
-I$(WIN_QT_INC)/QtWidgets \
|
||||
-I$(WIN_QT_INC)/QtOpenGL \
|
||||
-I$(WIN_QT_INC)/QtOpenGLWidgets \
|
||||
-o $@ $(SRC) \
|
||||
-L$(WIN_QT_LIB) $(WIN_QT_LIBS) \
|
||||
-static-libgcc -static-libstdc++ \
|
||||
-Wl,--out-implib,$(OUTDIR)/lib$(NAME).dll.a
|
||||
|
||||
# native Windows build in an MSYS2 MinGW64 shell (pkg-config available)
|
||||
windows-native:
|
||||
$(CXX) $(CXXFLAGS) $(CXXSTD) -shared \
|
||||
$(shell $(PKG_CONFIG) --cflags $(QT_PKGS)) \
|
||||
-o $(WIN_OUT) $(SRC) \
|
||||
$(shell $(PKG_CONFIG) --libs $(QT_PKGS)) \
|
||||
-static-libgcc -static-libstdc++ \
|
||||
-Wl,--out-implib,$(OUTDIR)/lib$(NAME).dll.a
|
||||
|
||||
install: $(HOST_OUT)
|
||||
install -d $(DESTDIR)$(PREFIX)/lib
|
||||
install -m755 $(HOST_OUT) $(DESTDIR)$(PREFIX)/lib/
|
||||
|
||||
install-app:
|
||||
@test -n "$(APPDIR)" || { echo "usage: make install-app APPDIR=/path/to/app/dir"; exit 1; }
|
||||
install -d $(APPDIR)
|
||||
@for f in $(LINUX_OUT) $(MACOS_OUT) $(WIN_OUT); do \
|
||||
if [ -f $$f ]; then install -m755 $$f $(APPDIR)/ && echo "installed $$f -> $(APPDIR)/"; fi; \
|
||||
done
|
||||
|
||||
clean:
|
||||
rm -f $(LINUX_OUT) $(MACOS_OUT) $(WIN_OUT) \
|
||||
$(OUTDIR)/lib$(NAME).dll.a $(OUTDIR)/$(NAME).lib $(OUTDIR)/$(NAME).exp \
|
||||
$(NAME).obj
|
||||
@@ -0,0 +1,43 @@
|
||||
@echo off
|
||||
rem ---------------------------------------------------------------------------
|
||||
rem Builds qlclglwidget.dll natively on Windows with MSVC.
|
||||
rem
|
||||
rem Use this when the target Qt6 / libQt6Pas is an MSVC build (the official
|
||||
rem Qt online-installer binaries are MSVC). A MinGW-built DLL cannot link
|
||||
rem against MSVC Qt DLLs - the C++ ABIs are incompatible.
|
||||
rem
|
||||
rem How to run:
|
||||
rem 1. Open "x64 Native Tools Command Prompt for VS" (vcvars64 environment).
|
||||
rem 2. set QTDIR=C:\Qt\6.7.2\msvc2019_64 (your MSVC Qt kit)
|
||||
rem 3. build-msvc.bat
|
||||
rem
|
||||
rem Output: ..\qlclglwidget.dll (put it next to the application executable).
|
||||
rem ---------------------------------------------------------------------------
|
||||
|
||||
if "%QTDIR%"=="" (
|
||||
echo error: set QTDIR to your MSVC Qt kit first, e.g.:
|
||||
echo set QTDIR=C:\Qt\6.7.2\msvc2019_64
|
||||
exit /b 1
|
||||
)
|
||||
if not exist "%QTDIR%\include\QtOpenGLWidgets" (
|
||||
echo error: %QTDIR%\include\QtOpenGLWidgets not found - is QTDIR an MSVC Qt6 kit?
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
cl /nologo /LD /EHsc /std:c++17 /permissive- /Zc:__cplusplus /MD /O2 ^
|
||||
/I"%QTDIR%\include" ^
|
||||
/I"%QTDIR%\include\QtCore" ^
|
||||
/I"%QTDIR%\include\QtGui" ^
|
||||
/I"%QTDIR%\include\QtWidgets" ^
|
||||
/I"%QTDIR%\include\QtOpenGL" ^
|
||||
/I"%QTDIR%\include\QtOpenGLWidgets" ^
|
||||
qlclglwidget.cpp /Fe:..\qlclglwidget.dll ^
|
||||
/link /LIBPATH:"%QTDIR%\lib" Qt6Widgets.lib Qt6OpenGLWidgets.lib Qt6Gui.lib Qt6Core.lib
|
||||
|
||||
if errorlevel 1 (
|
||||
echo build FAILED
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
del qlclglwidget.obj ..\qlclglwidget.exp ..\qlclglwidget.lib 2>nul
|
||||
echo built ..\qlclglwidget.dll
|
||||
@@ -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