diff options
Diffstat (limited to 'src/platformsupport')
25 files changed, 1020 insertions, 199 deletions
diff --git a/src/platformsupport/devicediscovery/qdevicediscovery_static.cpp b/src/platformsupport/devicediscovery/qdevicediscovery_static.cpp index 5c72dbe7e2..a1575677f5 100644 --- a/src/platformsupport/devicediscovery/qdevicediscovery_static.cpp +++ b/src/platformsupport/devicediscovery/qdevicediscovery_static.cpp @@ -47,7 +47,11 @@ #include <QLoggingCategory> #include <QtCore/private/qcore_unix_p.h> +#ifdef Q_OS_FREEBSD +#include <dev/evdev/input.h> +#else #include <linux/input.h> +#endif #include <fcntl.h> /* android (and perhaps some other linux-derived stuff) don't define everything diff --git a/src/platformsupport/eglconvenience/qeglconvenience.cpp b/src/platformsupport/eglconvenience/qeglconvenience.cpp index da41cfeabf..020d035bf7 100644 --- a/src/platformsupport/eglconvenience/qeglconvenience.cpp +++ b/src/platformsupport/eglconvenience/qeglconvenience.cpp @@ -100,18 +100,24 @@ QVector<EGLint> q_createConfigAttributesFromFormat(const QSurfaceFormat &format) configAttributes.append(EGL_ALPHA_SIZE); configAttributes.append(alphaSize > 0 ? alphaSize : 0); - configAttributes.append(EGL_DEPTH_SIZE); - configAttributes.append(depthSize > 0 ? depthSize : 0); - - configAttributes.append(EGL_STENCIL_SIZE); - configAttributes.append(stencilSize > 0 ? stencilSize : 0); - configAttributes.append(EGL_SAMPLES); configAttributes.append(sampleCount > 0 ? sampleCount : 0); configAttributes.append(EGL_SAMPLE_BUFFERS); configAttributes.append(sampleCount > 0); + if (format.renderableType() != QSurfaceFormat::OpenVG) { + configAttributes.append(EGL_DEPTH_SIZE); + configAttributes.append(depthSize > 0 ? depthSize : 0); + + configAttributes.append(EGL_STENCIL_SIZE); + configAttributes.append(stencilSize > 0 ? stencilSize : 0); + } else { + // OpenVG needs alpha mask for clipping + configAttributes.append(EGL_ALPHA_MASK_SIZE); + configAttributes.append(8); + } + return configAttributes; } diff --git a/src/platformsupport/eglconvenience/qeglplatformcontext.cpp b/src/platformsupport/eglconvenience/qeglplatformcontext.cpp index 6a3bc25418..2d3d91b4fa 100644 --- a/src/platformsupport/eglconvenience/qeglplatformcontext.cpp +++ b/src/platformsupport/eglconvenience/qeglplatformcontext.cpp @@ -167,6 +167,13 @@ void QEGLPlatformContext::init(const QSurfaceFormat &format, QPlatformOpenGLCont : EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR); } } + + // Special Options for OpenVG surfaces + if (m_format.renderableType() == QSurfaceFormat::OpenVG) { + contextAttrs.append(EGL_ALPHA_MASK_SIZE); + contextAttrs.append(8); + } + contextAttrs.append(EGL_NONE); m_contextAttrs = contextAttrs; diff --git a/src/platformsupport/fbconvenience/qfbcursor.cpp b/src/platformsupport/fbconvenience/qfbcursor.cpp index 004c586de3..7daf3f4d0c 100644 --- a/src/platformsupport/fbconvenience/qfbcursor.cpp +++ b/src/platformsupport/fbconvenience/qfbcursor.cpp @@ -60,8 +60,8 @@ QFbCursor::QFbCursor(QFbScreen *screen) mScreen(screen), mDirty(false), mOnScreen(false), - mGraphic(0), - mDeviceListener(0) + mCursorImage(nullptr), + mDeviceListener(nullptr) { QByteArray hideCursorVal = qgetenv("QT_QPA_FB_HIDECURSOR"); if (!hideCursorVal.isEmpty()) @@ -69,7 +69,7 @@ QFbCursor::QFbCursor(QFbScreen *screen) if (!mVisible) return; - mGraphic = new QPlatformCursorImage(0, 0, 0, 0, 0, 0); + mCursorImage = new QPlatformCursorImage(0, 0, 0, 0, 0, 0); setCursor(Qt::ArrowCursor); mDeviceListener = new QFbCursorDeviceListener(this); @@ -85,8 +85,8 @@ QFbCursor::~QFbCursor() QRect QFbCursor::getCurrentRect() { - QRect rect = mGraphic->image()->rect().translated(-mGraphic->hotspot().x(), - -mGraphic->hotspot().y()); + QRect rect = mCursorImage->image()->rect().translated(-mCursorImage->hotspot().x(), + -mCursorImage->hotspot().y()); rect.translate(m_pos); QPoint mScreenOffset = mScreen->geometry().topLeft(); rect.translate(-mScreenOffset); // global to local translation @@ -133,7 +133,7 @@ QRect QFbCursor::drawCursor(QPainter & painter) return QRect(); mPrevRect = mCurrentRect; - painter.drawImage(mPrevRect, *mGraphic->image()); + painter.drawImage(mPrevRect, *mCursorImage->image()); mOnScreen = true; return mPrevRect; } @@ -149,17 +149,17 @@ QRect QFbCursor::dirtyRect() void QFbCursor::setCursor(Qt::CursorShape shape) { - mGraphic->set(shape); + mCursorImage->set(shape); } void QFbCursor::setCursor(const QImage &image, int hotx, int hoty) { - mGraphic->set(image, hotx, hoty); + mCursorImage->set(image, hotx, hoty); } void QFbCursor::setCursor(const uchar *data, const uchar *mask, int width, int height, int hotX, int hotY) { - mGraphic->set(data, mask, width, height, hotX, hotY); + mCursorImage->set(data, mask, width, height, hotX, hotY); } #ifndef QT_NO_CURSOR diff --git a/src/platformsupport/fbconvenience/qfbcursor_p.h b/src/platformsupport/fbconvenience/qfbcursor_p.h index f08babd45b..beda10a5f3 100644 --- a/src/platformsupport/fbconvenience/qfbcursor_p.h +++ b/src/platformsupport/fbconvenience/qfbcursor_p.h @@ -87,11 +87,11 @@ public: virtual QRect drawCursor(QPainter &painter); // input methods - void pointerEvent(const QMouseEvent &event) Q_DECL_OVERRIDE; - QPoint pos() const Q_DECL_OVERRIDE; - void setPos(const QPoint &pos) Q_DECL_OVERRIDE; + void pointerEvent(const QMouseEvent &event) override; + QPoint pos() const override; + void setPos(const QPoint &pos) override; #ifndef QT_NO_CURSOR - void changeCursor(QCursor *widgetCursor, QWindow *window) Q_DECL_OVERRIDE; + void changeCursor(QCursor *widgetCursor, QWindow *window) override; #endif virtual void setDirty(); @@ -113,7 +113,7 @@ private: QRect mPrevRect; // last place the cursor was drawn bool mDirty; bool mOnScreen; - QPlatformCursorImage *mGraphic; + QPlatformCursorImage *mCursorImage; QFbCursorDeviceListener *mDeviceListener; QPoint m_pos; }; @@ -121,4 +121,3 @@ private: QT_END_NAMESPACE #endif // QFBCURSOR_P_H - diff --git a/src/platformsupport/fbconvenience/qfbscreen.cpp b/src/platformsupport/fbconvenience/qfbscreen.cpp index 216f2722a4..2b4498157c 100644 --- a/src/platformsupport/fbconvenience/qfbscreen.cpp +++ b/src/platformsupport/fbconvenience/qfbscreen.cpp @@ -51,19 +51,23 @@ QT_BEGIN_NAMESPACE -QFbScreen::QFbScreen() : mUpdatePending(false), mCursor(0), mGeometry(), mDepth(16), mFormat(QImage::Format_RGB16), mScreenImage(0), mCompositePainter(0), mIsUpToDate(false) +QFbScreen::QFbScreen() + : mUpdatePending(false), + mCursor(0), + mDepth(16), + mFormat(QImage::Format_RGB16), + mPainter(nullptr) { } QFbScreen::~QFbScreen() { - delete mCompositePainter; - delete mScreenImage; + delete mPainter; } void QFbScreen::initializeCompositor() { - mScreenImage = new QImage(mGeometry.size(), mFormat); + mScreenImage = QImage(mGeometry.size(), mFormat); scheduleUpdate(); } @@ -93,7 +97,6 @@ void QFbScreen::addWindow(QFbWindow *window) } } } - invalidateRectCache(); setDirty(window->geometry()); QWindow *w = topWindow(); QWindowSystemInterface::handleWindowActivated(w); @@ -103,7 +106,6 @@ void QFbScreen::addWindow(QFbWindow *window) void QFbScreen::removeWindow(QFbWindow *window) { mWindowStack.removeOne(window); - invalidateRectCache(); setDirty(window->geometry()); QWindow *w = topWindow(); QWindowSystemInterface::handleWindowActivated(w); @@ -116,7 +118,6 @@ void QFbScreen::raise(QFbWindow *window) if (index <= 0) return; mWindowStack.move(index, 0); - invalidateRectCache(); setDirty(window->geometry()); QWindow *w = topWindow(); QWindowSystemInterface::handleWindowActivated(w); @@ -129,7 +130,6 @@ void QFbScreen::lower(QFbWindow *window) if (index == -1 || index == (mWindowStack.size() - 1)) return; mWindowStack.move(index, mWindowStack.size() - 1); - invalidateRectCache(); setDirty(window->geometry()); QWindow *w = topWindow(); QWindowSystemInterface::handleWindowActivated(w); @@ -142,7 +142,7 @@ QWindow *QFbScreen::topWindow() const if (fbw->window()->type() == Qt::Window || fbw->window()->type() == Qt::Dialog) return fbw->window(); } - return 0; + return nullptr; } QWindow *QFbScreen::topLevelAt(const QPoint & p) const @@ -151,14 +151,19 @@ QWindow *QFbScreen::topLevelAt(const QPoint & p) const if (fbw->geometry().contains(p, false) && fbw->window()->isVisible()) return fbw->window(); } - return 0; + return nullptr; +} + +int QFbScreen::windowCount() const +{ + return mWindowStack.count(); } void QFbScreen::setDirty(const QRect &rect) { - QRect intersection = rect.intersected(mGeometry); - QPoint screenOffset = mGeometry.topLeft(); - mRepaintRegion += intersection.translated(-screenOffset); // global to local translation + const QRect intersection = rect.intersected(mGeometry); + const QPoint screenOffset = mGeometry.topLeft(); + mRepaintRegion += intersection.translated(-screenOffset); // global to local translation scheduleUpdate(); } @@ -177,141 +182,81 @@ void QFbScreen::setPhysicalSize(const QSize &size) void QFbScreen::setGeometry(const QRect &rect) { - delete mCompositePainter; - mCompositePainter = 0; - delete mScreenImage; + delete mPainter; + mPainter = nullptr; mGeometry = rect; - mScreenImage = new QImage(mGeometry.size(), mFormat); - invalidateRectCache(); + mScreenImage = QImage(mGeometry.size(), mFormat); QWindowSystemInterface::handleScreenGeometryChange(QPlatformScreen::screen(), geometry(), availableGeometry()); resizeMaximizedWindows(); } -void QFbScreen::generateRects() +bool QFbScreen::initialize() { - mCachedRects.clear(); - QPoint screenOffset = mGeometry.topLeft(); - QRegion remainingScreen(mGeometry.translated(-screenOffset)); // global to local translation - - for (int i = 0; i < mWindowStack.length(); i++) { - if (remainingScreen.isEmpty()) - break; -#if 0 - if (!mWindowStack[i]->isVisible()) - continue; - if (mWindowStack[i]->isMinimized()) - continue; - - if (!mWindowStack[i]->testAttribute(Qt::WA_TranslucentBackground)) { - QRect localGeometry = mWindowStack.at(i)->geometry().translated(-screenOffset); // global to local translation - remainingScreen -= localGeometry; - QRegion windowRegion(localGeometry); - windowRegion -= remainingScreen; - for (const QRect &rect : windowRegion) - mCachedRects += QPair<QRect, int>(rect, i); - } -#endif - } - mCachedRects.reserve(mCachedRects.count() + remainingScreen.rectCount()); - for (const QRect &rect : remainingScreen) - mCachedRects += QPair<QRect, int>(rect, -1); - mIsUpToDate = true; + return true; } QRegion QFbScreen::doRedraw() { - QPoint screenOffset = mGeometry.topLeft(); + const QPoint screenOffset = mGeometry.topLeft(); QRegion touchedRegion; if (mCursor && mCursor->isDirty() && mCursor->isOnScreen()) { - QRect lastCursor = mCursor->dirtyRect(); + const QRect lastCursor = mCursor->dirtyRect(); mRepaintRegion += lastCursor; } - if (mRepaintRegion.isEmpty() && (!mCursor || !mCursor->isDirty())) { + if (mRepaintRegion.isEmpty() && (!mCursor || !mCursor->isDirty())) return touchedRegion; - } - QVector<QRect> rects = mRepaintRegion.rects(); - - if (!mIsUpToDate) - generateRects(); - - if (!mCompositePainter) - mCompositePainter = new QPainter(mScreenImage); + if (!mPainter) + mPainter = new QPainter(&mScreenImage); + const QVector<QRect> rects = mRepaintRegion.rects(); + const QRect screenRect = mGeometry.translated(-screenOffset); for (int rectIndex = 0; rectIndex < mRepaintRegion.rectCount(); rectIndex++) { - QRegion rectRegion = rects[rectIndex]; + const QRect rect = rects[rectIndex].intersected(screenRect); + if (rect.isEmpty()) + continue; - for (int i = 0; i < mCachedRects.length(); i++) { - QRect screenSubRect = mCachedRects[i].first; - int layer = mCachedRects[i].second; - QRegion intersect = rectRegion.intersected(screenSubRect); + mPainter->setCompositionMode(QPainter::CompositionMode_Source); + mPainter->fillRect(rect, mScreenImage.hasAlphaChannel() ? Qt::transparent : Qt::black); - if (intersect.isEmpty()) + for (int layerIndex = mWindowStack.size() - 1; layerIndex != -1; layerIndex--) { + if (!mWindowStack[layerIndex]->window()->isVisible()) continue; - rectRegion -= intersect; - - // we only expect one rectangle, but defensive coding... - for (const QRect &rect : intersect) { - bool firstLayer = true; - if (layer == -1) { - mCompositePainter->setCompositionMode(QPainter::CompositionMode_Source); - mCompositePainter->fillRect(rect, mScreenImage->hasAlphaChannel() ? Qt::transparent : Qt::black); - firstLayer = false; - layer = mWindowStack.size() - 1; - } - - for (int layerIndex = layer; layerIndex != -1; layerIndex--) { - if (!mWindowStack[layerIndex]->window()->isVisible()) - continue; - // if (mWindowStack[layerIndex]->isMinimized()) - // continue; - - QRect windowRect = mWindowStack[layerIndex]->geometry().translated(-screenOffset); - QRect windowIntersect = rect.translated(-windowRect.left(), - -windowRect.top()); - - - QFbBackingStore *backingStore = mWindowStack[layerIndex]->backingStore(); - - if (backingStore) { - backingStore->lock(); - mCompositePainter->drawImage(rect, backingStore->image(), windowIntersect); - backingStore->unlock(); - } - if (firstLayer) { - firstLayer = false; - } - } + const QRect windowRect = mWindowStack[layerIndex]->geometry().translated(-screenOffset); + const QRect windowIntersect = rect.translated(-windowRect.left(), -windowRect.top()); + QFbBackingStore *backingStore = mWindowStack[layerIndex]->backingStore(); + if (backingStore) { + backingStore->lock(); + mPainter->drawImage(rect, backingStore->image(), windowIntersect); + backingStore->unlock(); } } } - QRect cursorRect; if (mCursor && (mCursor->isDirty() || mRepaintRegion.intersects(mCursor->lastPainted()))) { - mCompositePainter->setCompositionMode(QPainter::CompositionMode_SourceOver); - cursorRect = mCursor->drawCursor(*mCompositePainter); - touchedRegion += cursorRect; + mPainter->setCompositionMode(QPainter::CompositionMode_SourceOver); + touchedRegion += mCursor->drawCursor(*mPainter); } touchedRegion += mRepaintRegion; mRepaintRegion = QRegion(); - - -// qDebug() << "QFbScreen::doRedraw" << mWindowStack.size() << mScreenImage->size() << touchedRegion; - return touchedRegion; } QFbWindow *QFbScreen::windowForId(WId wid) const { - for (int i = 0; i < mWindowStack.count(); ++i) + for (int i = 0; i < mWindowStack.count(); ++i) { if (mWindowStack[i]->winId() == wid) return mWindowStack[i]; + } + return nullptr; +} +QFbScreen::Flags QFbScreen::flags() const +{ return 0; } QT_END_NAMESPACE - diff --git a/src/platformsupport/fbconvenience/qfbscreen_p.h b/src/platformsupport/fbconvenience/qfbscreen_p.h index e9b570aa1c..1c27a941cc 100644 --- a/src/platformsupport/fbconvenience/qfbscreen_p.h +++ b/src/platformsupport/fbconvenience/qfbscreen_p.h @@ -66,10 +66,18 @@ class QFbBackingStore; class QFbScreen : public QObject, public QPlatformScreen { Q_OBJECT + public: + enum Flag { + DontForceFirstWindowToFullScreen = 0x01 + }; + Q_DECLARE_FLAGS(Flags, Flag) + QFbScreen(); ~QFbScreen(); + virtual bool initialize(); + QRect geometry() const Q_DECL_OVERRIDE { return mGeometry; } int depth() const Q_DECL_OVERRIDE { return mDepth; } QImage::Format format() const Q_DECL_OVERRIDE { return mFormat; } @@ -85,6 +93,8 @@ public: virtual void raise(QFbWindow *window); virtual void lower(QFbWindow *window); virtual void topWindowChanged(QWindow *) {} + virtual int windowCount() const; + virtual Flags flags() const; void addPendingBackingStore(QFbBackingStore *bs) { mPendingBackingStores << bs; } @@ -112,20 +122,17 @@ protected: int mDepth; QImage::Format mFormat; QSizeF mPhysicalSize; - QImage *mScreenImage; + QImage mScreenImage; private: - void invalidateRectCache() { mIsUpToDate = false; } - void generateRects(); - - QPainter *mCompositePainter; - QVector<QPair<QRect, int> > mCachedRects; + QPainter *mPainter; QList<QFbBackingStore*> mPendingBackingStores; friend class QFbWindow; - bool mIsUpToDate; }; +Q_DECLARE_OPERATORS_FOR_FLAGS(QFbScreen::Flags) + QT_END_NAMESPACE #endif // QFBSCREEN_P_H diff --git a/src/platformsupport/fbconvenience/qfbwindow.cpp b/src/platformsupport/fbconvenience/qfbwindow.cpp index 2d5570fe5d..0be1dad04a 100644 --- a/src/platformsupport/fbconvenience/qfbwindow.cpp +++ b/src/platformsupport/fbconvenience/qfbwindow.cpp @@ -66,7 +66,6 @@ void QFbWindow::setGeometry(const QRect &rect) // store previous geometry for screen update mOldGeometry = geometry(); - platformScreen()->invalidateRectCache(); QWindowSystemInterface::handleGeometryChange(window(), rect); QPlatformWindow::setGeometry(rect); @@ -74,8 +73,13 @@ void QFbWindow::setGeometry(const QRect &rect) void QFbWindow::setVisible(bool visible) { + QFbScreen *fbScreen = platformScreen(); if (visible) { - if (mWindowState & Qt::WindowFullScreen) + bool convOk = false; + static bool envDisableForceFullScreen = qEnvironmentVariableIntValue("QT_QPA_FB_FORCE_FULLSCREEN", &convOk) == 0 && convOk; + const bool platformDisableForceFullScreen = fbScreen->flags().testFlag(QFbScreen::DontForceFirstWindowToFullScreen); + const bool forceFullScreen = !envDisableForceFullScreen && !platformDisableForceFullScreen && fbScreen->windowCount() == 0; + if (forceFullScreen || (mWindowState & Qt::WindowFullScreen)) setGeometry(platformScreen()->geometry()); else if (mWindowState & Qt::WindowMaximized) setGeometry(platformScreen()->availableGeometry()); @@ -83,9 +87,9 @@ void QFbWindow::setVisible(bool visible) QPlatformWindow::setVisible(visible); if (visible) - platformScreen()->addWindow(this); + fbScreen->addWindow(this); else - platformScreen()->removeWindow(this); + fbScreen->removeWindow(this); } @@ -93,14 +97,11 @@ void QFbWindow::setWindowState(Qt::WindowState state) { QPlatformWindow::setWindowState(state); mWindowState = state; - platformScreen()->invalidateRectCache(); } - void QFbWindow::setWindowFlags(Qt::WindowFlags flags) { mWindowFlags = flags; - platformScreen()->invalidateRectCache(); } Qt::WindowFlags QFbWindow::windowFlags() const @@ -120,20 +121,15 @@ void QFbWindow::lower() void QFbWindow::repaint(const QRegion ®ion) { - QRect currentGeometry = geometry(); - - QRect dirtyClient = region.boundingRect(); - QRect dirtyRegion(currentGeometry.left() + dirtyClient.left(), - currentGeometry.top() + dirtyClient.top(), - dirtyClient.width(), - dirtyClient.height()); - QRect mOldGeometryLocal = mOldGeometry; + const QRect currentGeometry = geometry(); + const QRect dirtyClient = region.boundingRect(); + const QRect dirtyRegion = dirtyClient.translated(currentGeometry.topLeft()); + const QRect oldGeometryLocal = mOldGeometry; mOldGeometry = currentGeometry; // If this is a move, redraw the previous location - if (mOldGeometryLocal != currentGeometry) - platformScreen()->setDirty(mOldGeometryLocal); + if (oldGeometryLocal != currentGeometry) + platformScreen()->setDirty(oldGeometryLocal); platformScreen()->setDirty(dirtyRegion); } QT_END_NAMESPACE - diff --git a/src/platformsupport/fontdatabases/fontdatabases.pro b/src/platformsupport/fontdatabases/fontdatabases.pro index 9376c3b702..49dead4668 100644 --- a/src/platformsupport/fontdatabases/fontdatabases.pro +++ b/src/platformsupport/fontdatabases/fontdatabases.pro @@ -7,7 +7,7 @@ CONFIG += static internal_module DEFINES += QT_NO_CAST_FROM_ASCII PRECOMPILED_HEADER = ../../corelib/global/qt_pch.h -darwin:!if(watchos:CONFIG(simulator, simulator|device)) { +darwin { include($$PWD/mac/coretext.pri) } else { qtConfig(freetype) { diff --git a/src/platformsupport/fontdatabases/mac/coretext.pri b/src/platformsupport/fontdatabases/mac/coretext.pri index 1caeb2c1ac..06f1971696 100644 --- a/src/platformsupport/fontdatabases/mac/coretext.pri +++ b/src/platformsupport/fontdatabases/mac/coretext.pri @@ -11,6 +11,21 @@ uikit: \ # On iOS/tvOS/watchOS CoreText and CoreGraphics are stand-alone frameworks LIBS_PRIVATE += -framework CoreText -framework CoreGraphics else: \ - # On Mac OS they are part of the ApplicationServices umbrella framework, - # even in 10.8 where they were also made available stand-alone. - LIBS_PRIVATE += -framework ApplicationServices + # On macOS they are re-exported by the AppKit framework + LIBS_PRIVATE += -framework AppKit + +# CoreText is documented to be available on watchOS, but the headers aren't present +# in the watchOS Simulator SDK like they are supposed to be. Work around the problem +# by adding the device SDK's headers to the search path as a fallback. +# rdar://25314492, rdar://27844864 +watchos:simulator { + simulator_system_frameworks = $$xcodeSDKInfo(Path, $${simulator.sdk})/System/Library/Frameworks + device_system_frameworks = $$xcodeSDKInfo(Path, $${device.sdk})/System/Library/Frameworks + for (arch, QMAKE_APPLE_SIMULATOR_ARCHS) { + QMAKE_CXXFLAGS += \ + -Xarch_$${arch} \ + -F$$simulator_system_frameworks \ + -Xarch_$${arch} \ + -F$$device_system_frameworks + } +} diff --git a/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase.cpp b/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase.cpp index 1c615e06ed..a5779fc291 100644 --- a/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase.cpp +++ b/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase.cpp @@ -591,12 +591,7 @@ namespace { */ QWindowsFontEngineData::QWindowsFontEngineData() - : clearTypeEnabled(false) - , fontSmoothingGamma(QWindowsFontDatabase::fontSmoothingGamma()) -#if !defined(QT_NO_DIRECTWRITE) - , directWriteFactory(0) - , directWriteGdiInterop(0) -#endif + : fontSmoothingGamma(QWindowsFontDatabase::fontSmoothingGamma()) { // from qapplication_win.cpp UINT result = 0; diff --git a/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase_p.h b/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase_p.h index 325f522335..15172c09da 100644 --- a/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase_p.h +++ b/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase_p.h @@ -74,12 +74,12 @@ public: uint pow_gamma[256]; - bool clearTypeEnabled; + bool clearTypeEnabled = false; qreal fontSmoothingGamma; - HDC hdc; + HDC hdc = 0; #if !defined(QT_NO_DIRECTWRITE) - IDWriteFactory *directWriteFactory; - IDWriteGdiInterop *directWriteGdiInterop; + IDWriteFactory *directWriteFactory = nullptr; + IDWriteGdiInterop *directWriteGdiInterop = nullptr; #endif }; diff --git a/src/platformsupport/fontdatabases/windows/qwindowsfontengine.cpp b/src/platformsupport/fontdatabases/windows/qwindowsfontengine.cpp index 0cd473e020..2b7351d124 100644 --- a/src/platformsupport/fontdatabases/windows/qwindowsfontengine.cpp +++ b/src/platformsupport/fontdatabases/windows/qwindowsfontengine.cpp @@ -239,21 +239,9 @@ QWindowsFontEngine::QWindowsFontEngine(const QString &name, : QFontEngine(Win), m_fontEngineData(fontEngineData), _name(name), - hfont(0), m_logfont(lf), ttf(0), - hasOutline(0), - cmap(0), - cmapSize(0), - lbearing(SHRT_MIN), - rbearing(SHRT_MIN), - x_height(-1), - synthesized_flags(-1), - lineWidth(-1), - widthCache(0), - widthCacheSize(0), - designAdvances(0), - designAdvancesSize(0) + hasOutline(0) { qCDebug(lcQpaFonts) << __FUNCTION__ << name << lf.lfHeight; hfont = CreateFontIndirect(&m_logfont); diff --git a/src/platformsupport/fontdatabases/windows/qwindowsfontengine_p.h b/src/platformsupport/fontdatabases/windows/qwindowsfontengine_p.h index 709de7d11d..5119adc0eb 100644 --- a/src/platformsupport/fontdatabases/windows/qwindowsfontengine_p.h +++ b/src/platformsupport/fontdatabases/windows/qwindowsfontengine_p.h @@ -145,29 +145,29 @@ private: const QString _name; QString uniqueFamilyName; - HFONT hfont; + HFONT hfont = 0; const LOGFONT m_logfont; uint ttf : 1; uint hasOutline : 1; uint hasUnreliableOutline : 1; uint cffTable : 1; TEXTMETRIC tm; - const unsigned char *cmap; - int cmapSize; + const unsigned char *cmap = nullptr; + int cmapSize = 0; QByteArray cmapTable; - mutable qreal lbearing; - mutable qreal rbearing; + mutable qreal lbearing = SHRT_MIN; + mutable qreal rbearing = SHRT_MIN; QFixed designToDevice; - int unitsPerEm; - QFixed x_height; + int unitsPerEm = 0; + QFixed x_height = -1; FaceId _faceId; - mutable int synthesized_flags; - mutable QFixed lineWidth; - mutable unsigned char *widthCache; - mutable uint widthCacheSize; - mutable QFixed *designAdvances; - mutable int designAdvancesSize; + mutable int synthesized_flags = -1; + mutable QFixed lineWidth = -1; + mutable unsigned char *widthCache = nullptr; + mutable uint widthCacheSize = 0; + mutable QFixed *designAdvances = nullptr; + mutable int designAdvancesSize = 0; }; class QWindowsMultiFontEngine : public QFontEngineMulti diff --git a/src/platformsupport/fontdatabases/windows/qwindowsnativeimage.cpp b/src/platformsupport/fontdatabases/windows/qwindowsnativeimage.cpp index 7022615511..f8fcff952a 100644 --- a/src/platformsupport/fontdatabases/windows/qwindowsnativeimage.cpp +++ b/src/platformsupport/fontdatabases/windows/qwindowsnativeimage.cpp @@ -110,9 +110,7 @@ static inline HBITMAP createDIB(HDC hdc, int width, int height, QWindowsNativeImage::QWindowsNativeImage(int width, int height, QImage::Format format) : - m_hdc(createDC()), - m_bitmap(0), - m_null_bitmap(0) + m_hdc(createDC()) { if (width != 0 && height != 0) { uchar *bits; diff --git a/src/platformsupport/fontdatabases/windows/qwindowsnativeimage_p.h b/src/platformsupport/fontdatabases/windows/qwindowsnativeimage_p.h index c27c0d1e98..6c47a527d2 100644 --- a/src/platformsupport/fontdatabases/windows/qwindowsnativeimage_p.h +++ b/src/platformsupport/fontdatabases/windows/qwindowsnativeimage_p.h @@ -80,8 +80,8 @@ private: const HDC m_hdc; QImage m_image; - HBITMAP m_bitmap; - HBITMAP m_null_bitmap; + HBITMAP m_bitmap = 0; + HBITMAP m_null_bitmap = 0; }; QT_END_NAMESPACE diff --git a/src/platformsupport/input/evdevkeyboard/qevdevkeyboard_defaultmap_p.h b/src/platformsupport/input/evdevkeyboard/qevdevkeyboard_defaultmap_p.h index bc0485232d..17bf0fb797 100644 --- a/src/platformsupport/input/evdevkeyboard/qevdevkeyboard_defaultmap_p.h +++ b/src/platformsupport/input/evdevkeyboard/qevdevkeyboard_defaultmap_p.h @@ -52,7 +52,11 @@ // #include "qnamespace.h" +#ifdef Q_OS_FREEBSD +#include <dev/evdev/input.h> +#else #include "linux/input.h" +#endif // no QT_BEGIN_NAMESPACE, since we include it internally... diff --git a/src/platformsupport/input/evdevkeyboard/qevdevkeyboardhandler.cpp b/src/platformsupport/input/evdevkeyboard/qevdevkeyboardhandler.cpp index 0eb6fc0847..5c87cb7c9c 100644 --- a/src/platformsupport/input/evdevkeyboard/qevdevkeyboardhandler.cpp +++ b/src/platformsupport/input/evdevkeyboard/qevdevkeyboardhandler.cpp @@ -49,7 +49,11 @@ #include <qpa/qwindowsysteminterface.h> #include <private/qcore_unix_p.h> +#ifdef Q_OS_FREEBSD +#include <dev/evdev/input.h> +#else #include <linux/input.h> +#endif QT_BEGIN_NAMESPACE diff --git a/src/platformsupport/input/evdevmouse/qevdevmousehandler.cpp b/src/platformsupport/input/evdevmouse/qevdevmousehandler.cpp index d5ea04bee8..9b4bcf1575 100644 --- a/src/platformsupport/input/evdevmouse/qevdevmousehandler.cpp +++ b/src/platformsupport/input/evdevmouse/qevdevmousehandler.cpp @@ -53,8 +53,12 @@ #include <errno.h> +#ifdef Q_OS_FREEBSD +#include <dev/evdev/input.h> +#else #include <linux/kd.h> #include <linux/input.h> +#endif #define TEST_BIT(array, bit) (array[bit/8] & (1<<(bit%8))) diff --git a/src/platformsupport/input/evdevtablet/qevdevtablethandler.cpp b/src/platformsupport/input/evdevtablet/qevdevtablethandler.cpp index dc03daedda..86f8a00b13 100644 --- a/src/platformsupport/input/evdevtablet/qevdevtablethandler.cpp +++ b/src/platformsupport/input/evdevtablet/qevdevtablethandler.cpp @@ -45,7 +45,11 @@ #include <QLoggingCategory> #include <QtCore/private/qcore_unix_p.h> #include <qpa/qwindowsysteminterface.h> +#ifdef Q_OS_FREEBSD +#include <dev/evdev/input.h> +#else #include <linux/input.h> +#endif QT_BEGIN_NAMESPACE diff --git a/src/platformsupport/input/evdevtouch/qevdevtouchhandler.cpp b/src/platformsupport/input/evdevtouch/qevdevtouchhandler.cpp index d53a317fc5..6870fd3dde 100644 --- a/src/platformsupport/input/evdevtouch/qevdevtouchhandler.cpp +++ b/src/platformsupport/input/evdevtouch/qevdevtouchhandler.cpp @@ -48,7 +48,11 @@ #include <QtCore/private/qcore_unix_p.h> #include <QtGui/private/qhighdpiscaling_p.h> #include <QtGui/private/qguiapplication_p.h> +#ifdef Q_OS_FREEBSD +#include <dev/evdev/input.h> +#else #include <linux/input.h> +#endif #if QT_CONFIG(mtdev) extern "C" { diff --git a/src/platformsupport/kmsconvenience/kmsconvenience.pro b/src/platformsupport/kmsconvenience/kmsconvenience.pro new file mode 100644 index 0000000000..d0ff0d4efb --- /dev/null +++ b/src/platformsupport/kmsconvenience/kmsconvenience.pro @@ -0,0 +1,20 @@ +TARGET = QtKmsSupport +MODULE = kms_support + +QT = core-private gui-private +CONFIG += static internal_module + +DEFINES += QT_NO_CAST_FROM_ASCII +PRECOMPILED_HEADER = ../../corelib/global/qt_pch.h + +HEADERS += + qkmsdevice_p.h + +SOURCES += \ + qkmsdevice.cpp + +QMAKE_USE += drm + +LIBS_PRIVATE += $$QMAKE_LIBS_DYNLOAD + +load(qt_module) diff --git a/src/platformsupport/kmsconvenience/qkmsdevice.cpp b/src/platformsupport/kmsconvenience/qkmsdevice.cpp new file mode 100644 index 0000000000..319c7f3d9b --- /dev/null +++ b/src/platformsupport/kmsconvenience/qkmsdevice.cpp @@ -0,0 +1,651 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2016 Pelagicore AG +** Copyright (C) 2015 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com> +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qkmsdevice_p.h" + +#include <QtCore/QJsonDocument> +#include <QtCore/QJsonObject> +#include <QtCore/QJsonArray> +#include <QtCore/QFile> +#include <QtCore/QLoggingCategory> + +#define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0]) + +QT_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(qLcKmsDebug, "qt.qpa.eglfs.kms") + +enum OutputConfiguration { + OutputConfigOff, + OutputConfigPreferred, + OutputConfigCurrent, + OutputConfigMode, + OutputConfigModeline +}; + +int QKmsDevice::crtcForConnector(drmModeResPtr resources, drmModeConnectorPtr connector) +{ + for (int i = 0; i < connector->count_encoders; i++) { + drmModeEncoderPtr encoder = drmModeGetEncoder(m_dri_fd, connector->encoders[i]); + if (!encoder) { + qWarning("Failed to get encoder"); + continue; + } + + quint32 possibleCrtcs = encoder->possible_crtcs; + drmModeFreeEncoder(encoder); + + for (int j = 0; j < resources->count_crtcs; j++) { + bool isPossible = possibleCrtcs & (1 << j); + bool isAvailable = !(m_crtc_allocator & 1 << resources->crtcs[j]); + + if (isPossible && isAvailable) + return j; + } + } + + return -1; +} + +static const char * const connector_type_names[] = { // must match DRM_MODE_CONNECTOR_* + "None", + "VGA", + "DVI", + "DVI", + "DVI", + "Composite", + "TV", + "LVDS", + "CTV", + "DIN", + "DP", + "HDMI", + "HDMI", + "TV", + "eDP", + "Virtual", + "DSI" +}; + +static QByteArray nameForConnector(const drmModeConnectorPtr connector) +{ + QByteArray connectorName("UNKNOWN"); + + if (connector->connector_type < ARRAY_LENGTH(connector_type_names)) + connectorName = connector_type_names[connector->connector_type]; + + connectorName += QByteArray::number(connector->connector_type_id); + + return connectorName; +} + +static bool parseModeline(const QByteArray &text, drmModeModeInfoPtr mode) +{ + char hsync[16]; + char vsync[16]; + float fclock; + + mode->type = DRM_MODE_TYPE_USERDEF; + mode->hskew = 0; + mode->vscan = 0; + mode->vrefresh = 0; + mode->flags = 0; + + if (sscanf(text.constData(), "%f %hd %hd %hd %hd %hd %hd %hd %hd %15s %15s", + &fclock, + &mode->hdisplay, + &mode->hsync_start, + &mode->hsync_end, + &mode->htotal, + &mode->vdisplay, + &mode->vsync_start, + &mode->vsync_end, + &mode->vtotal, hsync, vsync) != 11) + return false; + + mode->clock = fclock * 1000; + + if (strcmp(hsync, "+hsync") == 0) + mode->flags |= DRM_MODE_FLAG_PHSYNC; + else if (strcmp(hsync, "-hsync") == 0) + mode->flags |= DRM_MODE_FLAG_NHSYNC; + else + return false; + + if (strcmp(vsync, "+vsync") == 0) + mode->flags |= DRM_MODE_FLAG_PVSYNC; + else if (strcmp(vsync, "-vsync") == 0) + mode->flags |= DRM_MODE_FLAG_NVSYNC; + else + return false; + + return true; +} + +QPlatformScreen *QKmsDevice::createScreenForConnector(drmModeResPtr resources, + drmModeConnectorPtr connector, + VirtualDesktopInfo *vinfo) +{ + const QByteArray connectorName = nameForConnector(connector); + + const int crtc = crtcForConnector(resources, connector); + if (crtc < 0) { + qWarning() << "No usable crtc/encoder pair for connector" << connectorName; + return Q_NULLPTR; + } + + OutputConfiguration configuration; + QSize configurationSize; + drmModeModeInfo configurationModeline; + + auto userConfig = m_screenConfig->outputSettings(); + auto userConnectorConfig = userConfig.value(QString::fromUtf8(connectorName)); + // default to the preferred mode unless overridden in the config + const QByteArray mode = userConnectorConfig.value(QStringLiteral("mode"), QStringLiteral("preferred")) + .toByteArray().toLower(); + if (mode == "off") { + configuration = OutputConfigOff; + } else if (mode == "preferred") { + configuration = OutputConfigPreferred; + } else if (mode == "current") { + configuration = OutputConfigCurrent; + } else if (sscanf(mode.constData(), "%dx%d", &configurationSize.rwidth(), &configurationSize.rheight()) == 2) { + configuration = OutputConfigMode; + } else if (parseModeline(mode, &configurationModeline)) { + configuration = OutputConfigModeline; + } else { + qWarning("Invalid mode \"%s\" for output %s", mode.constData(), connectorName.constData()); + configuration = OutputConfigPreferred; + } + if (vinfo) { + *vinfo = VirtualDesktopInfo(); + vinfo->virtualIndex = userConnectorConfig.value(QStringLiteral("virtualIndex"), INT_MAX).toInt(); + if (userConnectorConfig.contains(QStringLiteral("virtualPos"))) { + const QByteArray vpos = userConnectorConfig.value(QStringLiteral("virtualPos")).toByteArray(); + const QByteArrayList vposComp = vpos.split(','); + if (vposComp.count() == 2) + vinfo->virtualPos = QPoint(vposComp[0].trimmed().toInt(), vposComp[1].trimmed().toInt()); + } + } + + const uint32_t crtc_id = resources->crtcs[crtc]; + + if (configuration == OutputConfigOff) { + qCDebug(qLcKmsDebug) << "Turning off output" << connectorName; + drmModeSetCrtc(m_dri_fd, crtc_id, 0, 0, 0, 0, 0, Q_NULLPTR); + return Q_NULLPTR; + } + + // Skip disconnected output + if (configuration == OutputConfigPreferred && connector->connection == DRM_MODE_DISCONNECTED) { + qCDebug(qLcKmsDebug) << "Skipping disconnected output" << connectorName; + return Q_NULLPTR; + } + + // Get the current mode on the current crtc + drmModeModeInfo crtc_mode; + memset(&crtc_mode, 0, sizeof crtc_mode); + if (drmModeEncoderPtr encoder = drmModeGetEncoder(m_dri_fd, connector->connector_id)) { + drmModeCrtcPtr crtc = drmModeGetCrtc(m_dri_fd, encoder->crtc_id); + drmModeFreeEncoder(encoder); + + if (!crtc) + return Q_NULLPTR; + + if (crtc->mode_valid) + crtc_mode = crtc->mode; + + drmModeFreeCrtc(crtc); + } + + QList<drmModeModeInfo> modes; + modes.reserve(connector->count_modes); + qCDebug(qLcKmsDebug) << connectorName << "mode count:" << connector->count_modes; + for (int i = 0; i < connector->count_modes; i++) { + const drmModeModeInfo &mode = connector->modes[i]; + qCDebug(qLcKmsDebug) << "mode" << i << mode.hdisplay << "x" << mode.vdisplay + << '@' << mode.vrefresh << "hz"; + modes << connector->modes[i]; + } + + int preferred = -1; + int current = -1; + int configured = -1; + int best = -1; + + for (int i = modes.size() - 1; i >= 0; i--) { + const drmModeModeInfo &m = modes.at(i); + + if (configuration == OutputConfigMode && + m.hdisplay == configurationSize.width() && + m.vdisplay == configurationSize.height()) { + configured = i; + } + + if (!memcmp(&crtc_mode, &m, sizeof m)) + current = i; + + if (m.type & DRM_MODE_TYPE_PREFERRED) + preferred = i; + + best = i; + } + + if (configuration == OutputConfigModeline) { + modes << configurationModeline; + configured = modes.size() - 1; + } + + if (current < 0 && crtc_mode.clock != 0) { + modes << crtc_mode; + current = mode.size() - 1; + } + + if (configuration == OutputConfigCurrent) + configured = current; + + int selected_mode = -1; + + if (configured >= 0) + selected_mode = configured; + else if (preferred >= 0) + selected_mode = preferred; + else if (current >= 0) + selected_mode = current; + else if (best >= 0) + selected_mode = best; + + if (selected_mode < 0) { + qWarning() << "No modes available for output" << connectorName; + return Q_NULLPTR; + } else { + int width = modes[selected_mode].hdisplay; + int height = modes[selected_mode].vdisplay; + int refresh = modes[selected_mode].vrefresh; + qCDebug(qLcKmsDebug) << "Selected mode" << selected_mode << ":" << width << "x" << height + << '@' << refresh << "hz for output" << connectorName; + } + + // physical size from connector < config values < env vars + int pwidth = qEnvironmentVariableIntValue("QT_QPA_EGLFS_PHYSICAL_WIDTH"); + if (!pwidth) + pwidth = qEnvironmentVariableIntValue("QT_QPA_PHYSICAL_WIDTH"); + int pheight = qEnvironmentVariableIntValue("QT_QPA_EGLFS_PHYSICAL_HEIGHT"); + if (!pheight) + pheight = qEnvironmentVariableIntValue("QT_QPA_PHYSICAL_HEIGHT"); + QSizeF physSize(pwidth, pheight); + if (physSize.isEmpty()) { + physSize = QSize(userConnectorConfig.value(QStringLiteral("physicalWidth")).toInt(), + userConnectorConfig.value(QStringLiteral("physicalHeight")).toInt()); + if (physSize.isEmpty()) { + physSize.setWidth(connector->mmWidth); + physSize.setHeight(connector->mmHeight); + } + } + qCDebug(qLcKmsDebug) << "Physical size is" << physSize << "mm" << "for output" << connectorName; + + QKmsOutput output = { + QString::fromUtf8(connectorName), + connector->connector_id, + crtc_id, + physSize, + selected_mode, + false, + drmModeGetCrtc(m_dri_fd, crtc_id), + modes, + connector->subpixel, + connectorProperty(connector, QByteArrayLiteral("DPMS")), + false, + 0, + false + }; + + bool ok; + int idx = qEnvironmentVariableIntValue("QT_QPA_EGLFS_KMS_PLANE_INDEX", &ok); + if (ok) { + drmModePlaneRes *planeResources = drmModeGetPlaneResources(m_dri_fd); + if (planeResources) { + if (idx >= 0 && idx < int(planeResources->count_planes)) { + drmModePlane *plane = drmModeGetPlane(m_dri_fd, planeResources->planes[idx]); + if (plane) { + output.wants_plane = true; + output.plane_id = plane->plane_id; + qCDebug(qLcKmsDebug, "Forcing plane index %d, plane id %u (belongs to crtc id %u)", + idx, plane->plane_id, plane->crtc_id); + drmModeFreePlane(plane); + } + } else { + qWarning("Invalid plane index %d, must be between 0 and %u", idx, planeResources->count_planes - 1); + } + } + } + + m_crtc_allocator |= (1 << output.crtc_id); + m_connector_allocator |= (1 << output.connector_id); + + return createScreen(output); +} + +drmModePropertyPtr QKmsDevice::connectorProperty(drmModeConnectorPtr connector, const QByteArray &name) +{ + drmModePropertyPtr prop; + + for (int i = 0; i < connector->count_props; i++) { + prop = drmModeGetProperty(m_dri_fd, connector->props[i]); + if (!prop) + continue; + if (strcmp(prop->name, name.constData()) == 0) + return prop; + drmModeFreeProperty(prop); + } + + return Q_NULLPTR; +} + +QKmsDevice::QKmsDevice(QKmsScreenConfig *screenConfig, const QString &path) + : m_screenConfig(screenConfig) + , m_path(path) + , m_dri_fd(-1) + , m_crtc_allocator(0) + , m_connector_allocator(0) +{ + if (m_path.isEmpty()) { + m_path = m_screenConfig->devicePath(); + qCDebug(qLcKmsDebug, "Using DRM device %s specified in config file", qPrintable(m_path)); + if (m_path.isEmpty()) + qFatal("No DRM device given"); + } else { + qCDebug(qLcKmsDebug, "Using backend-provided DRM device %s", qPrintable(m_path)); + } +} + +QKmsDevice::~QKmsDevice() +{ +} + +struct OrderedScreen +{ + OrderedScreen() : screen(nullptr) { } + OrderedScreen(QPlatformScreen *screen, const QKmsDevice::VirtualDesktopInfo &vinfo) + : screen(screen), vinfo(vinfo) { } + QPlatformScreen *screen; + QKmsDevice::VirtualDesktopInfo vinfo; +}; + +QDebug operator<<(QDebug dbg, const OrderedScreen &s) +{ + QDebugStateSaver saver(dbg); + dbg.nospace() << "OrderedScreen(" << s.screen << " : " << s.vinfo.virtualIndex + << " / " << s.vinfo.virtualPos << ")"; + return dbg; +} + +static bool orderedScreenLessThan(const OrderedScreen &a, const OrderedScreen &b) +{ + return a.vinfo.virtualIndex < b.vinfo.virtualIndex; +} + +void QKmsDevice::createScreens() +{ + drmModeResPtr resources = drmModeGetResources(m_dri_fd); + if (!resources) { + qWarning("drmModeGetResources failed"); + return; + } + + QVector<OrderedScreen> screens; + + int wantedConnectorIndex = -1; + bool ok; + int idx = qEnvironmentVariableIntValue("QT_QPA_EGLFS_KMS_CONNECTOR_INDEX", &ok); + if (ok) { + if (idx >= 0 && idx < resources->count_connectors) + wantedConnectorIndex = idx; + else + qWarning("Invalid connector index %d, must be between 0 and %u", idx, resources->count_connectors - 1); + } + + for (int i = 0; i < resources->count_connectors; i++) { + if (wantedConnectorIndex >= 0 && i != wantedConnectorIndex) + continue; + + drmModeConnectorPtr connector = drmModeGetConnector(m_dri_fd, resources->connectors[i]); + if (!connector) + continue; + + VirtualDesktopInfo vinfo; + QPlatformScreen *screen = createScreenForConnector(resources, connector, &vinfo); + if (screen) + screens.append(OrderedScreen(screen, vinfo)); + + drmModeFreeConnector(connector); + } + + drmModeFreeResources(resources); + + // Use stable sort to preserve the original order for outputs with unspecified indices. + std::stable_sort(screens.begin(), screens.end(), orderedScreenLessThan); + qCDebug(qLcKmsDebug) << "Sorted screen list:" << screens; + + QPoint pos(0, 0); + QList<QPlatformScreen *> siblings; + QVector<QPoint> virtualPositions; + + for (const OrderedScreen &orderedScreen : screens) { + QPlatformScreen *s = orderedScreen.screen; + QPoint virtualPos(0, 0); + // set up a horizontal or vertical virtual desktop + if (orderedScreen.vinfo.virtualPos.isNull()) { + virtualPos = pos; + if (m_screenConfig->virtualDesktopLayout() == QKmsScreenConfig::VirtualDesktopLayoutVertical) + pos.ry() += s->geometry().height(); + else + pos.rx() += s->geometry().width(); + } else { + virtualPos = orderedScreen.vinfo.virtualPos; + } + qCDebug(qLcKmsDebug) << "Adding screen" << s << "to QPA with geometry" << s->geometry(); + // The order in qguiapp's screens list will match the order set by + // virtualIndex. This is not only handy but also required since for instance + // evdevtouch relies on it when performing touch device - screen mapping. + if (!m_screenConfig->separateScreens()) { + siblings.append(s); + virtualPositions.append(virtualPos); + } else { + registerScreen(s, virtualPos, QList<QPlatformScreen *>() << s); + } + } + + if (!m_screenConfig->separateScreens()) { + // enable the virtual desktop + for (int i = 0; i < siblings.count(); ++i) + registerScreen(siblings[i], virtualPositions[i], siblings); + } +} + +int QKmsDevice::fd() const +{ + return m_dri_fd; +} + +QString QKmsDevice::devicePath() const +{ + return m_path; +} + +void QKmsDevice::setFd(int fd) +{ + m_dri_fd = fd; +} + +QKmsScreenConfig *QKmsDevice::screenConfig() const +{ + return m_screenConfig; +} + +QKmsScreenConfig::QKmsScreenConfig() + : m_hwCursor(true) + , m_separateScreens(false) + , m_pbuffers(false) + , m_virtualDesktopLayout(VirtualDesktopLayoutHorizontal) +{ + loadConfig(); +} + +void QKmsScreenConfig::loadConfig() +{ + QByteArray json = qgetenv("QT_QPA_EGLFS_KMS_CONFIG"); + if (json.isEmpty()) { + json = qgetenv("QT_QPA_KMS_CONFIG"); + if (json.isEmpty()) + return; + } + + qCDebug(qLcKmsDebug) << "Loading KMS setup from" << json; + + QFile file(QString::fromUtf8(json)); + if (!file.open(QFile::ReadOnly)) { + qCWarning(qLcKmsDebug) << "Could not open config file" + << json << "for reading"; + return; + } + + const QJsonDocument doc = QJsonDocument::fromJson(file.readAll()); + if (!doc.isObject()) { + qCWarning(qLcKmsDebug) << "Invalid config file" << json + << "- no top-level JSON object"; + return; + } + + const QJsonObject object = doc.object(); + + m_hwCursor = object.value(QLatin1String("hwcursor")).toBool(m_hwCursor); + m_pbuffers = object.value(QLatin1String("pbuffers")).toBool(m_pbuffers); + m_devicePath = object.value(QLatin1String("device")).toString(); + m_separateScreens = object.value(QLatin1String("separateScreens")).toBool(m_separateScreens); + + const QString vdOriString = object.value(QLatin1String("virtualDesktopLayout")).toString(); + if (!vdOriString.isEmpty()) { + if (vdOriString == QLatin1String("horizontal")) + m_virtualDesktopLayout = VirtualDesktopLayoutHorizontal; + else if (vdOriString == QLatin1String("vertical")) + m_virtualDesktopLayout = VirtualDesktopLayoutVertical; + else + qCWarning(qLcKmsDebug) << "Unknown virtualDesktopOrientation value" << vdOriString; + } + + const QJsonArray outputs = object.value(QLatin1String("outputs")).toArray(); + for (int i = 0; i < outputs.size(); i++) { + const QVariantMap outputSettings = outputs.at(i).toObject().toVariantMap(); + + if (outputSettings.contains(QStringLiteral("name"))) { + const QString name = outputSettings.value(QStringLiteral("name")).toString(); + + if (m_outputSettings.contains(name)) { + qCDebug(qLcKmsDebug) << "Output" << name << "configured multiple times!"; + } + + m_outputSettings.insert(name, outputSettings); + } + } + + qCDebug(qLcKmsDebug) << "Requested configuration (some settings may be ignored):\n" + << "\thwcursor:" << m_hwCursor << "\n" + << "\tpbuffers:" << m_pbuffers << "\n" + << "\tseparateScreens:" << m_separateScreens << "\n" + << "\tvirtualDesktopLayout:" << m_virtualDesktopLayout << "\n" + << "\toutputs:" << m_outputSettings; +} + +void QKmsOutput::restoreMode(QKmsDevice *device) +{ + if (mode_set && saved_crtc) { + drmModeSetCrtc(device->fd(), + saved_crtc->crtc_id, + saved_crtc->buffer_id, + 0, 0, + &connector_id, 1, + &saved_crtc->mode); + mode_set = false; + } +} + +void QKmsOutput::cleanup(QKmsDevice *device) +{ + if (dpms_prop) { + drmModeFreeProperty(dpms_prop); + dpms_prop = nullptr; + } + + restoreMode(device); + + if (saved_crtc) { + drmModeFreeCrtc(saved_crtc); + saved_crtc = nullptr; + } +} + +QPlatformScreen::SubpixelAntialiasingType QKmsOutput::subpixelAntialiasingTypeHint() const +{ + switch (subpixel) { + default: + case DRM_MODE_SUBPIXEL_UNKNOWN: + case DRM_MODE_SUBPIXEL_NONE: + return QPlatformScreen::Subpixel_None; + case DRM_MODE_SUBPIXEL_HORIZONTAL_RGB: + return QPlatformScreen::Subpixel_RGB; + case DRM_MODE_SUBPIXEL_HORIZONTAL_BGR: + return QPlatformScreen::Subpixel_BGR; + case DRM_MODE_SUBPIXEL_VERTICAL_RGB: + return QPlatformScreen::Subpixel_VRGB; + case DRM_MODE_SUBPIXEL_VERTICAL_BGR: + return QPlatformScreen::Subpixel_VBGR; + } +} + +void QKmsOutput::setPowerState(QKmsDevice *device, QPlatformScreen::PowerState state) +{ + if (dpms_prop) + drmModeConnectorSetProperty(device->fd(), connector_id, + dpms_prop->prop_id, (int) state); +} + +QT_END_NAMESPACE diff --git a/src/platformsupport/kmsconvenience/qkmsdevice_p.h b/src/platformsupport/kmsconvenience/qkmsdevice_p.h new file mode 100644 index 0000000000..c41448bc99 --- /dev/null +++ b/src/platformsupport/kmsconvenience/qkmsdevice_p.h @@ -0,0 +1,168 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2016 Pelagicore AG +** Copyright (C) 2015 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com> +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QKMSDEVICE_P_H +#define QKMSDEVICE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <qpa/qplatformscreen.h> +#include <QtCore/QMap> +#include <QtCore/QVariant> + +#include <xf86drm.h> +#include <xf86drmMode.h> + +QT_BEGIN_NAMESPACE + +class QKmsDevice; + +class QKmsScreenConfig +{ +public: + enum VirtualDesktopLayout { + VirtualDesktopLayoutHorizontal, + VirtualDesktopLayoutVertical + }; + + QKmsScreenConfig(); + + QString devicePath() const { return m_devicePath; } + + bool hwCursor() const { return m_hwCursor; } + bool separateScreens() const { return m_separateScreens; } + bool supportsPBuffers() const { return m_pbuffers; } + VirtualDesktopLayout virtualDesktopLayout() const { return m_virtualDesktopLayout; } + + QMap<QString, QVariantMap> outputSettings() const { return m_outputSettings; } + +private: + void loadConfig(); + + QString m_devicePath; + bool m_hwCursor; + bool m_separateScreens; + bool m_pbuffers; + VirtualDesktopLayout m_virtualDesktopLayout; + QMap<QString, QVariantMap> m_outputSettings; +}; + +struct QKmsOutput +{ + QString name; + uint32_t connector_id; + uint32_t crtc_id; + QSizeF physical_size; + int mode; // index of selected mode in list below + bool mode_set; + drmModeCrtcPtr saved_crtc; + QList<drmModeModeInfo> modes; + int subpixel; + drmModePropertyPtr dpms_prop; + bool wants_plane; + uint32_t plane_id; + bool plane_set; + + void restoreMode(QKmsDevice *device); + void cleanup(QKmsDevice *device); + QPlatformScreen::SubpixelAntialiasingType subpixelAntialiasingTypeHint() const; + void setPowerState(QKmsDevice *device, QPlatformScreen::PowerState state); +}; + +class QKmsDevice +{ +public: + struct VirtualDesktopInfo { + VirtualDesktopInfo() : virtualIndex(0) { } + int virtualIndex; + QPoint virtualPos; + }; + + QKmsDevice(QKmsScreenConfig *screenConfig, const QString &path = QString()); + virtual ~QKmsDevice(); + + virtual bool open() = 0; + virtual void close() = 0; + virtual void *nativeDisplay() const = 0; + + void createScreens(); + + int fd() const; + QString devicePath() const; + + QKmsScreenConfig *screenConfig() const; + +protected: + virtual QPlatformScreen *createScreen(const QKmsOutput &output) = 0; + virtual void registerScreen(QPlatformScreen *screen, + const QPoint &virtualPos, + const QList<QPlatformScreen *> &virtualSiblings) = 0; + + void setFd(int fd); + int crtcForConnector(drmModeResPtr resources, drmModeConnectorPtr connector); + QPlatformScreen *createScreenForConnector(drmModeResPtr resources, + drmModeConnectorPtr connector, + VirtualDesktopInfo *vinfo); + drmModePropertyPtr connectorProperty(drmModeConnectorPtr connector, const QByteArray &name); + + QKmsScreenConfig *m_screenConfig; + QString m_path; + int m_dri_fd; + + quint32 m_crtc_allocator; + quint32 m_connector_allocator; + +private: + Q_DISABLE_COPY(QKmsDevice) +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/platformsupport/platformsupport.pro b/src/platformsupport/platformsupport.pro index 09e2922505..7a97a12bae 100644 --- a/src/platformsupport/platformsupport.pro +++ b/src/platformsupport/platformsupport.pro @@ -7,7 +7,7 @@ SUBDIRS = \ fbconvenience \ themes -qtConfig(freetype)|if(darwin:!if(watchos:CONFIG(simulator, simulator|device)))|win32: \ +qtConfig(freetype)|darwin|win32: \ SUBDIRS += fontdatabases qtConfig(evdev)|qtConfig(tslib)|qtConfig(libinput) { @@ -24,6 +24,8 @@ qtConfig(egl): \ SUBDIRS += eglconvenience qtConfig(xlib):qtConfig(opengl):!qtConfig(opengles2): \ SUBDIRS += glxconvenience +qtConfig(kms): \ + SUBDIRS += kmsconvenience qtConfig(accessibility) { SUBDIRS += accessibility |