Files
ew8bakandClaude Fable 5 ac46e3d13a 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>
2026-07-03 15:00:26 +03:00

7.1 KiB
Raw Permalink Blame History

LazOpenGLContextEx

Форк штатного пакета LazOpenGLContext (Lazarus components/opengl), в котором Qt5/Qt6-бэкенд построен на настоящем QOpenGLWidget вместо GLX.

Зачем

Штатный TOpenGLControl под --ws=qt5/qt6 работает только в X11-сессии и вообще не собирается с Qt-виджетсетом на Windows/macOS: он руками создаёт GLX-контекст на winId(). Важно: QLCLOpenGLWidget из libQt6Pas — это не QOpenGLWidget, а голый QWidget с WA_NativeWindow и paintEngine()=nullptr, т.е. просто мишень для внешнего GLX-рендера (вызов на нём методов QOpenGLWidget = access violation — проверено).

Здесь контекст создаёт сам Qt через платформенный API:

Платформа Контекст
Linux Wayland EGL
Linux X11 GLX
Windows (ws=qt6) WGL
macOS (ws=qt6) CGL

Бэкенды gtk2/gtk3/win32/cocoa скопированы из штатного пакета без изменений (юниты переименованы с суффиксом Ex, чтобы не конфликтовать со стоковым пакетом).

Состав

  • openglcontextex.pasTOpenGLControl (класс называется так же, юнит OpenGLContextEx).
  • glqtnativecontext.pas — Qt-бэкенд поверх QOpenGLWidget.
  • csrc/qlclglwidget.cppвспомогательная C++-библиотека libqlclglwidget.so: наследник QOpenGLWidget с paintGL-хуком в Pascal + плоские экспорты (makeCurrent, doneCurrent, isValid, setFormat, defaultFramebufferObject, devicePixelRatioF). Грузится в рантайме: сначала рядом с исполняемым файлом, затем по системным путям.

Сборка вспомогательной библиотеки (csrc/)

Где собираем Команда Результат
Linux make -C csrc libqlclglwidget.so
macOS make -C csrc (авто) или make -C csrc macos libqlclglwidget.dylib
Linux → Windows (кросс) make -C csrc windows qlclglwidget.dll (MinGW)
Windows, MSYS2 MinGW64 shell make -C csrc (авто) или make -C csrc windows-native qlclglwidget.dll (MinGW)
Windows, Qt MinGW-kit без pkg-config make -C csrc windows MINGW_CXX=g++ WIN_QT_INC=C:/Qt/6.x/mingw_64/include WIN_QT_LIB=C:/Qt/6.x/mingw_64/lib qlclglwidget.dll (MinGW)
Windows, MSVC csrc\build-msvc.bat из «x64 Native Tools Command Prompt» с выставленным QTDIR qlclglwidget.dll (MSVC)

Плюс:

make -C csrc install        # установить в /usr/local/lib (PREFIX/DESTDIR поддерживаются)
make -C csrc install-app APPDIR=/путь/к/бинарнику   # положить рядом с приложением

Для кросс-сборки нужен тулчейн mingw-w64 и MinGW-сборка Qt6 (AUR mingw-w64-qt6-base, MXE, либо Qt MinGW-kit; пути переопределяются MINGW_PREFIX/WIN_QT_INC/WIN_QT_LIB). Для MSYS2: pacman -S mingw-w64-x86_64-gcc mingw-w64-x86_64-qt6-base pkgconf make.

ABI: MinGW-DLL работает только с MinGW-сборкой Qt6 на целевой машине, MSVC-DLL — только с MSVC-Qt (официальные бинарники Qt из online-инсталлятора — MSVC). Выбирайте вариант под ту Qt6, с которой собрана libQt6Pas приложения.

Использование

В проекте: зависимость LazOpenGLContextLazOpenGLContextEx, в uses: OpenGLContextOpenGLContextEx.

make -C csrc
lazbuild --ws=qt6 lazopenglcontextex.lpk
make -C csrc install-app APPDIR=<каталог с бинарником приложения>

Как устроен Qt-бэкенд

  • Qt сам делает контекст текущим и биндит FBO виджета перед вызовом paintGL(); оттуда доставляется LM_PAINT, так что весь рендер в OnPaint работает как раньше.
  • SwapBuffers внутри paint — no-op (композитингом FBO занимается Qt после возврата из paintGL); вне paint — планирует перерисовку (QWidget::update).
  • MakeCurrent/ReleaseContext вне paint → QOpenGLWidget::makeCurrent/doneCurrent. До первого показа виджета контекста ещё нет — MakeCurrent вернёт False.
  • LOpenGLViewport (AutoResizeViewport) умножает координаты на devicePixelRatio.

Ограничения / нюансы

  • Не вызывать glBindFramebuffer(..., 0) — QOpenGLWidget рендерит в свой FBO.
  • Альфа фреймбуфера = прозрачность виджета. Qt композитит FBO с учётом альфы; легаси-GL-код после блендинга оставляет alpha<1, и виджет просвечивал бы насквозь. Поэтому обёртка после каждого paintGL принудительно заливает альфу единицей (QLCLGLWidget_setForceOpaque, включено по умолчанию). Отключается автоматически, если контрол запросил AlphaBits > 0 — тогда альфа остаётся под контролем приложения.
  • SharedControl игнорируется: Qt автоматически шарит контексты всех QOpenGLWidget внутри одного top-level окна. Для шаринга между окнами нужно выставить Qt::AA_ShareOpenGLContexts до создания QApplication.
  • Windows + qt6: чтобы Qt не выбрал ANGLE/GLES (где нет immediate mode), выставить QT_OPENGL=desktop или атрибут AA_UseDesktopOpenGL.
  • HiDPI (devicePixelRatio > 1): FBO больше логического размера виджета. Явные вызовы glViewport в коде приложения должны умножать на dpr сами.
  • AUXBuffers не поддерживаются QSurfaceFormat (игнорируются).
  • Qt4 (LCLQT), gtk1 и carbon из форка выброшены.

Статус проверки

  • Linux Wayland (KDE, qt6): спектр EWSDR рендерится через GL, стартует без ошибок, скриншот подтверждён (2026-07-03).
  • Linux XWayland/xcb: стартует без ошибок.
  • Windows/macOS: не проверялось (build.sh под них ещё нет — нужен аналог с MSVC/clang и Qt-заголовками).