Implement hardware accelerated rendering
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
This implements GL accelerated rendering using the KHR_platform_gbm EGL extension [0] and arcan_shmifext_signal_planes(). While for offscreen surfaces we can simply delegate to QEGLPbuffer from Qt's own EGL convenience helpers, for onscreen windows we have to add the slightly more involved integration with GBM surfaces. [0] https://registry.khronos.org/EGL/extensions/KHR/EGL_KHR_platform_gbm.txt
This commit is contained in:
parent
7cfe2e5e12
commit
3e35da596b
14 changed files with 332 additions and 18 deletions
|
|
@ -8,13 +8,14 @@ set(CMAKE_CXX_STANDARD 20)
|
|||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
||||
find_package("Qt${TARGET_QT_VERSION}" CONFIG REQUIRED COMPONENTS Core Gui)
|
||||
find_package("Qt${TARGET_QT_VERSION}" CONFIG REQUIRED COMPONENTS Core Gui OpenGL)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(arcan-shmif REQUIRED IMPORTED_TARGET arcan-shmif)
|
||||
pkg_check_modules(arcan-shmif-ext REQUIRED IMPORTED_TARGET arcan-shmif-ext)
|
||||
|
||||
file(GLOB_RECURSE SRCS "src/*.cpp" "src/*.h")
|
||||
|
||||
list(APPEND LINK_LIBS Qt::Core Qt::CorePrivate Qt::Gui Qt::GuiPrivate PkgConfig::arcan-shmif)
|
||||
list(APPEND LINK_LIBS Qt::Core Qt::CorePrivate Qt::Gui Qt::GuiPrivate Qt::OpenGL PkgConfig::arcan-shmif PkgConfig::arcan-shmif-ext)
|
||||
|
||||
if(TARGET_QT_VERSION GREATER_EQUAL 6)
|
||||
find_package(WrapFreetype)
|
||||
|
|
@ -26,8 +27,16 @@ else()
|
|||
add_library(${PROJECT_NAME} SHARED ${SRCS})
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME qarcan)
|
||||
find_package(Freetype)
|
||||
find_package("Qt${TARGET_QT_VERSION}" CONFIG REQUIRED COMPONENTS FontDatabaseSupport EventDispatcherSupport)
|
||||
list(APPEND LINK_LIBS Freetype::Freetype Qt::FontDatabaseSupportPrivate Qt::EventDispatcherSupportPrivate)
|
||||
find_package("Qt${TARGET_QT_VERSION}" CONFIG REQUIRED COMPONENTS FontDatabaseSupport EventDispatcherSupport EglSupport)
|
||||
list(APPEND LINK_LIBS Freetype::Freetype Qt::FontDatabaseSupportPrivate Qt::EventDispatcherSupportPrivate Qt::EglSupport)
|
||||
|
||||
# workaround for Qt5 not exporting QtEglSupport correctly
|
||||
get_target_property(EGLSUPPORT_INCLUDES Qt5::EglSupport INTERFACE_INCLUDE_DIRECTORIES)
|
||||
foreach(EGLSUPPORT_INCLUDE IN LISTS EGLSUPPORT_INCLUDES)
|
||||
if(EGLSUPPORT_INCLUDE MATCHES "QtEglSupport$")
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE "${EGLSUPPORT_INCLUDE}/${Qt5_VERSION}")
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} ${LINK_LIBS})
|
||||
|
|
|
|||
|
|
@ -46,7 +46,8 @@ export MESA_DEBUG=1 # to debug the EGL integration
|
|||
```
|
||||
|
||||
Each shmif segment is represented as a [QArcanConnection](src/qarcanconnection.cpp), where each event loop runs in its own thread.
|
||||
The QPA entry point is [QArcanIntegration](src/qarcanintegration.cpp), which creates one main segment and requests a new segment from the main segment for each new [QArcanWindow](src/qarcanwindow.cpp). Non-accelerated drawing is done in [QArcanBackingStore](src/qarcanbackingstore.cpp).
|
||||
The QPA entry point is [QArcanIntegration](src/qarcanintegration.cpp), which creates one main segment and requests a new segment from the main segment for each new [QArcanWindow](src/qarcanwindow.cpp).
|
||||
Accelerated drawing is done using the [KHR_platform_gbm](https://registry.khronos.org/EGL/extensions/KHR/EGL_KHR_platform_gbm.txt) EGL extension in [QArcanGLContext](src/qarcanglcontext.cpp). For non-accelerated drawing [QArcanBackingStore](src/qarcanbackingstore.cpp) implements software rendering.
|
||||
The Qt window hierarchy is mirrored as the same shmif segment hierarchy, e.g. segments belonging to popup windows are requested from its parent window segment, not from the main segment.
|
||||
|
||||
For an overview of the most important available QPA events you will want to look at [QWindowSystemInterface](https://code.qt.io/cgit/qt/qtbase.git/tree/src/gui/kernel/qwindowsysteminterface.cpp) and [QPlatformWindow](https://code.qt.io/cgit/qt/qtbase.git/tree/src/gui/kernel/qplatformwindow.cpp).
|
||||
|
|
|
|||
|
|
@ -5,8 +5,6 @@
|
|||
#include <qpa/qplatformbackingstore.h>
|
||||
#include <qpa/qplatformwindow.h>
|
||||
|
||||
#include "qarcanutils.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QArcanBackingStore : public QPlatformBackingStore {
|
||||
|
|
|
|||
|
|
@ -252,6 +252,21 @@ QRect QArcanConnection::geometry() const {
|
|||
return m_geometry;
|
||||
}
|
||||
|
||||
arcan::shmifext_setup_status QArcanConnection::setupExt() {
|
||||
QArcanContGuard _ {&m_cont};
|
||||
if (!ok()) {
|
||||
return arcan::shmifext_setup_status::SHHIFEXT_UNKNOWN;
|
||||
}
|
||||
|
||||
m_ext = arcan::arcan_shmifext_defaults(cont());
|
||||
m_ext.no_context = true;
|
||||
const auto res = arcan::arcan_shmifext_setup(cont(), m_ext);
|
||||
if (res != arcan::SHMIFEXT_OK) {
|
||||
qCDebug(lcQpaArcanShmif(), "Failed to setup EXT: %d", res);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
uint32_t QArcanConnection::nextSegmentCookie() {
|
||||
// we cannot start at 0, because segments without a cookie (that Arcan pushes to us, e.g. on clipboard paste) use 0 already
|
||||
static std::atomic_uint32_t nextSegmentCookie = 1;
|
||||
|
|
|
|||
|
|
@ -54,12 +54,14 @@ public:
|
|||
void run() override;
|
||||
|
||||
QRect geometry() const;
|
||||
arcan::shmifext_setup_status setupExt();
|
||||
private:
|
||||
static uint32_t nextSegmentCookie();
|
||||
void handleInitial();
|
||||
void dispatchListeners(std::function<void(QArcanEventListener *)> fun);
|
||||
|
||||
arcan::arcan_shmif_cont m_cont;
|
||||
struct arcan::arcan_shmifext_setup m_ext;
|
||||
uint8_t m_mouseState[ASHMIF_MSTATE_SZ];
|
||||
std::string m_pendingMessage;
|
||||
std::vector<QArcanEventListener *> m_listeners;
|
||||
|
|
|
|||
110
src/qarcanglcontext.cpp
Normal file
110
src/qarcanglcontext.cpp
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
#include "qarcanglcontext.h"
|
||||
|
||||
#include "qarcanwindow.h"
|
||||
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
|
||||
#include <QtGui/private/qeglpbuffer_p.h>
|
||||
#else
|
||||
#include <QtEglSupport/private/qeglpbuffer_p.h>
|
||||
#endif
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <gbm.h>
|
||||
#include <libdrm/drm.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
Q_LOGGING_CATEGORY(lcQpaArcanGLContext, "qt.qpa.arcan.glcontext")
|
||||
|
||||
QArcanGLContext::QArcanGLContext(QOpenGLContext *context, EGLDisplay display, struct gbm_device *gbmDevice) : QEGLPlatformContext(context->format(), context->shareHandle(), display, nullptr), m_gbmDevice(gbmDevice) {
|
||||
}
|
||||
|
||||
QPlatformOffscreenSurface *QArcanGLContext::createOffscreenSurface(QOffscreenSurface *surface, EGLDisplay display) {
|
||||
QSurfaceFormat fmt = surface->requestedFormat();
|
||||
return new QEGLPbuffer(display, fmt, surface);
|
||||
}
|
||||
|
||||
EGLSurface QArcanGLContext::eglSurfaceForPlatformSurface(QPlatformSurface *surface) {
|
||||
if (surface->surface()->surfaceClass() == QSurface::Window) {
|
||||
return static_cast<QArcanWindow *>(surface)->eglSurface();
|
||||
} else {
|
||||
return static_cast<QEGLPbuffer *>(surface)->pbuffer();
|
||||
}
|
||||
}
|
||||
|
||||
#define DMABUF_PLANES_LIMIT 4
|
||||
// lifted from src/platform/egl-dri/egl_gbm_helper.h
|
||||
bool helper_bo_dmabuf(
|
||||
struct gbm_device *dev,
|
||||
EGLDisplay dpy,
|
||||
size_t w,
|
||||
size_t h,
|
||||
int fmt,
|
||||
struct gbm_bo *bo,
|
||||
struct arcan::shmifext_buffer_plane *planes,
|
||||
size_t *n_planes) {
|
||||
size_t np = gbm_bo_get_plane_count(bo);
|
||||
if (np > DMABUF_PLANES_LIMIT)
|
||||
return false;
|
||||
|
||||
uint64_t mod = gbm_bo_get_modifier(bo);
|
||||
planes[0].w = w;
|
||||
planes[0].h = h;
|
||||
|
||||
uint32_t mod_hi = mod >> 32;
|
||||
uint32_t mod_lo = mod & 0xffffffff;
|
||||
|
||||
for (size_t i = 0; i < np; i++) {
|
||||
union gbm_bo_handle h = gbm_bo_get_handle_for_plane(bo, i);
|
||||
struct drm_prime_handle prime =
|
||||
{.handle = h.u32, .flags = DRM_RDWR | DRM_CLOEXEC};
|
||||
|
||||
if (-1 == ioctl(gbm_device_get_fd(dev), DRM_IOCTL_PRIME_HANDLE_TO_FD, &prime))
|
||||
goto cleanup;
|
||||
|
||||
planes[i].gbm.stride = gbm_bo_get_stride_for_plane(bo, i);
|
||||
planes[i].gbm.offset = gbm_bo_get_offset(bo, i);
|
||||
planes[i].gbm.format = fmt;
|
||||
planes[i].fd = prime.fd;
|
||||
planes[i].gbm.mod_hi = mod_hi;
|
||||
planes[i].gbm.mod_lo = mod_lo;
|
||||
}
|
||||
|
||||
*n_planes = np;
|
||||
return true;
|
||||
|
||||
cleanup:
|
||||
for (size_t i = 0; i < np; i++)
|
||||
if (-1 != planes[i].fd)
|
||||
close(planes[i].fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
void QArcanGLContext::swapBuffers(QPlatformSurface *surface) {
|
||||
QEGLPlatformContext::swapBuffers(surface);
|
||||
if (surface->surface()->surfaceClass() != QSurface::Window) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto window = static_cast<QArcanWindow *>(surface);
|
||||
QArcanContGuard _ {window->cont()->cont()};
|
||||
|
||||
auto gbmSurface = window->gbmSurface();
|
||||
|
||||
m_gbmBo = gbm_surface_lock_front_buffer(gbmSurface);
|
||||
if (!m_gbmBo) {
|
||||
qCWarning(lcQpaArcanGLContext(), "Failed to lock front buffer");
|
||||
return;
|
||||
}
|
||||
uint32_t fmt = gbm_bo_get_format(m_gbmBo);
|
||||
struct arcan::shmifext_buffer_plane planes[DMABUF_PLANES_LIMIT];
|
||||
size_t nPlanes;
|
||||
if (!helper_bo_dmabuf(m_gbmDevice, window->eglDisplay(), window->window()->width(), window->window()->height(), fmt, m_gbmBo, planes, &nPlanes)) {
|
||||
qCWarning(lcQpaArcanGLContext(), "Failed to get planes");
|
||||
}
|
||||
arcan::arcan_shmifext_signal_planes(window->cont()->cont(), arcan::SHMIF_SIGVID, nPlanes, planes);
|
||||
gbm_surface_release_buffer(gbmSurface, m_gbmBo);
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
32
src/qarcanglcontext.h
Normal file
32
src/qarcanglcontext.h
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
#ifndef QARCANGLCONTEXT_H
|
||||
#define QARCANGLCONTEXT_H
|
||||
|
||||
#include "qarcanutils.h"
|
||||
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
|
||||
#include <QtGui/private/qeglplatformcontext_p.h>
|
||||
#else
|
||||
#include <QtEglSupport/private/qeglplatformcontext_p.h>
|
||||
#endif
|
||||
|
||||
#include <QOffscreenSurface>
|
||||
|
||||
#include "qarcanwindow.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QArcanGLContext : public QEGLPlatformContext {
|
||||
public:
|
||||
QArcanGLContext(QOpenGLContext *context, EGLDisplay display, struct gbm_device *gbmDevice);
|
||||
static QPlatformOffscreenSurface *createOffscreenSurface(QOffscreenSurface *surface, EGLDisplay display);
|
||||
|
||||
EGLSurface eglSurfaceForPlatformSurface(QPlatformSurface *surface) override;
|
||||
void swapBuffers(QPlatformSurface *surface) override;
|
||||
private:
|
||||
struct gbm_device *m_gbmDevice = nullptr;
|
||||
struct gbm_bo *m_gbmBo = nullptr;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QARCANGLCONTEXT_H
|
||||
|
|
@ -34,7 +34,9 @@
|
|||
|
||||
#include "qarcanclipboard.h"
|
||||
#include "qarcandrag.h"
|
||||
#include "qarcanglcontext.h"
|
||||
#include "qarcannativeinterface.h"
|
||||
#include "qarcanwindow.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
|
|
@ -72,8 +74,12 @@ bool QArcanIntegration::hasCapability(QPlatformIntegration::Capability cap) cons
|
|||
return true;
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
|
||||
case RhiBasedRendering:
|
||||
return false;
|
||||
return true;
|
||||
#endif
|
||||
case OpenGL:
|
||||
return true;
|
||||
case ThreadedOpenGL:
|
||||
return true;
|
||||
default:
|
||||
return QPlatformIntegration::hasCapability(cap);
|
||||
}
|
||||
|
|
@ -98,7 +104,11 @@ QPlatformWindow *QArcanIntegration::createPlatformWindow(QWindow *window) const
|
|||
}
|
||||
}
|
||||
|
||||
QArcanWindow *w = new QArcanWindow(window, cont, std::bind_front(&QArcanIntegration::notifyClipboard, this), m_contStealAvailable);
|
||||
if (window->supportsOpenGL()) {
|
||||
m_primaryScreen->setupExt();
|
||||
}
|
||||
|
||||
QArcanWindow *w = new QArcanWindow(window, cont, std::bind_front(&QArcanIntegration::notifyClipboard, this), m_contStealAvailable, window->supportsOpenGL(), m_primaryScreen->eglDisplay(), m_primaryScreen->gbmDevice());
|
||||
// Only allow single segment mode on the first window request.
|
||||
// Some simple WMs may not accept_target() for subsegments in general,
|
||||
// so in that case we can fallback to the main segment.
|
||||
|
|
@ -116,6 +126,20 @@ QPlatformBackingStore *QArcanIntegration::createPlatformBackingStore(QWindow *wi
|
|||
return new QArcanBackingStore(window);
|
||||
}
|
||||
|
||||
QPlatformOpenGLContext *QArcanIntegration::createPlatformOpenGLContext(QOpenGLContext *context) const {
|
||||
if (!m_primaryScreen->setupExt()) {
|
||||
return nullptr;
|
||||
}
|
||||
return new QArcanGLContext(context, m_primaryScreen->eglDisplay(), m_primaryScreen->gbmDevice());
|
||||
}
|
||||
|
||||
QPlatformOffscreenSurface *QArcanIntegration::createPlatformOffscreenSurface(QOffscreenSurface *surface) const {
|
||||
if (!m_primaryScreen->setupExt()) {
|
||||
return nullptr;
|
||||
}
|
||||
return QArcanGLContext::createOffscreenSurface(surface, m_primaryScreen->eglDisplay());
|
||||
}
|
||||
|
||||
QAbstractEventDispatcher *QArcanIntegration::createEventDispatcher() const {
|
||||
return QtGenericUnixDispatcher::createUnixEventDispatcher();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
#include "qarcanconnection.h"
|
||||
#include "qarcanscreen.h"
|
||||
#include "qarcanutils.h"
|
||||
#include "qarcanwindow.h"
|
||||
|
||||
#include <qpa/qplatformintegration.h>
|
||||
#include <qpa/qplatformscreen.h>
|
||||
|
|
@ -26,6 +25,8 @@ public:
|
|||
|
||||
QPlatformWindow *createPlatformWindow(QWindow *window) const override;
|
||||
QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const override;
|
||||
QPlatformOpenGLContext *createPlatformOpenGLContext(QOpenGLContext *context) const override;
|
||||
QPlatformOffscreenSurface *createPlatformOffscreenSurface(QOffscreenSurface *surface) const override;
|
||||
QAbstractEventDispatcher *createEventDispatcher() const override;
|
||||
QPlatformClipboard *clipboard() const override;
|
||||
QPlatformDrag *drag() const override;
|
||||
|
|
|
|||
|
|
@ -57,10 +57,47 @@ void QArcanScreen::setPpcm(float ppcm) {
|
|||
// sanity check that we do not get a bogus value
|
||||
return;
|
||||
}
|
||||
m_dpi = ppcm * QArcan::cmPerInch;
|
||||
const auto newDpi = ppcm * QArcan::cmPerInch;
|
||||
if (newDpi == m_dpi) {
|
||||
return;
|
||||
}
|
||||
m_dpi = newDpi;
|
||||
qCDebug(lcQpaArcanScreen(), "New DPI: %f", m_dpi);
|
||||
// TODO: Why does Qt not react to these DPI change events?
|
||||
QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(this->screen(), m_dpi, m_dpi);
|
||||
}
|
||||
|
||||
bool QArcanScreen::setupExt() {
|
||||
if (m_eglDisplay != EGL_NO_DISPLAY) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int devFd = open("/dev/dri/renderD128", O_RDWR | O_CLOEXEC);
|
||||
if (devFd < -1) {
|
||||
qCWarning(lcQpaArcanScreen(), "Failed to open render device");
|
||||
return false;
|
||||
}
|
||||
m_gbmDevice = gbm_create_device(devFd);
|
||||
if (!m_gbmDevice) {
|
||||
qCWarning(lcQpaArcanScreen(), "Failed to create GBM device");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_eglDisplay = eglGetPlatformDisplay(EGL_PLATFORM_GBM_KHR, m_gbmDevice, nullptr);
|
||||
if (m_eglDisplay == EGL_NO_DISPLAY) {
|
||||
qCWarning(lcQpaArcanScreen(), "Failed to get platform display");
|
||||
return false;
|
||||
}
|
||||
EGLint major, minor;
|
||||
return eglInitialize(m_eglDisplay, &major, &minor);
|
||||
}
|
||||
|
||||
struct gbm_device *QArcanScreen::gbmDevice() const {
|
||||
return m_gbmDevice;
|
||||
}
|
||||
|
||||
EGLDisplay QArcanScreen::eglDisplay() const {
|
||||
return m_eglDisplay;
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
|
|
|||
|
|
@ -1,10 +1,17 @@
|
|||
#ifndef QARCANSCREEN_H
|
||||
#define QARCANSCREEN_H
|
||||
|
||||
#include "qarcanutils.h"
|
||||
#include <fcntl.h>
|
||||
#include <gbm.h>
|
||||
#include <qpa/qplatformscreen.h>
|
||||
|
||||
#include "qarcanconnection.h"
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
|
||||
#include <QtGui/private/qeglconvenience_p.h>
|
||||
#else
|
||||
#include <QtEglSupport/private/qeglconvenience_p.h>
|
||||
#endif
|
||||
|
||||
#include "qarcanutils.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
|
|
@ -22,12 +29,19 @@ public:
|
|||
void setGeometry(QRect geometry);
|
||||
void setRate(int rate);
|
||||
void setPpcm(float ppcm);
|
||||
|
||||
bool setupExt();
|
||||
struct gbm_device *gbmDevice() const;
|
||||
EGLDisplay eglDisplay() const;
|
||||
private:
|
||||
QRect m_geometry {0, 0, 1920, 1080};
|
||||
int m_depth = 32;
|
||||
int m_rate = 60;
|
||||
float m_dpi = 96;
|
||||
QImage::Format m_format = QImage::Format_ARGB32_Premultiplied; // TODO: Support other formats
|
||||
QImage::Format m_format = QImage::Format_ARGB32_Premultiplied;
|
||||
|
||||
struct gbm_device *m_gbmDevice = nullptr;
|
||||
EGLDisplay m_eglDisplay = EGL_NO_DISPLAY;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
#ifndef QARCANUTILS_H
|
||||
#define QARCANUTILS_H
|
||||
|
||||
#define WANT_ARCAN_SHMIF_HELPER
|
||||
|
||||
#include <QLoggingCategory>
|
||||
#include <qpa/qplatformscreen.h>
|
||||
#include <qpa/qplatformwindow.h>
|
||||
|
|
|
|||
|
|
@ -1,16 +1,19 @@
|
|||
#include "qarcanwindow.h"
|
||||
|
||||
#include <gbm.h>
|
||||
#include <qpa/qwindowsysteminterface.h>
|
||||
|
||||
#include "qarcanscreen.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
Q_LOGGING_CATEGORY(lcQpaArcanEvents, "qt.qpa.arcan.events")
|
||||
Q_LOGGING_CATEGORY(lcQpaArcanWindow, "qt.qpa.arcan.window")
|
||||
|
||||
QArcanWindow::QArcanWindow(QWindow *window, std::shared_ptr<QArcanConnection> parentCont, QArcanClipboard::ClipboardHandlerFn clipboardHandler, bool maySteal)
|
||||
: QPlatformWindow(window), m_parentCont(parentCont), m_clipboardCallback(clipboardHandler), m_steal(maySteal) {
|
||||
QArcanWindow::QArcanWindow(QWindow *window, std::shared_ptr<QArcanConnection> parentCont, QArcanClipboard::ClipboardHandlerFn clipboardHandler, bool maySteal, bool setupExt, EGLDisplay display, struct gbm_device *gbmDevice)
|
||||
: QPlatformWindow(window), m_parentCont(parentCont), m_clipboardCallback(clipboardHandler), m_steal(maySteal), m_setupExt(setupExt), m_eglDisplay(display), m_gbmDevice(gbmDevice) {
|
||||
m_parentCont->addEventListener(this);
|
||||
QPoint pos;
|
||||
if (window->flags().testFlag(Qt::Popup)) {
|
||||
|
|
@ -114,6 +117,10 @@ void QArcanWindow::handleNewSegment(uint32_t cookie, bool accepted) {
|
|||
}
|
||||
// cont.hints = arcan::SHMIF_RHINT_VSIGNAL_EV;
|
||||
m_cont = std::make_shared<QArcanConnection>(std::move(cont), this);
|
||||
|
||||
if (m_setupExt) {
|
||||
setupExt();
|
||||
}
|
||||
}
|
||||
|
||||
void QArcanWindow::handleMouseMoveEvent(int16_t x, int16_t y) {
|
||||
|
|
@ -148,6 +155,7 @@ void QArcanWindow::handleDisplayHint(int32_t width, int32_t height, int flags, f
|
|||
#endif
|
||||
}
|
||||
|
||||
window()->resize(width, height);
|
||||
m_cont->resize(width, height);
|
||||
}
|
||||
|
||||
|
|
@ -205,8 +213,14 @@ void QArcanWindow::handleTermination() {
|
|||
|
||||
void QArcanWindow::handleResizeSuccess(int width, int height) {
|
||||
// trigger redraw
|
||||
window()->resize(width, height);
|
||||
|
||||
QWindowSystemInterface::handleExposeEvent(window(), QRegion(0, 0, width, height));
|
||||
qCDebug(lcQpaArcanEvents(), "Resize successful");
|
||||
|
||||
if (m_setupExt) {
|
||||
createSurface();
|
||||
}
|
||||
}
|
||||
|
||||
void QArcanWindow::requestActivateWindow() {
|
||||
|
|
@ -236,7 +250,47 @@ void QArcanWindow::syncViewport(std::optional<QRect> rect, bool visible) {
|
|||
QArcanContGuard _ {m_cont->cont()};
|
||||
m_cont->requestViewport(m_parentCont->segmentToken(), r.x(), r.y(), r.width(), r.height(), visible);
|
||||
}
|
||||
m_cont->resize(r.width(), r.height(), false);
|
||||
m_cont->resize(r.width(), r.height(), true);
|
||||
}
|
||||
}
|
||||
|
||||
EGLSurface QArcanWindow::eglSurface() const {
|
||||
return m_eglSurface;
|
||||
}
|
||||
|
||||
EGLDisplay QArcanWindow::eglDisplay() const {
|
||||
return m_eglDisplay;
|
||||
}
|
||||
|
||||
struct gbm_surface *QArcanWindow::gbmSurface() const {
|
||||
return m_gbmSurface;
|
||||
}
|
||||
|
||||
void QArcanWindow::setupExt() {
|
||||
m_cont->setupExt();
|
||||
QArcanContGuard _ {m_cont->cont()};
|
||||
createSurface();
|
||||
}
|
||||
|
||||
void QArcanWindow::createSurface() {
|
||||
const auto fmt = window()->requestedFormat();
|
||||
const auto display = eglDisplay();
|
||||
auto config = q_configFromGLFormat(display, fmt);
|
||||
EGLint nativeFormat;
|
||||
eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &nativeFormat);
|
||||
if (m_gbmSurface) {
|
||||
// gbm_surface_destroy(m_gbmSurface);
|
||||
}
|
||||
// create GBM surface
|
||||
m_gbmSurface = gbm_surface_create(m_gbmDevice, window()->width(), window()->height(), nativeFormat, GBM_BO_USE_RENDERING);
|
||||
if (!m_gbmSurface) {
|
||||
qCWarning(lcQpaArcanWindow(), "Failed to create GBM surface");
|
||||
return;
|
||||
}
|
||||
// create platform window surface
|
||||
m_eglSurface = eglCreatePlatformWindowSurface(display, config, m_gbmSurface, nullptr);
|
||||
if (m_eglSurface == EGL_NO_SURFACE) {
|
||||
qCWarning(lcQpaArcanWindow(), "Failed to create platform surface");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#ifndef QARCANWINDOW_H
|
||||
#define QARCANWINDOW_H
|
||||
|
||||
#include <EGL/egl.h>
|
||||
#include <qpa/qplatformscreen.h>
|
||||
#include <qpa/qplatformwindow.h>
|
||||
|
||||
|
|
@ -8,12 +9,13 @@
|
|||
#include "qarcanconnection.h"
|
||||
#include "qarcanutils.h"
|
||||
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QArcanWindow : public QPlatformWindow,
|
||||
public QArcanEventListener {
|
||||
public:
|
||||
explicit QArcanWindow(QWindow *window, std::shared_ptr<QArcanConnection> parentCont, QArcanClipboard::ClipboardHandlerFn clipboardHandler, bool maySteal = false);
|
||||
explicit QArcanWindow(QWindow *window, std::shared_ptr<QArcanConnection> parentCont, QArcanClipboard::ClipboardHandlerFn clipboardHandler, bool maySteal = false, bool setupExt = false, EGLDisplay display = EGL_NO_DISPLAY, struct gbm_device *gbmDevice = nullptr);
|
||||
~QArcanWindow();
|
||||
|
||||
// QPlatformWindow
|
||||
|
|
@ -40,10 +42,17 @@ public:
|
|||
|
||||
QPoint transientPos() const;
|
||||
void syncViewport(std::optional<QRect> rect = std::nullopt, bool visible = true);
|
||||
EGLSurface eglSurface() const;
|
||||
EGLDisplay eglDisplay() const;
|
||||
struct gbm_surface *gbmSurface() const;
|
||||
protected:
|
||||
private:
|
||||
void setupExt();
|
||||
void createSurface();
|
||||
|
||||
std::shared_ptr<QArcanConnection> m_cont;
|
||||
std::shared_ptr<QArcanConnection> m_parentCont;
|
||||
QOpenGLContext *m_context = nullptr;
|
||||
std::unique_ptr<QArcanClipboardHandler> m_clipboardCont;
|
||||
QArcanClipboard::ClipboardHandlerFn m_clipboardCallback;
|
||||
uint32_t m_cookie = QArcan::invalidSegmentCookie;
|
||||
|
|
@ -55,6 +64,12 @@ private:
|
|||
QString m_windowtitle;
|
||||
bool m_valid = true;
|
||||
bool m_steal = false;
|
||||
|
||||
bool m_setupExt = false;
|
||||
EGLSurface m_eglSurface = EGL_NO_SURFACE;
|
||||
EGLDisplay m_eglDisplay = EGL_NO_DISPLAY;
|
||||
struct gbm_device *m_gbmDevice = nullptr;
|
||||
struct gbm_surface *m_gbmSurface = nullptr;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue