diff options
Diffstat (limited to 'src/plugins/platforms')
35 files changed, 1305 insertions, 305 deletions
diff --git a/src/plugins/platforms/android/androidjniinput.cpp b/src/plugins/platforms/android/androidjniinput.cpp index 595892c1d1..62da60ab92 100644 --- a/src/plugins/platforms/android/androidjniinput.cpp +++ b/src/plugins/platforms/android/androidjniinput.cpp @@ -155,7 +155,7 @@ namespace QtAndroidInput static void longPress(JNIEnv */*env*/, jobject /*thiz*/, jint /*winId*/, jint x, jint y) { //### TODO: add proper API for Qt 5.2 - static bool rightMouseFromLongPress = qgetenv("QT_NECESSITAS_COMPATIBILITY_LONG_PRESS").toInt(); + static bool rightMouseFromLongPress = qEnvironmentVariableIntValue("QT_NECESSITAS_COMPATIBILITY_LONG_PRESS"); if (!rightMouseFromLongPress) return; m_ignoreMouseEvents = true; diff --git a/src/plugins/platforms/android/androidjnimain.cpp b/src/plugins/platforms/android/androidjnimain.cpp index d1e78dfe5d..df0a8c56cb 100644 --- a/src/plugins/platforms/android/androidjnimain.cpp +++ b/src/plugins/platforms/android/androidjnimain.cpp @@ -412,7 +412,7 @@ namespace QtAndroid bool blockEventLoopsWhenSuspended() { - static bool block = qgetenv("QT_BLOCK_EVENT_LOOPS_WHEN_SUSPENDED").toInt(); + static bool block = qEnvironmentVariableIntValue("QT_BLOCK_EVENT_LOOPS_WHEN_SUSPENDED"); return block; } diff --git a/src/plugins/platforms/android/androidjnimenu.cpp b/src/plugins/platforms/android/androidjnimenu.cpp index 23182c6e57..36c349f6b4 100644 --- a/src/plugins/platforms/android/androidjnimenu.cpp +++ b/src/plugins/platforms/android/androidjnimenu.cpp @@ -199,7 +199,7 @@ namespace QtAndroidMenu env->DeleteLocalRef(env->CallObjectMethod(menuItem, setEnabledMenuItemMethodID, enabled)); if (!icon.isNull()) { // isNull() only checks the d pointer, not the actual image data. - int sz = qMax(36, qgetenv("QT_ANDROID_APP_ICON_SIZE").toInt()); + int sz = qMax(36, qEnvironmentVariableIntValue("QT_ANDROID_APP_ICON_SIZE")); QImage img = icon.pixmap(QSize(sz,sz), enabled ? QIcon::Normal diff --git a/src/plugins/platforms/android/qandroidassetsfileenginehandler.cpp b/src/plugins/platforms/android/qandroidassetsfileenginehandler.cpp index 06303cdd10..2010c19bd4 100644 --- a/src/plugins/platforms/android/qandroidassetsfileenginehandler.cpp +++ b/src/plugins/platforms/android/qandroidassetsfileenginehandler.cpp @@ -254,7 +254,7 @@ private: AndroidAssetsFileEngineHandler::AndroidAssetsFileEngineHandler() - : m_assetsCache(std::max(5, qgetenv("QT_ANDROID_MAX_ASSETS_CACHE_SIZE").toInt())) + : m_assetsCache(std::max(5, qEnvironmentVariableIntValue("QT_ANDROID_MAX_ASSETS_CACHE_SIZE"))) , m_hasPrepopulatedCache(false) , m_hasTriedPrepopulatingCache(false) { diff --git a/src/plugins/platforms/android/qandroidplatformscreen.cpp b/src/plugins/platforms/android/qandroidplatformscreen.cpp index 684ae64703..8a07735e5f 100644 --- a/src/plugins/platforms/android/qandroidplatformscreen.cpp +++ b/src/plugins/platforms/android/qandroidplatformscreen.cpp @@ -85,7 +85,7 @@ QAndroidPlatformScreen::QAndroidPlatformScreen():QObject(),QPlatformScreen() m_size = QSize(QAndroidPlatformIntegration::m_defaultScreenWidth, QAndroidPlatformIntegration::m_defaultScreenHeight); // Raster only apps should set QT_ANDROID_RASTER_IMAGE_DEPTH to 16 // is way much faster than 32 - if (qgetenv("QT_ANDROID_RASTER_IMAGE_DEPTH").toInt() == 16) { + if (qEnvironmentVariableIntValue("QT_ANDROID_RASTER_IMAGE_DEPTH") == 16) { m_format = QImage::Format_RGB16; m_depth = 16; } else { diff --git a/src/plugins/platforms/android/qandroidplatformtheme.cpp b/src/plugins/platforms/android/qandroidplatformtheme.cpp index f9f2e4a944..b955cff44d 100644 --- a/src/plugins/platforms/android/qandroidplatformtheme.cpp +++ b/src/plugins/platforms/android/qandroidplatformtheme.cpp @@ -440,7 +440,7 @@ QVariant QAndroidPlatformTheme::themeHint(ThemeHint hint) const { switch (hint) { case StyleNames: - if (qgetenv("QT_USE_ANDROID_NATIVE_STYLE").toInt() + if (qEnvironmentVariableIntValue("QT_USE_ANDROID_NATIVE_STYLE") && m_androidStyleData) { return QStringList("android"); } @@ -448,7 +448,7 @@ QVariant QAndroidPlatformTheme::themeHint(ThemeHint hint) const case MouseDoubleClickDistance: { - int minimumDistance = qgetenv("QT_ANDROID_MINIMUM_MOUSE_DOUBLE_CLICK_DISTANCE").toInt(); + int minimumDistance = qEnvironmentVariableIntValue("QT_ANDROID_MINIMUM_MOUSE_DOUBLE_CLICK_DISTANCE"); int ret = minimumDistance; QAndroidPlatformIntegration *platformIntegration @@ -492,7 +492,7 @@ QString QAndroidPlatformTheme::standardButtonText(int button) const bool QAndroidPlatformTheme::usePlatformNativeDialog(QPlatformTheme::DialogType type) const { if (type == MessageDialog) - return qgetenv("QT_USE_ANDROID_NATIVE_DIALOGS").toInt() == 1; + return qEnvironmentVariableIntValue("QT_USE_ANDROID_NATIVE_DIALOGS") == 1; return false; } diff --git a/src/plugins/platforms/cocoa/qnsview.mm b/src/plugins/platforms/cocoa/qnsview.mm index 699340795d..0ce5b3b332 100644 --- a/src/plugins/platforms/cocoa/qnsview.mm +++ b/src/plugins/platforms/cocoa/qnsview.mm @@ -1419,7 +1419,7 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent) if (m_sendKeyEvent && m_composingText.isEmpty()) QWindowSystemInterface::handleExtendedKeyEvent(focusWindow, timestamp, QEvent::Type(eventType), keyCode, modifiers, - nativeScanCode, nativeVirtualKey, nativeModifiers, text, [nsevent isARepeat]); + nativeScanCode, nativeVirtualKey, nativeModifiers, text, [nsevent isARepeat], false); m_sendKeyEvent = false; m_resendKeyEvent = false; diff --git a/src/plugins/platforms/eglfs/eglfs.pri b/src/plugins/platforms/eglfs/eglfs.pri index 6f463ba7d9..182539d8fe 100644 --- a/src/plugins/platforms/eglfs/eglfs.pri +++ b/src/plugins/platforms/eglfs/eglfs.pri @@ -17,13 +17,15 @@ SOURCES += $$PWD/qeglfsintegration.cpp \ $$PWD/qeglfswindow.cpp \ $$PWD/qeglfsscreen.cpp \ $$PWD/qeglfshooks_stub.cpp \ - $$PWD/qeglfscontext.cpp + $$PWD/qeglfscontext.cpp \ + $$PWD/qeglfsoffscreenwindow.cpp HEADERS += $$PWD/qeglfsintegration.h \ $$PWD/qeglfswindow.h \ $$PWD/qeglfsscreen.h \ $$PWD/qeglfshooks.h \ - $$PWD/qeglfscontext.h + $$PWD/qeglfscontext.h \ + $$PWD/qeglfsoffscreenwindow.h QMAKE_LFLAGS += $$QMAKE_LFLAGS_NOUNDEF diff --git a/src/plugins/platforms/eglfs/qeglfscontext.cpp b/src/plugins/platforms/eglfs/qeglfscontext.cpp index 6216fa8575..00778373ae 100644 --- a/src/plugins/platforms/eglfs/qeglfscontext.cpp +++ b/src/plugins/platforms/eglfs/qeglfscontext.cpp @@ -45,7 +45,8 @@ QT_BEGIN_NAMESPACE QEglFSContext::QEglFSContext(const QSurfaceFormat &format, QPlatformOpenGLContext *share, EGLDisplay display, EGLConfig *config, const QVariant &nativeHandle) - : QEGLPlatformContext(format, share, display, config, nativeHandle) + : QEGLPlatformContext(format, share, display, config, nativeHandle), + m_tempWindow(0) { } @@ -57,17 +58,45 @@ EGLSurface QEglFSContext::eglSurfaceForPlatformSurface(QPlatformSurface *surface return static_cast<QEGLPbuffer *>(surface)->pbuffer(); } +EGLSurface QEglFSContext::createTemporaryOffscreenSurface() +{ + if (QEglFSHooks::hooks()->supportsPBuffers()) + return QEGLPlatformContext::createTemporaryOffscreenSurface(); + + if (!m_tempWindow) { + m_tempWindow = QEglFSHooks::hooks()->createNativeOffscreenWindow(format()); + if (!m_tempWindow) { + qWarning("QEglFSContext: Failed to create temporary native window"); + return EGL_NO_SURFACE; + } + } + EGLConfig config = q_configFromGLFormat(eglDisplay(), format()); + return eglCreateWindowSurface(eglDisplay(), config, m_tempWindow, 0); +} + +void QEglFSContext::destroyTemporaryOffscreenSurface(EGLSurface surface) +{ + if (QEglFSHooks::hooks()->supportsPBuffers()) { + QEGLPlatformContext::destroyTemporaryOffscreenSurface(surface); + } else { + eglDestroySurface(eglDisplay(), surface); + QEglFSHooks::hooks()->destroyNativeWindow(m_tempWindow); + m_tempWindow = 0; + } +} + void QEglFSContext::swapBuffers(QPlatformSurface *surface) { // draw the cursor if (surface->surface()->surfaceClass() == QSurface::Window) { QPlatformWindow *window = static_cast<QPlatformWindow *>(surface); - if (QEGLPlatformCursor *cursor = static_cast<QEGLPlatformCursor *>(window->screen()->cursor())) + if (QEGLPlatformCursor *cursor = qobject_cast<QEGLPlatformCursor *>(window->screen()->cursor())) cursor->paintOnScreen(); } - QEglFSHooks::hooks()->waitForVSync(); + QEglFSHooks::hooks()->waitForVSync(surface); QEGLPlatformContext::swapBuffers(surface); + QEglFSHooks::hooks()->presentBuffer(surface); } QT_END_NAMESPACE diff --git a/src/plugins/platforms/eglfs/qeglfscontext.h b/src/plugins/platforms/eglfs/qeglfscontext.h index fcdad5ed22..a656a50e2f 100644 --- a/src/plugins/platforms/eglfs/qeglfscontext.h +++ b/src/plugins/platforms/eglfs/qeglfscontext.h @@ -45,7 +45,12 @@ public: QEglFSContext(const QSurfaceFormat &format, QPlatformOpenGLContext *share, EGLDisplay display, EGLConfig *config, const QVariant &nativeHandle); EGLSurface eglSurfaceForPlatformSurface(QPlatformSurface *surface) Q_DECL_OVERRIDE; + EGLSurface createTemporaryOffscreenSurface() Q_DECL_OVERRIDE; + void destroyTemporaryOffscreenSurface(EGLSurface surface) Q_DECL_OVERRIDE; void swapBuffers(QPlatformSurface *surface) Q_DECL_OVERRIDE; + +private: + EGLNativeWindowType m_tempWindow; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/eglfs/qeglfshooks.h b/src/plugins/platforms/eglfs/qeglfshooks.h index c51af17a56..b2b8d46741 100644 --- a/src/plugins/platforms/eglfs/qeglfshooks.h +++ b/src/plugins/platforms/eglfs/qeglfshooks.h @@ -42,8 +42,9 @@ QT_BEGIN_NAMESPACE -class QEGLPlatformCursor; class QEglFSScreen; +class QEglFSIntegration; +class QPlatformSurface; class QEglFSHooks { @@ -52,6 +53,8 @@ public: virtual void platformInit(); virtual void platformDestroy(); virtual EGLNativeDisplayType platformDisplay() const; + virtual void screenInit(); + virtual void screenDestroy(); virtual QSizeF physicalScreenSize() const; virtual QSize screenSize() const; virtual QDpi logicalDpi() const; @@ -63,15 +66,19 @@ public: virtual EGLNativeWindowType createNativeWindow(QPlatformWindow *platformWindow, const QSize &size, const QSurfaceFormat &format); + virtual EGLNativeWindowType createNativeOffscreenWindow(const QSurfaceFormat &format); virtual void destroyNativeWindow(EGLNativeWindowType window); virtual bool hasCapability(QPlatformIntegration::Capability cap) const; - virtual QEGLPlatformCursor *createCursor(QPlatformScreen *screen) const; + virtual QPlatformCursor *createCursor(QPlatformScreen *screen) const; virtual bool filterConfig(EGLDisplay display, EGLConfig config) const; - virtual void waitForVSync() const; + virtual void waitForVSync(QPlatformSurface *surface) const; + virtual void presentBuffer(QPlatformSurface *surface); virtual QByteArray fbDeviceName() const; virtual int framebufferIndex() const; + virtual bool supportsPBuffers() const; + static QEglFSHooks *hooks() { #ifdef EGLFS_PLATFORM_HOOKS diff --git a/src/plugins/platforms/eglfs/qeglfshooks_kms.cpp b/src/plugins/platforms/eglfs/qeglfshooks_kms.cpp index 9e5d624d87..4c430964a3 100644 --- a/src/plugins/platforms/eglfs/qeglfshooks_kms.cpp +++ b/src/plugins/platforms/eglfs/qeglfshooks_kms.cpp @@ -40,17 +40,185 @@ ****************************************************************************/ #include "qeglfshooks.h" +#include "qeglfsintegration.h" +#include "qeglfsscreen.h" + +#include <QtPlatformSupport/private/qeglplatformcursor_p.h> #include <QtPlatformSupport/private/qdevicediscovery_p.h> #include <QtCore/private/qcore_unix_p.h> #include <QtCore/QScopedPointer> +#include <QtCore/QJsonDocument> +#include <QtCore/QJsonObject> +#include <QtCore/QJsonArray> +#include <QtCore/QLoggingCategory> #include <QtGui/qpa/qplatformwindow.h> +#include <QtGui/qpa/qplatformcursor.h> +#include <QtGui/QPainter> +#include <QtGui/private/qguiapplication_p.h> #include <xf86drm.h> #include <xf86drmMode.h> #include <gbm.h> +#ifndef DRM_CAP_CURSOR_WIDTH +#define DRM_CAP_CURSOR_WIDTH 0x8 +#endif + +#ifndef DRM_CAP_CURSOR_HEIGHT +#define DRM_CAP_CURSOR_HEIGHT 0x9 +#endif + +#define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0]) + QT_USE_NAMESPACE +Q_LOGGING_CATEGORY(qLcEglfsKmsDebug, "qt.qpa.eglfs.kms") + +class QEglFSKmsCursor; +class QEglFSKmsScreen; + +enum OutputConfiguration { + OutputConfigOff, + OutputConfigPreferred, + OutputConfigCurrent, + OutputConfigMode, + OutputConfigModeline +}; + +struct QEglFSKmsOutput { + 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; +}; + +class QEglFSKmsDevice +{ + Q_DISABLE_COPY(QEglFSKmsDevice) + + QString m_path; + int m_dri_fd; + gbm_device *m_gbm_device; + + quint32 m_crtc_allocator; + quint32 m_connector_allocator; + + int crtcForConnector(drmModeResPtr resources, drmModeConnectorPtr connector); + QEglFSKmsScreen *screenForConnector(drmModeResPtr resources, drmModeConnectorPtr connector, QPoint pos); + + static void pageFlipHandler(int fd, + unsigned int sequence, + unsigned int tv_sec, + unsigned int tv_usec, + void *user_data); +public: + QEglFSKmsDevice(const QString &path); + + bool open(); + void close(); + + void createScreens(); + + gbm_device *device() const; + int fd() const; + + void handleDrmEvent(); +}; + +class QEglFSKmsScreen : public QEglFSScreen +{ + QEglFSKmsDevice *m_device; + gbm_surface *m_gbm_surface; + + gbm_bo *m_gbm_bo_current; + gbm_bo *m_gbm_bo_next; + + QEglFSKmsOutput m_output; + QPoint m_pos; + QScopedPointer<QEglFSKmsCursor> m_cursor; + + struct FrameBuffer { + FrameBuffer() : fb(0) {} + uint32_t fb; + }; + static void bufferDestroyedHandler(gbm_bo *bo, void *data); + FrameBuffer *framebufferForBufferObject(gbm_bo *bo); + + static QMutex m_waitForFlipMutex; + +public: + QEglFSKmsScreen(QEglFSKmsDevice *device, QEglFSKmsOutput output, QPoint position); + ~QEglFSKmsScreen(); + + QRect geometry() const Q_DECL_OVERRIDE; + int depth() const Q_DECL_OVERRIDE; + QImage::Format format() const Q_DECL_OVERRIDE; + + QSizeF physicalSize() const Q_DECL_OVERRIDE; + QDpi logicalDpi() const Q_DECL_OVERRIDE; + Qt::ScreenOrientation nativeOrientation() const Q_DECL_OVERRIDE; + Qt::ScreenOrientation orientation() const Q_DECL_OVERRIDE; + + QString name() const Q_DECL_OVERRIDE; + + QPlatformCursor *cursor() const Q_DECL_OVERRIDE; + + QEglFSKmsDevice *device() const { return m_device; } + + gbm_surface *surface() const { return m_gbm_surface; } + gbm_surface *createSurface(); + void destroySurface(); + + void waitForFlip(); + void flip(); + void flipFinished(); + + QEglFSKmsOutput &output() { return m_output; } + void restoreMode(); +}; + +QMutex QEglFSKmsScreen::m_waitForFlipMutex; + +class QEglFSKmsCursor : public QPlatformCursor +{ + Q_OBJECT +public: + QEglFSKmsCursor(QEglFSKmsScreen *screen); + ~QEglFSKmsCursor(); + + // input methods + void pointerEvent(const QMouseEvent & event) Q_DECL_OVERRIDE; +#ifndef QT_NO_CURSOR + void changeCursor(QCursor * windowCursor, QWindow * window) Q_DECL_OVERRIDE; +#endif + QPoint pos() const Q_DECL_OVERRIDE; + void setPos(const QPoint &pos) Q_DECL_OVERRIDE; + +private: + void initCursorAtlas(); + + QEglFSKmsScreen *m_screen; + QSize m_cursorSize; + gbm_bo *m_bo; + QPoint m_pos; + QPlatformCursorImage m_cursorImage; + bool m_visible; + + // cursor atlas information + struct CursorAtlas { + CursorAtlas() : cursorsPerRow(0), cursorWidth(0), cursorHeight(0) { } + int cursorsPerRow; + int width, height; // width and height of the atlas + int cursorWidth, cursorHeight; // width and height of cursors inside the atlas + QList<QPoint> hotSpots; + QImage image; + } m_cursorAtlas; +}; + class QEglKmsHooks : public QEglFSHooks { public: @@ -59,111 +227,83 @@ public: void platformInit() Q_DECL_OVERRIDE; void platformDestroy() Q_DECL_OVERRIDE; EGLNativeDisplayType platformDisplay() const Q_DECL_OVERRIDE; - QSizeF physicalScreenSize() const Q_DECL_OVERRIDE; - QSize screenSize() const Q_DECL_OVERRIDE; - int screenDepth() const Q_DECL_OVERRIDE; + void screenInit() Q_DECL_OVERRIDE; QSurfaceFormat surfaceFormatFor(const QSurfaceFormat &inputFormat) const Q_DECL_OVERRIDE; EGLNativeWindowType createNativeWindow(QPlatformWindow *platformWindow, const QSize &size, const QSurfaceFormat &format) Q_DECL_OVERRIDE; + EGLNativeWindowType createNativeOffscreenWindow(const QSurfaceFormat &format) Q_DECL_OVERRIDE; void destroyNativeWindow(EGLNativeWindowType window) Q_DECL_OVERRIDE; bool hasCapability(QPlatformIntegration::Capability cap) const Q_DECL_OVERRIDE; - void waitForVSync() const Q_DECL_OVERRIDE; + QPlatformCursor *createCursor(QPlatformScreen *screen) const Q_DECL_OVERRIDE; + void waitForVSync(QPlatformSurface *surface) const Q_DECL_OVERRIDE; + void presentBuffer(QPlatformSurface *surface) Q_DECL_OVERRIDE; + bool supportsPBuffers() const Q_DECL_OVERRIDE; - void waitForVSyncImpl(); - bool setup_kms(); - - struct FrameBuffer { - FrameBuffer() : fb(0) {} - uint32_t fb; - }; - FrameBuffer *framebufferForBufferObject(gbm_bo *bo); + bool hwCursor() const; + QMap<QString, QVariantMap> outputSettings() const; private: - // device bits - QByteArray m_device; - int m_dri_fd; - gbm_device *m_gbm_device; + void loadConfig(); - // KMS bits - drmModeConnector *m_drm_connector; - drmModeEncoder *m_drm_encoder; - drmModeModeInfo m_drm_mode; - quint32 m_drm_crtc; + QEglFSKmsDevice *m_device; - // Drawing bits - gbm_surface *m_gbm_surface; + bool m_hwCursor; + bool m_pbuffers; + QString m_devicePath; + QMap<QString, QVariantMap> m_outputSettings; }; static QEglKmsHooks kms_hooks; QEglFSHooks *platformHooks = &kms_hooks; QEglKmsHooks::QEglKmsHooks() - : m_dri_fd(-1) - , m_gbm_device(Q_NULLPTR) - , m_drm_connector(Q_NULLPTR) - , m_drm_encoder(Q_NULLPTR) - , m_drm_crtc(0) - , m_gbm_surface(Q_NULLPTR) -{ - -} + : m_device(Q_NULLPTR) + , m_hwCursor(true) + , m_pbuffers(false) +{} void QEglKmsHooks::platformInit() { - QDeviceDiscovery *d = QDeviceDiscovery::create(QDeviceDiscovery::Device_VideoMask); - QStringList devices = d->scanConnectedDevices(); - d->deleteLater(); + loadConfig(); - if (devices.isEmpty()) - qFatal("Could not find DRM device!"); + if (!m_devicePath.isEmpty()) { + qCDebug(qLcEglfsKmsDebug) << "Using DRM device" << m_devicePath << "specified in config file"; + } else { - m_device = devices.first().toLocal8Bit(); - m_dri_fd = qt_safe_open(m_device.constData(), O_RDWR | O_CLOEXEC); - if (m_dri_fd == -1) { - qErrnoWarning("Could not open DRM device %s", m_device.constData()); - qFatal("DRM device required, aborting."); - } + QDeviceDiscovery *d = QDeviceDiscovery::create(QDeviceDiscovery::Device_VideoMask); + QStringList devices = d->scanConnectedDevices(); + qCDebug(qLcEglfsKmsDebug) << "Found the following video devices:" << devices; + d->deleteLater(); - if (!setup_kms()) - qFatal("Could not set up KMS on device %s!", m_device.constData()); + if (devices.isEmpty()) + qFatal("Could not find DRM device!"); - m_gbm_device = gbm_create_device(m_dri_fd); - if (!m_gbm_device) - qFatal("Could not initialize gbm on device %s!", m_device.constData()); + m_devicePath = devices.first(); + qCDebug(qLcEglfsKmsDebug) << "Using" << m_devicePath; + } + + m_device = new QEglFSKmsDevice(m_devicePath); + if (!m_device->open()) + qFatal("Could not open device %s - aborting!", qPrintable(m_devicePath)); } void QEglKmsHooks::platformDestroy() { - gbm_device_destroy(m_gbm_device); - m_gbm_device = Q_NULLPTR; - - if (qt_safe_close(m_dri_fd) == -1) - qErrnoWarning("Could not close DRM device %s", m_device.constData()); - - m_dri_fd = -1; + m_device->close(); + delete m_device; + m_device = Q_NULLPTR; } EGLNativeDisplayType QEglKmsHooks::platformDisplay() const { - return static_cast<EGLNativeDisplayType>(m_gbm_device); -} - -QSizeF QEglKmsHooks::physicalScreenSize() const -{ - return QSizeF(m_drm_connector->mmWidth, - m_drm_connector->mmHeight); -} - -QSize QEglKmsHooks::screenSize() const -{ - return QSize(m_drm_mode.hdisplay, - m_drm_mode.vdisplay); + Q_ASSERT(m_device); + return reinterpret_cast<EGLNativeDisplayType>(m_device->device()); } -int QEglKmsHooks::screenDepth() const +void QEglKmsHooks::screenInit() { - return 32; + m_device->createScreens(); } QSurfaceFormat QEglKmsHooks::surfaceFormatFor(const QSurfaceFormat &inputFormat) const @@ -181,31 +321,35 @@ EGLNativeWindowType QEglKmsHooks::createNativeWindow(QPlatformWindow *platformWi const QSize &size, const QSurfaceFormat &format) { - Q_UNUSED(platformWindow); Q_UNUSED(size); Q_UNUSED(format); - if (m_gbm_surface) { - qWarning("Only single window apps supported!"); + QEglFSKmsScreen *screen = static_cast<QEglFSKmsScreen *>(platformWindow->screen()); + if (screen->surface()) { + qWarning("Only single window per screen supported!"); return 0; } - m_gbm_surface = gbm_surface_create(m_gbm_device, - screenSize().width(), - screenSize().height(), - GBM_FORMAT_XRGB8888, - GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); - if (!m_gbm_surface) - qFatal("Could not initialize GBM surface"); + return reinterpret_cast<EGLNativeWindowType>(screen->createSurface()); +} + +EGLNativeWindowType QEglKmsHooks::createNativeOffscreenWindow(const QSurfaceFormat &format) +{ + Q_UNUSED(format); + Q_ASSERT(m_device); - return reinterpret_cast<EGLNativeWindowType>(m_gbm_surface); + qCDebug(qLcEglfsKmsDebug) << "Creating native off screen window"; + gbm_surface *surface = gbm_surface_create(m_device->device(), + 1, 1, + GBM_FORMAT_XRGB8888, + GBM_BO_USE_RENDERING); + + return reinterpret_cast<EGLNativeWindowType>(surface); } void QEglKmsHooks::destroyNativeWindow(EGLNativeWindowType window) { gbm_surface *surface = reinterpret_cast<gbm_surface *>(window); - if (surface == m_gbm_surface) - m_gbm_surface = Q_NULLPTR; gbm_surface_destroy(surface); } @@ -215,16 +359,250 @@ bool QEglKmsHooks::hasCapability(QPlatformIntegration::Capability cap) const case QPlatformIntegration::ThreadedPixmaps: case QPlatformIntegration::OpenGL: case QPlatformIntegration::ThreadedOpenGL: - case QPlatformIntegration::BufferQueueingOpenGL: return true; default: return false; } } -static void gbm_bo_destroyed_callback(gbm_bo *bo, void *data) +QPlatformCursor *QEglKmsHooks::createCursor(QPlatformScreen *screen) const +{ + if (m_hwCursor) + return Q_NULLPTR; + else + return new QEGLPlatformCursor(screen); +} + +void QEglKmsHooks::waitForVSync(QPlatformSurface *surface) const +{ + QWindow *window = static_cast<QWindow *>(surface->surface()); + QEglFSKmsScreen *screen = static_cast<QEglFSKmsScreen *>(window->screen()->handle()); + + screen->waitForFlip(); +} + +void QEglKmsHooks::presentBuffer(QPlatformSurface *surface) +{ + QWindow *window = static_cast<QWindow *>(surface->surface()); + QEglFSKmsScreen *screen = static_cast<QEglFSKmsScreen *>(window->screen()->handle()); + + screen->flip(); +} + +bool QEglKmsHooks::supportsPBuffers() const +{ + return m_pbuffers; +} + +bool QEglKmsHooks::hwCursor() const +{ + return m_hwCursor; +} + +QMap<QString, QVariantMap> QEglKmsHooks::outputSettings() const +{ + return m_outputSettings; +} + +void QEglKmsHooks::loadConfig() +{ + static QByteArray json = qgetenv("QT_QPA_EGLFS_KMS_CONFIG"); + if (json.isEmpty()) + return; + + qCDebug(qLcEglfsKmsDebug) << "Loading KMS setup from" << json; + + QFile file(QString::fromUtf8(json)); + if (!file.open(QFile::ReadOnly)) { + qCDebug(qLcEglfsKmsDebug) << "Could not open config file" + << json << "for reading"; + return; + } + + QJsonDocument doc = QJsonDocument::fromJson(file.readAll()); + if (!doc.isObject()) { + qCDebug(qLcEglfsKmsDebug) << "Invalid config file" << json + << "- no top-level JSON object"; + return; + } + + QJsonObject object = doc.object(); + + m_hwCursor = object.value("hwcursor").toBool(m_hwCursor); + m_pbuffers = object.value("pbuffers").toBool(m_pbuffers); + m_devicePath = object.value("device").toString(); + + QJsonArray outputs = object.value("outputs").toArray(); + for (int i = 0; i < outputs.size(); i++) { + QVariantMap outputSettings = outputs.at(i).toObject().toVariantMap(); + + if (outputSettings.contains("name")) { + QString name = outputSettings.value("name").toString(); + + if (m_outputSettings.contains(name)) { + qCDebug(qLcEglfsKmsDebug) << "Output" << name << "configured multiple times!"; + } + + m_outputSettings.insert(name, outputSettings); + } + } + + qCDebug(qLcEglfsKmsDebug) << "Configuration:\n" + << "\thwcursor:" << m_hwCursor << "\n" + << "\tpbuffers:" << m_pbuffers << "\n" + << "\toutputs:" << m_outputSettings; +} + +QEglFSKmsCursor::QEglFSKmsCursor(QEglFSKmsScreen *screen) + : m_screen(screen) + , m_cursorSize(64, 64) // 64x64 is the old standard size, we now try to query the real size below + , m_bo(Q_NULLPTR) + , m_cursorImage(0, 0, 0, 0, 0, 0) + , m_visible(true) { - QEglKmsHooks::FrameBuffer *fb = static_cast<QEglKmsHooks::FrameBuffer *>(data); + uint64_t width, height; + if ((drmGetCap(m_screen->device()->fd(), DRM_CAP_CURSOR_WIDTH, &width) == 0) + && (drmGetCap(m_screen->device()->fd(), DRM_CAP_CURSOR_HEIGHT, &height) == 0)) { + m_cursorSize.setWidth(width); + m_cursorSize.setHeight(height); + } + + m_bo = gbm_bo_create(m_screen->device()->device(), m_cursorSize.width(), m_cursorSize.height(), + GBM_FORMAT_ARGB8888, GBM_BO_USE_CURSOR_64X64 | GBM_BO_USE_WRITE); + if (!m_bo) { + qWarning("Could not create buffer for cursor!"); + } else { + initCursorAtlas(); + } + + drmModeMoveCursor(m_screen->device()->fd(), m_screen->output().crtc_id, 0, 0); +} + +QEglFSKmsCursor::~QEglFSKmsCursor() +{ + drmModeSetCursor(m_screen->device()->fd(), m_screen->output().crtc_id, 0, 0, 0); + drmModeMoveCursor(m_screen->device()->fd(), m_screen->output().crtc_id, 0, 0); + + gbm_bo_destroy(m_bo); + m_bo = Q_NULLPTR; +} + +void QEglFSKmsCursor::pointerEvent(const QMouseEvent &event) +{ + setPos(event.screenPos().toPoint()); +} + +#ifndef QT_NO_CURSOR +void QEglFSKmsCursor::changeCursor(QCursor *windowCursor, QWindow *window) +{ + Q_UNUSED(window); + + if (!m_visible) + return; + + const Qt::CursorShape newShape = windowCursor ? windowCursor->shape() : Qt::ArrowCursor; + if (newShape == Qt::BitmapCursor) { + m_cursorImage.set(windowCursor->pixmap().toImage(), + windowCursor->hotSpot().x(), + windowCursor->hotSpot().y()); + } else { + // Standard cursor, look up in atlas + const int width = m_cursorAtlas.cursorWidth; + const int height = m_cursorAtlas.cursorHeight; + const qreal ws = (qreal)m_cursorAtlas.cursorWidth / m_cursorAtlas.width; + const qreal hs = (qreal)m_cursorAtlas.cursorHeight / m_cursorAtlas.height; + + QRect textureRect(ws * (newShape % m_cursorAtlas.cursorsPerRow) * m_cursorAtlas.width, + hs * (newShape / m_cursorAtlas.cursorsPerRow) * m_cursorAtlas.height, + width, + height); + QPoint hotSpot = m_cursorAtlas.hotSpots[newShape]; + m_cursorImage.set(m_cursorAtlas.image.copy(textureRect), + hotSpot.x(), + hotSpot.y()); + } + + if (m_cursorImage.image()->width() > m_cursorSize.width() || m_cursorImage.image()->height() > m_cursorSize.height()) + qWarning("Cursor larger than %dx%d, cursor will be clipped.", m_cursorSize.width(), m_cursorSize.height()); + + QImage cursorImage(m_cursorSize, QImage::Format_ARGB32); + cursorImage.fill(Qt::transparent); + + QPainter painter; + painter.begin(&cursorImage); + painter.drawImage(0, 0, *m_cursorImage.image()); + painter.end(); + + gbm_bo_write(m_bo, cursorImage.constBits(), cursorImage.byteCount()); + + uint32_t handle = gbm_bo_get_handle(m_bo).u32; + int status = drmModeSetCursor(m_screen->device()->fd(), m_screen->output().crtc_id, handle, + m_cursorSize.width(), m_cursorSize.height()); + if (status != 0) + qWarning("Could not set cursor: %d", status); +} +#endif // QT_NO_CURSOR + +QPoint QEglFSKmsCursor::pos() const +{ + return m_pos; +} + +void QEglFSKmsCursor::setPos(const QPoint &pos) +{ + QPoint adjustedPos = pos - m_cursorImage.hotspot(); + int ret = drmModeMoveCursor(m_screen->device()->fd(), m_screen->output().crtc_id, adjustedPos.x(), adjustedPos.y()); + if (ret == 0) { + m_pos = pos; + } else { + qWarning("Failed to move cursor: %d", ret); + } +} + +void QEglFSKmsCursor::initCursorAtlas() +{ + static QByteArray json = qgetenv("QT_QPA_EGLFS_CURSOR"); + if (json.isEmpty()) + json = ":/cursor.json"; + + qCDebug(qLcEglfsKmsDebug) << "Initializing cursor atlas from" << json; + + QFile file(QString::fromUtf8(json)); + if (!file.open(QFile::ReadOnly)) { + drmModeSetCursor(m_screen->device()->fd(), m_screen->output().crtc_id, 0, 0, 0); + drmModeMoveCursor(m_screen->device()->fd(), m_screen->output().crtc_id, 0, 0); + m_visible = false; + return; + } + + QJsonDocument doc = QJsonDocument::fromJson(file.readAll()); + QJsonObject object = doc.object(); + + QString atlas = object.value(QLatin1String("image")).toString(); + Q_ASSERT(!atlas.isEmpty()); + + const int cursorsPerRow = object.value(QLatin1String("cursorsPerRow")).toDouble(); + Q_ASSERT(cursorsPerRow); + m_cursorAtlas.cursorsPerRow = cursorsPerRow; + + const QJsonArray hotSpots = object.value(QLatin1String("hotSpots")).toArray(); + Q_ASSERT(hotSpots.count() == Qt::LastCursor + 1); + for (int i = 0; i < hotSpots.count(); i++) { + QPoint hotSpot(hotSpots[i].toArray()[0].toDouble(), hotSpots[i].toArray()[1].toDouble()); + m_cursorAtlas.hotSpots << hotSpot; + } + + QImage image = QImage(atlas).convertToFormat(QImage::Format_ARGB32); + m_cursorAtlas.cursorWidth = image.width() / m_cursorAtlas.cursorsPerRow; + m_cursorAtlas.cursorHeight = image.height() / ((Qt::LastCursor + cursorsPerRow) / cursorsPerRow); + m_cursorAtlas.width = image.width(); + m_cursorAtlas.height = image.height(); + m_cursorAtlas.image = image; +} + +void QEglFSKmsScreen::bufferDestroyedHandler(gbm_bo *bo, void *data) +{ + FrameBuffer *fb = static_cast<FrameBuffer *>(data); if (fb->fb) { gbm_device *device = gbm_bo_get_device(bo); @@ -234,7 +612,7 @@ static void gbm_bo_destroyed_callback(gbm_bo *bo, void *data) delete fb; } -QEglKmsHooks::FrameBuffer *QEglKmsHooks::framebufferForBufferObject(gbm_bo *bo) +QEglFSKmsScreen::FrameBuffer *QEglFSKmsScreen::framebufferForBufferObject(gbm_bo *bo) { { FrameBuffer *fb = static_cast<FrameBuffer *>(gbm_bo_get_user_data(bo)); @@ -249,7 +627,7 @@ QEglKmsHooks::FrameBuffer *QEglKmsHooks::framebufferForBufferObject(gbm_bo *bo) QScopedPointer<FrameBuffer> fb(new FrameBuffer); - int ret = drmModeAddFB(m_dri_fd, width, height, 24, 32, + int ret = drmModeAddFB(m_device->fd(), width, height, 24, 32, stride, handle, &fb->fb); if (ret) { @@ -257,166 +635,542 @@ QEglKmsHooks::FrameBuffer *QEglKmsHooks::framebufferForBufferObject(gbm_bo *bo) return Q_NULLPTR; } - gbm_bo_set_user_data(bo, fb.data(), gbm_bo_destroyed_callback); + gbm_bo_set_user_data(bo, fb.data(), bufferDestroyedHandler); return fb.take(); + } -static void page_flip_handler(int fd, - unsigned int sequence, - unsigned int tv_sec, - unsigned int tv_usec, - void *user_data) +QEglFSKmsScreen::QEglFSKmsScreen(QEglFSKmsDevice *device, QEglFSKmsOutput output, QPoint position) + : QEglFSScreen(eglGetDisplay(reinterpret_cast<EGLNativeDisplayType>(device->device()))) + , m_device(device) + , m_gbm_surface(Q_NULLPTR) + , m_gbm_bo_current(Q_NULLPTR) + , m_gbm_bo_next(Q_NULLPTR) + , m_output(output) + , m_pos(position) + , m_cursor(Q_NULLPTR) { - Q_UNUSED(fd); - Q_UNUSED(sequence); - Q_UNUSED(tv_sec); - Q_UNUSED(tv_usec); +} + +QEglFSKmsScreen::~QEglFSKmsScreen() +{ + restoreMode(); +} + +QRect QEglFSKmsScreen::geometry() const +{ + const int mode = m_output.mode; + return QRect(m_pos.x(), m_pos.y(), + m_output.modes[mode].hdisplay, + m_output.modes[mode].vdisplay); +} + +int QEglFSKmsScreen::depth() const +{ + return 32; +} + +QImage::Format QEglFSKmsScreen::format() const +{ + return QImage::Format_RGB32; +} - // We are no longer flipping - *static_cast<bool *>(user_data) = false; +QSizeF QEglFSKmsScreen::physicalSize() const +{ + return m_output.physical_size; +} + +QDpi QEglFSKmsScreen::logicalDpi() const +{ + QSizeF ps = physicalSize(); + QSize s = geometry().size(); + + if (ps.isValid() && s.isValid()) + return QDpi(25.4 * s.width() / ps.width(), + 25.4 * s.height() / ps.height()); + else + return QDpi(100, 100); +} + +Qt::ScreenOrientation QEglFSKmsScreen::nativeOrientation() const +{ + return Qt::PrimaryOrientation; +} + +Qt::ScreenOrientation QEglFSKmsScreen::orientation() const +{ + return Qt::PrimaryOrientation; +} + +QString QEglFSKmsScreen::name() const +{ + return m_output.name; } -void QEglKmsHooks::waitForVSync() const +QPlatformCursor *QEglFSKmsScreen::cursor() const { - const_cast<QEglKmsHooks*>(this)->waitForVSyncImpl(); + if (kms_hooks.hwCursor()) { + if (m_cursor.isNull()) { + QEglFSKmsScreen *that = const_cast<QEglFSKmsScreen *>(this); + that->m_cursor.reset(new QEglFSKmsCursor(that)); + } + + return m_cursor.data(); + } else { + return QEglFSScreen::cursor(); + } } -void QEglKmsHooks::waitForVSyncImpl() +gbm_surface *QEglFSKmsScreen::createSurface() { if (!m_gbm_surface) { - qWarning("Cannot sync before platform init!"); - return; + qCDebug(qLcEglfsKmsDebug) << "Creating window for screen" << name(); + m_gbm_surface = gbm_surface_create(m_device->device(), + geometry().width(), + geometry().height(), + GBM_FORMAT_XRGB8888, + GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); + } + return m_gbm_surface; +} + +void QEglFSKmsScreen::destroySurface() +{ + if (m_gbm_bo_current) { + gbm_bo_destroy(m_gbm_bo_current); + m_gbm_bo_current = Q_NULLPTR; } - if (!gbm_surface_has_free_buffers(m_gbm_surface)) { - qWarning("Out of free GBM buffers!"); + if (m_gbm_bo_next) { + gbm_bo_destroy(m_gbm_bo_next); + m_gbm_bo_next = Q_NULLPTR; + } + + if (m_gbm_surface) { + gbm_surface_destroy(m_gbm_surface); + m_gbm_surface = Q_NULLPTR; + } +} + +void QEglFSKmsScreen::waitForFlip() +{ + // Don't lock the mutex unless we actually need to + if (!m_gbm_bo_next) + return; + + QMutexLocker lock(&m_waitForFlipMutex); + while (m_gbm_bo_next) + m_device->handleDrmEvent(); +} + +void QEglFSKmsScreen::flip() +{ + if (!m_gbm_surface) { + qWarning("Cannot sync before platform init!"); return; } - gbm_bo *front_buffer = gbm_surface_lock_front_buffer(m_gbm_surface); - if (!front_buffer) { + m_gbm_bo_next = gbm_surface_lock_front_buffer(m_gbm_surface); + if (!m_gbm_bo_next) { qWarning("Could not lock GBM surface front buffer!"); return; } - QEglKmsHooks::FrameBuffer *fb = framebufferForBufferObject(front_buffer); + FrameBuffer *fb = framebufferForBufferObject(m_gbm_bo_next); - int ret = drmModeSetCrtc(m_dri_fd, - m_drm_crtc, - fb->fb, - 0, 0, - &m_drm_connector->connector_id, 1, - &m_drm_mode); - if (ret) { - qErrnoWarning("Could not set DRM mode!"); - return; + if (!m_output.mode_set) { + int ret = drmModeSetCrtc(m_device->fd(), + m_output.crtc_id, + fb->fb, + 0, 0, + &m_output.connector_id, 1, + &m_output.modes[m_output.mode]); + + if (ret) + qErrnoWarning("Could not set DRM mode!"); + else + m_output.mode_set = true; } - bool flipping = true; - ret = drmModePageFlip(m_dri_fd, - m_drm_encoder->crtc_id, - fb->fb, - DRM_MODE_PAGE_FLIP_EVENT, - &flipping); + int ret = drmModePageFlip(m_device->fd(), + m_output.crtc_id, + fb->fb, + DRM_MODE_PAGE_FLIP_EVENT, + this); if (ret) { qErrnoWarning("Could not queue DRM page flip!"); - return; + gbm_surface_release_buffer(m_gbm_surface, m_gbm_bo_next); + m_gbm_bo_next = Q_NULLPTR; } +} - drmEventContext drmEvent = { - DRM_EVENT_CONTEXT_VERSION, - Q_NULLPTR, // vblank handler - page_flip_handler // page flip handler - }; +void QEglFSKmsScreen::flipFinished() +{ + if (m_gbm_bo_current) + gbm_surface_release_buffer(m_gbm_surface, + m_gbm_bo_current); - fd_set fds; - FD_ZERO(&fds); - FD_SET(m_dri_fd, &fds); + m_gbm_bo_current = m_gbm_bo_next; + m_gbm_bo_next = Q_NULLPTR; +} - time_t start, cur; - time(&start); +void QEglFSKmsScreen::restoreMode() +{ + if (m_output.mode_set && m_output.saved_crtc) { + drmModeSetCrtc(m_device->fd(), + m_output.saved_crtc->crtc_id, + m_output.saved_crtc->buffer_id, + 0, 0, + &m_output.connector_id, 1, + &m_output.saved_crtc->mode); + + drmModeFreeCrtc(m_output.saved_crtc); + m_output.saved_crtc = Q_NULLPTR; + + m_output.mode_set = false; + } +} - while (flipping && (time(&cur) < start + 1)) { - timespec v; - memset(&v, 0, sizeof(v)); - v.tv_sec = start + 1 - cur; +int QEglFSKmsDevice::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); - ret = qt_safe_select(m_dri_fd + 1, &fds, Q_NULLPTR, Q_NULLPTR, &v); + for (int j = 0; j < resources->count_crtcs; j++) { + bool isPossible = possibleCrtcs & (1 << j); + bool isAvailable = !(m_crtc_allocator & 1 << resources->crtcs[j]); - if (ret == 0) { - // timeout - break; - } else if (ret == -1) { - qErrnoWarning("Error while selecting on DRM fd"); - break; - } else if (drmHandleEvent(m_dri_fd, &drmEvent)) { - qWarning("Could not handle DRM event!"); + if (isPossible && isAvailable) + return j; } } - gbm_surface_release_buffer(m_gbm_surface, front_buffer); + return -1; } -bool QEglKmsHooks::setup_kms() +static const char * const connector_type_names[] = { + "None", + "VGA", + "DVI", + "DVI", + "DVI", + "Composite", + "TV", + "LVDS", + "CTV", + "DIN", + "DP", + "HDMI", + "HDMI", + "TV", + "eDP", +}; + +static QString nameForConnector(const drmModeConnectorPtr connector) { - drmModeRes *resources; - drmModeConnector *connector; - drmModeEncoder *encoder; - quint32 crtc = 0; - int i; + QString connectorName = "UNKNOWN"; - resources = drmModeGetResources(m_dri_fd); - if (!resources) { - qWarning("drmModeGetResources failed"); - return false; - } + if (connector->connector_type < ARRAY_LENGTH(connector_type_names)) + connectorName = connector_type_names[connector->connector_type]; + + connectorName += QString::number(connector->connector_type_id); + + return connectorName; +} + +static bool parseModeline(const QString &s, 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(qPrintable(s), "%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; - for (i = 0; i < resources->count_connectors; i++) { - connector = drmModeGetConnector(m_dri_fd, resources->connectors[i]); - if (connector == NULL) - continue; + return true; +} - if (connector->connection == DRM_MODE_CONNECTED && - connector->count_modes > 0) { - break; - } +QEglFSKmsScreen *QEglFSKmsDevice::screenForConnector(drmModeResPtr resources, drmModeConnectorPtr connector, QPoint pos) +{ + const QString connectorName = nameForConnector(connector); - drmModeFreeConnector(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; + + const QString mode = kms_hooks.outputSettings().value(connectorName).value("mode", "preferred").toString().toLower(); + if (mode == "off") { + configuration = OutputConfigOff; + } else if (mode == "preferred") { + configuration = OutputConfigPreferred; + } else if (mode == "current") { + configuration = OutputConfigCurrent; + } else if (sscanf(qPrintable(mode), "%dx%d", &configurationSize.rwidth(), &configurationSize.rheight()) == 2) { + configuration = OutputConfigMode; + } else if (parseModeline(mode, &configurationModeline)) { + configuration = OutputConfigModeline; + } else { + qWarning("Invalid mode \"%s\" for output %s", qPrintable(mode), qPrintable(connectorName)); + configuration = OutputConfigPreferred; } - if (i == resources->count_connectors) { - qWarning("No currently active connector found."); - return false; + const uint32_t crtc_id = resources->crtcs[crtc]; + + if (configuration == OutputConfigOff) { + qCDebug(qLcEglfsKmsDebug) << "Turning off output" << connectorName; + drmModeSetCrtc(m_dri_fd, crtc_id, 0, 0, 0, 0, 0, Q_NULLPTR); + return Q_NULLPTR; } - for (i = 0; i < resources->count_encoders; i++) { - encoder = drmModeGetEncoder(m_dri_fd, resources->encoders[i]); + // 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 (encoder == NULL) - continue; + if (!crtc) + return Q_NULLPTR; - if (encoder->encoder_id == connector->encoder_id) - break; + if (crtc->mode_valid) + crtc_mode = crtc->mode; - drmModeFreeEncoder(encoder); + drmModeFreeCrtc(crtc); } - for (int j = 0; j < resources->count_crtcs; j++) { - if ((encoder->possible_crtcs & (1 << j))) { - crtc = resources->crtcs[j]; - break; + QList<drmModeModeInfo> modes; + qCDebug(qLcEglfsKmsDebug) << connectorName << "mode count:" << connector->count_modes; + for (int i = 0; i < connector->count_modes; i++) { + const drmModeModeInfo &mode = connector->modes[i]; + qCDebug(qLcEglfsKmsDebug) << "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 (crtc == 0) - qFatal("No suitable CRTC available"); + if (configuration == OutputConfigModeline) { + modes << configurationModeline; + configured = modes.size() - 1; + } - m_drm_connector = connector; - m_drm_encoder = encoder; - m_drm_mode = connector->modes[0]; - m_drm_crtc = crtc; + if (current < 0 && crtc_mode.clock != 0) { + modes << crtc_mode; + current = mode.size() - 1; + } - drmModeFreeResources(resources); + 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(qLcEglfsKmsDebug) << "Selected mode" << selected_mode << ":" << width << "x" << height + << "@" << refresh << "hz for output" << connectorName; + } + + QEglFSKmsOutput output = { + connectorName, + connector->connector_id, + crtc_id, + QSizeF(connector->mmWidth, connector->mmHeight), + selected_mode, + false, + drmModeGetCrtc(m_dri_fd, crtc_id), + modes + }; + + m_crtc_allocator |= (1 << output.crtc_id); + m_connector_allocator |= (1 << output.connector_id); + + return new QEglFSKmsScreen(this, output, pos); +} + +void QEglFSKmsDevice::pageFlipHandler(int fd, unsigned int sequence, unsigned int tv_sec, unsigned int tv_usec, void *user_data) +{ + Q_UNUSED(fd); + Q_UNUSED(sequence); + Q_UNUSED(tv_sec); + Q_UNUSED(tv_usec); + + QEglFSKmsScreen *screen = static_cast<QEglFSKmsScreen *>(user_data); + screen->flipFinished(); +} + +QEglFSKmsDevice::QEglFSKmsDevice(const QString &path) + : m_path(path) + , m_dri_fd(-1) + , m_gbm_device(Q_NULLPTR) + , m_crtc_allocator(0) + , m_connector_allocator(0) +{ +} + +bool QEglFSKmsDevice::open() +{ + Q_ASSERT(m_dri_fd == -1); + Q_ASSERT(m_gbm_device == Q_NULLPTR); + + qCDebug(qLcEglfsKmsDebug) << "Opening device" << m_path; + m_dri_fd = qt_safe_open(m_path.toLocal8Bit().constData(), O_RDWR | O_CLOEXEC); + if (m_dri_fd == -1) { + qErrnoWarning("Could not open DRM device %s", qPrintable(m_path)); + return false; + } + + qCDebug(qLcEglfsKmsDebug) << "Creating GBM device for file descriptor" << m_dri_fd + << "obtained from" << m_path; + m_gbm_device = gbm_create_device(m_dri_fd); + if (!m_gbm_device) { + qErrnoWarning("Could not create GBM device"); + qt_safe_close(m_dri_fd); + m_dri_fd = -1; + return false; + } return true; } + +void QEglFSKmsDevice::close() +{ + if (m_gbm_device) { + gbm_device_destroy(m_gbm_device); + m_gbm_device = Q_NULLPTR; + } + + if (m_dri_fd != -1) { + qt_safe_close(m_dri_fd); + m_dri_fd = -1; + } +} + +void QEglFSKmsDevice::createScreens() +{ + drmModeResPtr resources = drmModeGetResources(m_dri_fd); + if (!resources) { + qWarning("drmModeGetResources failed"); + return; + } + + QPoint pos(0, 0); + QEglFSIntegration *integration = static_cast<QEglFSIntegration *>(QGuiApplicationPrivate::platformIntegration()); + + for (int i = 0; i < resources->count_connectors; i++) { + drmModeConnectorPtr connector = drmModeGetConnector(m_dri_fd, resources->connectors[i]); + if (!connector) + continue; + + QEglFSKmsScreen *screen = screenForConnector(resources, connector, pos); + if (screen) { + integration->addScreen(screen); + pos.rx() += screen->geometry().width(); + } + + drmModeFreeConnector(connector); + } + + drmModeFreeResources(resources); +} + +gbm_device *QEglFSKmsDevice::device() const +{ + return m_gbm_device; +} + +int QEglFSKmsDevice::fd() const +{ + return m_dri_fd; +} + +void QEglFSKmsDevice::handleDrmEvent() +{ + drmEventContext drmEvent = { + DRM_EVENT_CONTEXT_VERSION, + Q_NULLPTR, // vblank handler + pageFlipHandler // page flip handler + }; + + drmHandleEvent(m_dri_fd, &drmEvent); +} + +#include "qeglfshooks_kms.moc" diff --git a/src/plugins/platforms/eglfs/qeglfshooks_stub.cpp b/src/plugins/platforms/eglfs/qeglfshooks_stub.cpp index 26d77a2abb..1b042a0743 100644 --- a/src/plugins/platforms/eglfs/qeglfshooks_stub.cpp +++ b/src/plugins/platforms/eglfs/qeglfshooks_stub.cpp @@ -32,9 +32,13 @@ ****************************************************************************/ #include "qeglfshooks.h" +#include "qeglfsintegration.h" +#include "qeglfsscreen.h" + #include <QtPlatformSupport/private/qeglplatformcursor_p.h> #include <QtPlatformSupport/private/qeglconvenience_p.h> #include <QtCore/QRegularExpression> +#include <QtGui/private/qguiapplication_p.h> #if defined(Q_OS_LINUX) #include <fcntl.h> @@ -96,6 +100,18 @@ EGLNativeDisplayType QEglFSHooks::platformDisplay() const return EGL_DEFAULT_DISPLAY; } +void QEglFSHooks::screenInit() +{ + QEglFSIntegration *integration = static_cast<QEglFSIntegration *>(QGuiApplicationPrivate::platformIntegration()); + integration->addScreen(new QEglFSScreen(integration->display())); +} + +void QEglFSHooks::screenDestroy() +{ + while (!qApp->screens().isEmpty()) + delete qApp->screens().last()->handle(); +} + QSizeF QEglFSHooks::physicalScreenSize() const { return q_physicalScreenSizeFromFb(framebuffer, screenSize()); @@ -139,7 +155,7 @@ QSurfaceFormat QEglFSHooks::surfaceFormatFor(const QSurfaceFormat &inputFormat) { QSurfaceFormat format = inputFormat; - static const bool force888 = qgetenv("QT_QPA_EGLFS_FORCE888").toInt(); + static const bool force888 = qEnvironmentVariableIntValue("QT_QPA_EGLFS_FORCE888"); if (force888) { format.setRedBufferSize(8); format.setGreenBufferSize(8); @@ -164,6 +180,12 @@ EGLNativeWindowType QEglFSHooks::createNativeWindow(QPlatformWindow *platformWin return 0; } +EGLNativeWindowType QEglFSHooks::createNativeOffscreenWindow(const QSurfaceFormat &format) +{ + Q_UNUSED(format); + return 0; +} + void QEglFSHooks::destroyNativeWindow(EGLNativeWindowType window) { Q_UNUSED(window); @@ -175,15 +197,17 @@ bool QEglFSHooks::hasCapability(QPlatformIntegration::Capability cap) const return false; } -QEGLPlatformCursor *QEglFSHooks::createCursor(QPlatformScreen *screen) const +QPlatformCursor *QEglFSHooks::createCursor(QPlatformScreen *screen) const { return new QEGLPlatformCursor(screen); } -void QEglFSHooks::waitForVSync() const +void QEglFSHooks::waitForVSync(QPlatformSurface *surface) const { + Q_UNUSED(surface); + #if defined(FBIO_WAITFORVSYNC) - static const bool forceSync = qgetenv("QT_QPA_EGLFS_FORCEVSYNC").toInt(); + static const bool forceSync = qEnvironmentVariableIntValue("QT_QPA_EGLFS_FORCEVSYNC"); if (forceSync && framebuffer != -1) { int arg = 0; if (ioctl(framebuffer, FBIO_WAITFORVSYNC, &arg) == -1) @@ -192,6 +216,16 @@ void QEglFSHooks::waitForVSync() const #endif } +void QEglFSHooks::presentBuffer(QPlatformSurface *surface) +{ + Q_UNUSED(surface); +} + +bool QEglFSHooks::supportsPBuffers() const +{ + return true; +} + #ifndef EGLFS_PLATFORM_HOOKS QEglFSHooks stubHooks; #endif diff --git a/src/plugins/platforms/eglfs/qeglfshooks_x11.cpp b/src/plugins/platforms/eglfs/qeglfshooks_x11.cpp index b16857297c..c026546ced 100644 --- a/src/plugins/platforms/eglfs/qeglfshooks_x11.cpp +++ b/src/plugins/platforms/eglfs/qeglfshooks_x11.cpp @@ -265,7 +265,7 @@ QSize QEglFSX11Hooks::screenSize() const m_screenSize = QSize(env.at(0).toInt(), env.at(1).toInt()); } else { m_screenSize = QSize(640, 480); - qDebug("EGLFS_X11_SIZE not set, falling back to 640x480"); + qWarning("EGLFS_X11_SIZE not set, falling back to 640x480"); } } return m_screenSize; @@ -313,7 +313,7 @@ EGLNativeWindowType QEglFSX11Hooks::createNativeWindow(QPlatformWindow *platform xcb_change_property(m_connection, XCB_PROP_MODE_REPLACE, m_window, m_atoms[Atoms::WM_PROTOCOLS], XCB_ATOM_ATOM, 32, 1, &m_atoms[Atoms::WM_DELETE_WINDOW]); - if (qgetenv("EGLFS_X11_FULLSCREEN").toInt()) { + if (qEnvironmentVariableIntValue("EGLFS_X11_FULLSCREEN")) { // Go fullscreen. The QScreen and QWindow size is controlled by EGLFS_X11_SIZE regardless, // this is just the native window. xcb_change_property(m_connection, XCB_PROP_MODE_REPLACE, m_window, diff --git a/src/plugins/platforms/eglfs/qeglfsintegration.cpp b/src/plugins/platforms/eglfs/qeglfsintegration.cpp index 2a4eae3fe2..ffae64d31c 100644 --- a/src/plugins/platforms/eglfs/qeglfsintegration.cpp +++ b/src/plugins/platforms/eglfs/qeglfsintegration.cpp @@ -36,6 +36,7 @@ #include "qeglfswindow.h" #include "qeglfshooks.h" #include "qeglfscontext.h" +#include "qeglfsoffscreenwindow.h" #include <QtGui/private/qguiapplication_p.h> @@ -62,16 +63,11 @@ QT_BEGIN_NAMESPACE QEglFSIntegration::QEglFSIntegration() { - mDisableInputHandlers = qgetenv("QT_QPA_EGLFS_DISABLE_INPUT").toInt(); + mDisableInputHandlers = qEnvironmentVariableIntValue("QT_QPA_EGLFS_DISABLE_INPUT"); initResources(); } -QEglFSIntegration::~QEglFSIntegration() -{ - QEglFSHooks::hooks()->platformDestroy(); -} - bool QEglFSIntegration::hasCapability(QPlatformIntegration::Capability cap) const { // We assume that devices will have more and not less capabilities @@ -81,6 +77,11 @@ bool QEglFSIntegration::hasCapability(QPlatformIntegration::Capability cap) cons return QEGLPlatformIntegration::hasCapability(cap); } +void QEglFSIntegration::addScreen(QPlatformScreen *screen) +{ + screenAdded(screen); +} + void QEglFSIntegration::initialize() { QEglFSHooks::hooks()->platformInit(); @@ -89,16 +90,20 @@ void QEglFSIntegration::initialize() if (!mDisableInputHandlers) createInputHandlers(); + + QEglFSHooks::hooks()->screenInit(); } -EGLNativeDisplayType QEglFSIntegration::nativeDisplay() const +void QEglFSIntegration::destroy() { - return QEglFSHooks::hooks()->platformDisplay(); + QEglFSHooks::hooks()->screenDestroy(); + QEGLPlatformIntegration::destroy(); + QEglFSHooks::hooks()->platformDestroy(); } -QEGLPlatformScreen *QEglFSIntegration::createScreen() const +EGLNativeDisplayType QEglFSIntegration::nativeDisplay() const { - return new QEglFSScreen(display()); + return QEglFSHooks::hooks()->platformDisplay(); } QEGLPlatformWindow *QEglFSIntegration::createWindow(QWindow *window) const @@ -115,9 +120,9 @@ QEGLPlatformContext *QEglFSIntegration::createContext(const QSurfaceFormat &form QSurfaceFormat adjustedFormat = QEglFSHooks::hooks()->surfaceFormatFor(format); if (!nativeHandle || nativeHandle->isNull()) { EGLConfig config = QEglFSIntegration::chooseConfig(display, adjustedFormat); - ctx = new QEglFSContext(adjustedFormat, shareContext, display, &config, QVariant()); + ctx = new QEglFSContext(adjustedFormat, shareContext, display, &config, QVariant()); } else { - ctx = new QEglFSContext(adjustedFormat, shareContext, display, 0, *nativeHandle); + ctx = new QEglFSContext(adjustedFormat, shareContext, display, 0, *nativeHandle); } *nativeHandle = QVariant::fromValue<QEGLNativeContext>(QEGLNativeContext(ctx->eglContext(), display)); return ctx; @@ -127,18 +132,13 @@ QPlatformOffscreenSurface *QEglFSIntegration::createOffscreenSurface(EGLDisplay const QSurfaceFormat &format, QOffscreenSurface *surface) const { - return new QEGLPbuffer(display, QEglFSHooks::hooks()->surfaceFormatFor(format), surface); -} + QSurfaceFormat fmt = QEglFSHooks::hooks()->surfaceFormatFor(format); + if (QEglFSHooks::hooks()->supportsPBuffers()) + return new QEGLPbuffer(display, fmt, surface); + else + return new QEglFSOffscreenWindow(display, fmt, surface); -QVariant QEglFSIntegration::styleHint(QPlatformIntegration::StyleHint hint) const -{ - switch (hint) - { - case QPlatformIntegration::ShowIsFullScreen: - return screen()->compositingWindow() == 0; - default: - return QPlatformIntegration::styleHint(hint); - } + // Never return null. Multiple QWindows are not supported by this plugin. } EGLConfig QEglFSIntegration::chooseConfig(EGLDisplay display, const QSurfaceFormat &format) diff --git a/src/plugins/platforms/eglfs/qeglfsintegration.h b/src/plugins/platforms/eglfs/qeglfsintegration.h index d612a270f2..86038910ca 100644 --- a/src/plugins/platforms/eglfs/qeglfsintegration.h +++ b/src/plugins/platforms/eglfs/qeglfsintegration.h @@ -44,17 +44,17 @@ class QEglFSIntegration : public QEGLPlatformIntegration { public: QEglFSIntegration(); - ~QEglFSIntegration(); + + void addScreen(QPlatformScreen *screen); void initialize() Q_DECL_OVERRIDE; + void destroy() Q_DECL_OVERRIDE; bool hasCapability(QPlatformIntegration::Capability cap) const Q_DECL_OVERRIDE; - QVariant styleHint(QPlatformIntegration::StyleHint hint) const Q_DECL_OVERRIDE; static EGLConfig chooseConfig(EGLDisplay display, const QSurfaceFormat &format); protected: - QEGLPlatformScreen *createScreen() const Q_DECL_OVERRIDE; QEGLPlatformWindow *createWindow(QWindow *window) const Q_DECL_OVERRIDE; QEGLPlatformContext *createContext(const QSurfaceFormat &format, QPlatformOpenGLContext *shareContext, diff --git a/src/plugins/platforms/eglfs/qeglfsoffscreenwindow.cpp b/src/plugins/platforms/eglfs/qeglfsoffscreenwindow.cpp new file mode 100644 index 0000000000..0e8e3957d0 --- /dev/null +++ b/src/plugins/platforms/eglfs/qeglfsoffscreenwindow.cpp @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qeglfsoffscreenwindow.h" +#include "qeglfshooks.h" +#include <QtGui/QOffscreenSurface> +#include <QtPlatformSupport/private/qeglconvenience_p.h> + +QT_BEGIN_NAMESPACE + +/* + In some cases pbuffers are not available. Triggering QtGui's built-in + fallback for a hidden QWindow is not suitable for eglfs since this would be + treated as an attempt to create multiple top-level, native windows. + + Therefore this class is provided as an alternative to QEGLPbuffer. + + This class requires the hooks to implement createNativeOffscreenWindow(). +*/ + +QEglFSOffscreenWindow::QEglFSOffscreenWindow(EGLDisplay display, const QSurfaceFormat &format, QOffscreenSurface *offscreenSurface) + : QPlatformOffscreenSurface(offscreenSurface) + , m_format(format) + , m_display(display) + , m_surface(EGL_NO_SURFACE) + , m_window(0) +{ + m_window = QEglFSHooks::hooks()->createNativeOffscreenWindow(format); + if (!m_window) { + qWarning("QEglFSOffscreenWindow: Failed to create native window"); + return; + } + EGLConfig config = q_configFromGLFormat(m_display, m_format); + m_surface = eglCreateWindowSurface(m_display, config, m_window, 0); + if (m_surface != EGL_NO_SURFACE) + m_format = q_glFormatFromConfig(m_display, config); +} + +QEglFSOffscreenWindow::~QEglFSOffscreenWindow() +{ + if (m_surface != EGL_NO_SURFACE) + eglDestroySurface(m_display, m_surface); + if (m_window) + QEglFSHooks::hooks()->destroyNativeWindow(m_window); +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/eglfs/qeglfsoffscreenwindow.h b/src/plugins/platforms/eglfs/qeglfsoffscreenwindow.h new file mode 100644 index 0000000000..8bd4089c34 --- /dev/null +++ b/src/plugins/platforms/eglfs/qeglfsoffscreenwindow.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QEGLFSOFFSCREENWINDOW_H +#define QEGLFSOFFSCREENWINDOW_H + +#include <EGL/egl.h> +#include <qpa/qplatformoffscreensurface.h> + +QT_BEGIN_NAMESPACE + +class QEglFSOffscreenWindow : public QPlatformOffscreenSurface +{ +public: + QEglFSOffscreenWindow(EGLDisplay display, const QSurfaceFormat &format, QOffscreenSurface *offscreenSurface); + ~QEglFSOffscreenWindow(); + + QSurfaceFormat format() const Q_DECL_OVERRIDE { return m_format; } + bool isValid() const Q_DECL_OVERRIDE { return m_surface != EGL_NO_SURFACE; } + +private: + QSurfaceFormat m_format; + EGLDisplay m_display; + EGLSurface m_surface; + EGLNativeWindowType m_window; +}; + +QT_END_NAMESPACE + +#endif // QEGLFSOFFSCREENWINDOW_H diff --git a/src/plugins/platforms/eglfs/qeglfsscreen.cpp b/src/plugins/platforms/eglfs/qeglfsscreen.cpp index cd68540581..60f7347ba5 100644 --- a/src/plugins/platforms/eglfs/qeglfsscreen.cpp +++ b/src/plugins/platforms/eglfs/qeglfsscreen.cpp @@ -34,7 +34,7 @@ #include "qeglfsscreen.h" #include "qeglfswindow.h" #include "qeglfshooks.h" -#include <QtPlatformSupport/private/qeglplatformcursor_p.h> +#include <QtGui/qpa/qplatformcursor.h> QT_BEGIN_NAMESPACE diff --git a/src/plugins/platforms/eglfs/qeglfsscreen.h b/src/plugins/platforms/eglfs/qeglfsscreen.h index 132646d7a6..c5ab3b52f5 100644 --- a/src/plugins/platforms/eglfs/qeglfsscreen.h +++ b/src/plugins/platforms/eglfs/qeglfsscreen.h @@ -42,7 +42,6 @@ QT_BEGIN_NAMESPACE -class QEGLPlatformCursor; class QEglFSWindow; class QOpenGLContext; @@ -78,7 +77,7 @@ private: friend class QEglFSWindow; EGLSurface m_surface; - QEGLPlatformCursor *m_cursor; + QPlatformCursor *m_cursor; QEGLPlatformWindow *m_rootWindow; QOpenGLContext *m_rootContext; }; diff --git a/src/plugins/platforms/eglfs/qeglfswindow.cpp b/src/plugins/platforms/eglfs/qeglfswindow.cpp index f5839e086d..59c2014d66 100644 --- a/src/plugins/platforms/eglfs/qeglfswindow.cpp +++ b/src/plugins/platforms/eglfs/qeglfswindow.cpp @@ -116,7 +116,7 @@ void QEglFSWindow::destroy() { QEglFSScreen *screen = this->screen(); if (m_flags.testFlag(HasNativeWindow)) { - QEGLPlatformCursor *cursor = static_cast<QEGLPlatformCursor *>(screen->cursor()); + QEGLPlatformCursor *cursor = qobject_cast<QEGLPlatformCursor *>(screen->cursor()); if (cursor) cursor->resetResources(); @@ -147,8 +147,9 @@ void QEglFSWindow::invalidateSurface() void QEglFSWindow::resetSurface() { - EGLDisplay display = static_cast<QEglFSScreen *>(screen())->display(); - m_window = QEglFSHooks::hooks()->createNativeWindow(this, QEglFSHooks::hooks()->screenSize(), m_format); + QEglFSScreen *nativeScreen = static_cast<QEglFSScreen *>(screen()); + EGLDisplay display = nativeScreen->display(); + m_window = QEglFSHooks::hooks()->createNativeWindow(this, nativeScreen->geometry().size(), m_format); m_surface = eglCreateWindowSurface(display, m_config, m_window, NULL); if (m_surface == EGL_NO_SURFACE) { EGLint error = eglGetError(); diff --git a/src/plugins/platforms/ios/quiaccessibilityelement.mm b/src/plugins/platforms/ios/quiaccessibilityelement.mm index 63b6827ad3..3dff271d00 100644 --- a/src/plugins/platforms/ios/quiaccessibilityelement.mm +++ b/src/plugins/platforms/ios/quiaccessibilityelement.mm @@ -193,4 +193,33 @@ action->doAction(QAccessibleActionInterface::decreaseAction()); } +- (BOOL) accessibilityScroll : (UIAccessibilityScrollDirection) direction +{ + QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid); + QAccessibleActionInterface *action = iface->actionInterface(); + if (!action) + return NO; + switch (direction) { + case UIAccessibilityScrollDirectionRight: + action->doAction(QAccessibleActionInterface::scrollRightAction()); + return YES; + case UIAccessibilityScrollDirectionLeft: + action->doAction(QAccessibleActionInterface::scrollLeftAction()); + return YES; + case UIAccessibilityScrollDirectionUp: + action->doAction(QAccessibleActionInterface::scrollUpAction()); + return YES; + case UIAccessibilityScrollDirectionDown: + action->doAction(QAccessibleActionInterface::scrollDownAction()); + return YES; + case UIAccessibilityScrollDirectionNext: + action->doAction(QAccessibleActionInterface::nextPageAction()); + return YES; + case UIAccessibilityScrollDirectionPrevious: + action->doAction(QAccessibleActionInterface::previousPageAction()); + return YES; + } + return NO; +} + @end diff --git a/src/plugins/platforms/kms/qkmsscreen.cpp b/src/plugins/platforms/kms/qkmsscreen.cpp index ddcdd10928..84c1eba448 100644 --- a/src/plugins/platforms/kms/qkmsscreen.cpp +++ b/src/plugins/platforms/kms/qkmsscreen.cpp @@ -216,7 +216,7 @@ void QKmsScreen::performPageFlip() // Initialize cursor - static int hideCursor = qgetenv("QT_QPA_KMS_HIDECURSOR").toInt(); + static int hideCursor = qEnvironmentVariableIntValue("QT_QPA_KMS_HIDECURSOR"); if (!hideCursor) { QCursor cursor(Qt::ArrowCursor); m_cursor->changeCursor(&cursor, 0); diff --git a/src/plugins/platforms/minimal/qminimalintegration.cpp b/src/plugins/platforms/minimal/qminimalintegration.cpp index 252f842df4..6bd9d8c96d 100644 --- a/src/plugins/platforms/minimal/qminimalintegration.cpp +++ b/src/plugins/platforms/minimal/qminimalintegration.cpp @@ -66,7 +66,7 @@ QMinimalIntegration::QMinimalIntegration(const QStringList ¶meters) , m_options(parseOptions(parameters)) { if (qEnvironmentVariableIsSet(debugBackingStoreEnvironmentVariable) - && qgetenv(debugBackingStoreEnvironmentVariable).toInt() > 0) { + && qEnvironmentVariableIntValue(debugBackingStoreEnvironmentVariable) > 0) { m_options |= DebugBackingStore | EnableFonts; } diff --git a/src/plugins/platforms/qnx/qqnxglcontext.cpp b/src/plugins/platforms/qnx/qqnxglcontext.cpp index 09736304e1..7133d01e87 100644 --- a/src/plugins/platforms/qnx/qqnxglcontext.cpp +++ b/src/plugins/platforms/qnx/qqnxglcontext.cpp @@ -164,7 +164,7 @@ EGLenum QQnxGLContext::checkEGLError(const char *msg) return error; } -void QQnxGLContext::initialize() +void QQnxGLContext::initializeContext() { qGLContextDebug() << Q_FUNC_INFO; @@ -182,7 +182,7 @@ void QQnxGLContext::initialize() } } -void QQnxGLContext::shutdown() +void QQnxGLContext::shutdownContext() { qGLContextDebug() << Q_FUNC_INFO; diff --git a/src/plugins/platforms/qnx/qqnxglcontext.h b/src/plugins/platforms/qnx/qqnxglcontext.h index 090c7ed49d..a91a89901f 100644 --- a/src/plugins/platforms/qnx/qqnxglcontext.h +++ b/src/plugins/platforms/qnx/qqnxglcontext.h @@ -53,8 +53,8 @@ public: static EGLenum checkEGLError(const char *msg); - static void initialize(); - static void shutdown(); + static void initializeContext(); + static void shutdownContext(); void requestSurfaceChange(); diff --git a/src/plugins/platforms/qnx/qqnxintegration.cpp b/src/plugins/platforms/qnx/qqnxintegration.cpp index 34b79b61da..6a3cd902af 100644 --- a/src/plugins/platforms/qnx/qqnxintegration.cpp +++ b/src/plugins/platforms/qnx/qqnxintegration.cpp @@ -182,7 +182,7 @@ QQnxIntegration::QQnxIntegration(const QStringList ¶mList) #if !defined(QT_NO_OPENGL) // Initialize global OpenGL resources - QQnxGLContext::initialize(); + QQnxGLContext::initializeContext(); #endif // Create/start event thread @@ -306,7 +306,7 @@ QQnxIntegration::~QQnxIntegration() #if !defined(QT_NO_OPENGL) // Cleanup global OpenGL resources - QQnxGLContext::shutdown(); + QQnxGLContext::shutdownContext(); #endif #if defined(QQNX_PPS) diff --git a/src/plugins/platforms/qnx/qqnxscreen.cpp b/src/plugins/platforms/qnx/qqnxscreen.cpp index f3f93e7f64..04a6611cc4 100644 --- a/src/plugins/platforms/qnx/qqnxscreen.cpp +++ b/src/plugins/platforms/qnx/qqnxscreen.cpp @@ -305,7 +305,7 @@ static int defaultDepth() if (defaultDepth == 0) { // check if display depth was specified in environment variable; // use default value if no valid value found - defaultDepth = qgetenv("QQNX_DISPLAY_DEPTH").toInt(); + defaultDepth = qEnvironmentVariableIntValue("QQNX_DISPLAY_DEPTH"); if (defaultDepth != 16 && defaultDepth != 32) defaultDepth = 32; } @@ -679,7 +679,7 @@ void QQnxScreen::adjustOrientation() return; bool ok = false; - const int rotation = qgetenv("ORIENTATION").toInt(&ok); + const int rotation = qEnvironmentVariableIntValue("ORIENTATION", &ok); if (ok) setRotation(rotation); @@ -858,7 +858,7 @@ void QQnxScreen::setRootWindow(QQnxWindow *window) { // Optionally disable the screen power save bool ok = false; - const int disablePowerSave = qgetenv("QQNX_DISABLE_POWER_SAVE").toInt(&ok); + const int disablePowerSave = qEnvironmentVariableIntValue("QQNX_DISABLE_POWER_SAVE", &ok); if (ok && disablePowerSave) { const int mode = SCREEN_IDLE_MODE_KEEP_AWAKE; int result = screen_set_window_property_iv(window->nativeHandle(), SCREEN_PROPERTY_IDLE_MODE, &mode); diff --git a/src/plugins/platforms/windows/accessible/iaccessible2.h b/src/plugins/platforms/windows/accessible/iaccessible2.h index 351bf2555c..f7f5e022c3 100644 --- a/src/plugins/platforms/windows/accessible/iaccessible2.h +++ b/src/plugins/platforms/windows/accessible/iaccessible2.h @@ -39,24 +39,7 @@ #include "qwindowsmsaaaccessible.h" #include "comutils.h" -#include "Accessible2.h" -#include "AccessibleAction.h" -#include "AccessibleApplication.h" -#include "AccessibleComponent.h" -#include "AccessibleEditableText.h" -#include "AccessibleHyperlink.h" -#include "AccessibleHypertext.h" -#include "AccessibleImage.h" -#include "AccessibleRelation.h" -#include "AccessibleTable.h" -#include "AccessibleTable2.h" -#include "AccessibleTableCell.h" -#include "AccessibleText.h" -#include "AccessibleValue.h" - -#include "AccessibleEventID.h" -#include "AccessibleRole.h" -#include "AccessibleStates.h" +#include "ia2_api_all.h" #include <servprov.h> diff --git a/src/plugins/platforms/windows/accessible/qwindowsmsaaaccessible.h b/src/plugins/platforms/windows/accessible/qwindowsmsaaaccessible.h index a78d821b1d..c522cf281e 100644 --- a/src/plugins/platforms/windows/accessible/qwindowsmsaaaccessible.h +++ b/src/plugins/platforms/windows/accessible/qwindowsmsaaaccessible.h @@ -42,7 +42,7 @@ #include <QtGui/qaccessible.h> #ifndef Q_CC_MINGW # include <oleacc.h> -# include "Accessible2.h" // IAccessible2 inherits from IAccessible +# include "ia2_api_all.h" // IAccessible2 inherits from IAccessible #else // MinGW # include <basetyps.h> diff --git a/src/plugins/platforms/windows/qwindowsfontengine.cpp b/src/plugins/platforms/windows/qwindowsfontengine.cpp index d8ccb8cd56..e6f30f2c35 100644 --- a/src/plugins/platforms/windows/qwindowsfontengine.cpp +++ b/src/plugins/platforms/windows/qwindowsfontengine.cpp @@ -1181,19 +1181,13 @@ QImage QWindowsFontEngine::alphaMapForGlyph(glyph_t glyph, const QTransform &xfo return QImage(); } - QImage indexed(mask->width(), mask->height(), QImage::Format_Indexed8); + QImage alphaMap(mask->width(), mask->height(), QImage::Format_Alpha8); - // ### This part is kinda pointless, but we'll crash later if we don't because some - // code paths expects there to be colortables for index8-bit... - QVector<QRgb> colors(256); - for (int i=0; i<256; ++i) - colors[i] = qRgba(0, 0, 0, i); - indexed.setColorTable(colors); // Copy data... Cannot use QPainter here as GDI has messed up the // Alpha channel of the ni.image pixels... for (int y=0; y<mask->height(); ++y) { - uchar *dest = indexed.scanLine(y); + uchar *dest = alphaMap.scanLine(y); if (mask->image().format() == QImage::Format_RGB16) { const qint16 *src = (qint16 *) ((const QImage &) mask->image()).scanLine(y); for (int x=0; x<mask->width(); ++x) @@ -1215,7 +1209,7 @@ QImage QWindowsFontEngine::alphaMapForGlyph(glyph_t glyph, const QTransform &xfo DeleteObject(font); } - return indexed; + return alphaMap; } #define SPI_GETFONTSMOOTHINGCONTRAST 0x200C diff --git a/src/plugins/platforms/windows/qwindowsfontenginedirectwrite.cpp b/src/plugins/platforms/windows/qwindowsfontenginedirectwrite.cpp index 648f68bb19..f8747a741e 100644 --- a/src/plugins/platforms/windows/qwindowsfontenginedirectwrite.cpp +++ b/src/plugins/platforms/windows/qwindowsfontenginedirectwrite.cpp @@ -485,15 +485,11 @@ QImage QWindowsFontEngineDirectWrite::alphaMapForGlyph(glyph_t glyph, QFixed sub { QImage im = imageForGlyph(glyph, subPixelPosition, 0, QTransform()); - QImage indexed(im.width(), im.height(), QImage::Format_Indexed8); - QVector<QRgb> colors(256); - for (int i=0; i<256; ++i) - colors[i] = qRgba(0, 0, 0, i); - indexed.setColorTable(colors); + QImage alphaMap(im.width(), im.height(), QImage::Format_Alpha8); for (int y=0; y<im.height(); ++y) { uint *src = (uint*) im.scanLine(y); - uchar *dst = indexed.scanLine(y); + uchar *dst = alphaMap.scanLine(y); for (int x=0; x<im.width(); ++x) { *dst = 255 - (m_fontEngineData->pow_gamma[qGray(0xffffffff - *src)] * 255. / 2047.); ++dst; @@ -501,7 +497,7 @@ QImage QWindowsFontEngineDirectWrite::alphaMapForGlyph(glyph_t glyph, QFixed sub } } - return indexed; + return alphaMap; } bool QWindowsFontEngineDirectWrite::supportsSubPixelPositions() const diff --git a/src/plugins/platforms/windows/qwindowsmime.cpp b/src/plugins/platforms/windows/qwindowsmime.cpp index bc8e9347dc..42f4e66d6c 100644 --- a/src/plugins/platforms/windows/qwindowsmime.cpp +++ b/src/plugins/platforms/windows/qwindowsmime.cpp @@ -343,10 +343,11 @@ static bool setData(const QByteArray &data, STGMEDIUM *pmedium) return true; } -static QByteArray getData(int cf, IDataObject *pDataObj) +static QByteArray getData(int cf, IDataObject *pDataObj, int lindex = -1) { QByteArray data; FORMATETC formatetc = setCf(cf); + formatetc.lindex = lindex; STGMEDIUM s; if (pDataObj->GetData(&formatetc, &s) == S_OK) { DWORD * val = (DWORD*)GlobalLock(s.hGlobal); @@ -791,7 +792,6 @@ QVariant QWindowsMimeURI::convertToMime(const QString &mimeType, LPDATAOBJECT pD { if (mimeType == QLatin1String("text/uri-list")) { if (canGetData(CF_HDROP, pDataObj)) { - QByteArray texturi; QList<QVariant> urls; QByteArray data = getData(CF_HDROP, pDataObj); @@ -1340,21 +1340,34 @@ static bool isCustomMimeType(const QString &mimeType) return mimeType.startsWith(QLatin1String(x_qt_windows_mime), Qt::CaseInsensitive); } -static QString customMimeType(const QString &mimeType) +static QString customMimeType(const QString &mimeType, int *lindex = 0) { int len = sizeof(x_qt_windows_mime) - 1; - int n = mimeType.lastIndexOf(QLatin1Char('\"'))-len; - return mimeType.mid(len, n); + int n = mimeType.lastIndexOf(QLatin1Char('\"')) - len; + QString ret = mimeType.mid(len, n); + + const int beginPos = mimeType.indexOf(QLatin1String(";index=")); + if (beginPos > -1) { + const int endPos = mimeType.indexOf(QLatin1Char(';'), beginPos + 1); + const int indexStartPos = beginPos + 7; + if (lindex) + *lindex = mimeType.midRef(indexStartPos, endPos == -1 ? endPos : endPos - indexStartPos).toInt(); + } else { + if (lindex) + *lindex = -1; + } + return ret; } bool QLastResortMimes::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const { if (isCustomMimeType(mimeType)) { + // MSDN documentation for QueryGetData says only -1 is supported, so ignore lindex here. QString clipFormat = customMimeType(mimeType); int cf = RegisterClipboardFormat(reinterpret_cast<const wchar_t *> (clipFormat.utf16())); return canGetData(cf, pDataObj); } else if (formats.keys(mimeType).isEmpty()) { - // if it is not in there then register it an see if we can get it + // if it is not in there then register it and see if we can get it int cf = QWindowsMime::registerMimeType(mimeType); return canGetData(cf, pDataObj); } else { @@ -1370,9 +1383,10 @@ QVariant QLastResortMimes::convertToMime(const QString &mimeType, IDataObject *p if (canConvertToMime(mimeType, pDataObj)) { QByteArray data; if (isCustomMimeType(mimeType)) { - QString clipFormat = customMimeType(mimeType); + int lindex; + QString clipFormat = customMimeType(mimeType, &lindex); int cf = RegisterClipboardFormat(reinterpret_cast<const wchar_t *> (clipFormat.utf16())); - data = getData(cf, pDataObj); + data = getData(cf, pDataObj, lindex); } else if (formats.keys(mimeType).isEmpty()) { int cf = QWindowsMime::registerMimeType(mimeType); data = getData(cf, pDataObj); @@ -1452,6 +1466,7 @@ QWindowsMime * QWindowsMimeConverter::converterToMime(const QString &mimeType, I QStringList QWindowsMimeConverter::allMimesForFormats(IDataObject *pDataObj) const { + qCDebug(lcQpaMime) << "QWindowsMime::allMimesForFormats()"; ensureInitialized(); QStringList formats; LPENUMFORMATETC FAR fmtenum; @@ -1461,10 +1476,9 @@ QStringList QWindowsMimeConverter::allMimesForFormats(IDataObject *pDataObj) con FORMATETC fmtetc; while (S_OK == fmtenum->Next(1, &fmtetc, 0)) { #if defined(QMIME_DEBUG) - qDebug("QWindowsMime::allMimesForFormats()"); wchar_t buf[256] = {0}; GetClipboardFormatName(fmtetc.cfFormat, buf, 255); - qDebug("CF = %d : %s", fmtetc.cfFormat, QString::fromWCharArray(buf)); + qDebug("CF = %d : %s", fmtetc.cfFormat, qPrintable(QString::fromWCharArray(buf))); #endif for (int i= m_mimes.size() - 1; i >= 0; --i) { QString format = m_mimes.at(i)->mimeForFormat(fmtetc); @@ -1478,7 +1492,7 @@ QStringList QWindowsMimeConverter::allMimesForFormats(IDataObject *pDataObj) con } fmtenum->Release(); } - qCDebug(lcQpaMime) << __FUNCTION__ << pDataObj << formats; + qCDebug(lcQpaMime) << pDataObj << formats; return formats; } diff --git a/src/plugins/platforms/winrt/qwinrttheme.cpp b/src/plugins/platforms/winrt/qwinrttheme.cpp index f64b47960a..339a658c3e 100644 --- a/src/plugins/platforms/winrt/qwinrttheme.cpp +++ b/src/plugins/platforms/winrt/qwinrttheme.cpp @@ -170,7 +170,7 @@ QWinRTTheme::QWinRTTheme() bool QWinRTTheme::usePlatformNativeDialog(DialogType type) const { static bool useNativeDialogs = qEnvironmentVariableIsSet("QT_USE_WINRT_NATIVE_DIALOGS") - ? qgetenv("QT_USE_WINRT_NATIVE_DIALOGS").toInt() : true; + ? qEnvironmentVariableIntValue("QT_USE_WINRT_NATIVE_DIALOGS") : true; if (type == FileDialog || type == MessageDialog) return useNativeDialogs; diff --git a/src/plugins/platforms/xcb/qxcbscreen.cpp b/src/plugins/platforms/xcb/qxcbscreen.cpp index 8bdedba8ac..def6e2ce9a 100644 --- a/src/plugins/platforms/xcb/qxcbscreen.cpp +++ b/src/plugins/platforms/xcb/qxcbscreen.cpp @@ -320,7 +320,7 @@ QDpi QXcbScreen::logicalDpi() const qreal QXcbScreen::devicePixelRatio() const { - static int override_dpr = qgetenv("QT_DEVICE_PIXEL_RATIO").toInt(); + static int override_dpr = qEnvironmentVariableIntValue("QT_DEVICE_PIXEL_RATIO"); static bool auto_dpr = qgetenv("QT_DEVICE_PIXEL_RATIO").toLower() == "auto"; if (override_dpr > 0) return override_dpr; |