summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGerry Boland <gerry.boland@canonical.com>2016-11-02 16:46:53 +0000
committerMatti Paaso <matti.paaso@qt.io>2017-01-24 05:52:52 +0000
commitc28fde3fdac19fd5a5f614bb7983080031c924b3 (patch)
tree441d2d4ea4e1e17db49ed3b12c7c724cbc6db894
parent4b507e8257243e36f40089d57099c2d668c5884d (diff)
Mirclient: update based on upstream development in lp:qtubuntu
This is based on revision 360 of lp:qtubuntu. Main features/bugs fixed: - fix QQuickWidget-based app rendering - wire up Qt window types to Mir to enable desktop-based applications to function with a window manager - use QEGLPlatformContext and QEGLPBuffer instead of custom code - correctly populate and update list of QScreens - support for switching keyboard layouts - improve window resizing to fix visual glitching Change-Id: If816a858eb10b6356275d4b80c89a72562b3c29f Reviewed-by: Eirik Aavitsland <eirik.aavitsland@qt.io> Reviewed-by: Matti Paaso <matti.paaso@qt.io>
-rw-r--r--src/gui/configure.json2
-rw-r--r--src/plugins/platforms/mirclient/mirclient.pro18
-rw-r--r--src/plugins/platforms/mirclient/qmirclientappstatecontroller.cpp102
-rw-r--r--src/plugins/platforms/mirclient/qmirclientappstatecontroller.h62
-rw-r--r--src/plugins/platforms/mirclient/qmirclientbackingstore.cpp12
-rw-r--r--src/plugins/platforms/mirclient/qmirclientbackingstore.h1
-rw-r--r--src/plugins/platforms/mirclient/qmirclientclipboard.cpp303
-rw-r--r--src/plugins/platforms/mirclient/qmirclientclipboard.h37
-rw-r--r--src/plugins/platforms/mirclient/qmirclientcursor.cpp43
-rw-r--r--src/plugins/platforms/mirclient/qmirclientcursor.h2
-rw-r--r--src/plugins/platforms/mirclient/qmirclientdebugextension.cpp79
-rw-r--r--src/plugins/platforms/mirclient/qmirclientdebugextension.h63
-rw-r--r--src/plugins/platforms/mirclient/qmirclientdesktopwindow.cpp50
-rw-r--r--src/plugins/platforms/mirclient/qmirclientdesktopwindow.h53
-rw-r--r--src/plugins/platforms/mirclient/qmirclientglcontext.cpp159
-rw-r--r--src/plugins/platforms/mirclient/qmirclientglcontext.h28
-rw-r--r--src/plugins/platforms/mirclient/qmirclientinput.cpp331
-rw-r--r--src/plugins/platforms/mirclient/qmirclientinput.h10
-rw-r--r--src/plugins/platforms/mirclient/qmirclientintegration.cpp255
-rw-r--r--src/plugins/platforms/mirclient/qmirclientintegration.h47
-rw-r--r--src/plugins/platforms/mirclient/qmirclientlogging.h24
-rw-r--r--src/plugins/platforms/mirclient/qmirclientnativeinterface.cpp103
-rw-r--r--src/plugins/platforms/mirclient/qmirclientnativeinterface.h19
-rw-r--r--src/plugins/platforms/mirclient/qmirclientplugin.cpp15
-rw-r--r--src/plugins/platforms/mirclient/qmirclientplugin.h4
-rw-r--r--src/plugins/platforms/mirclient/qmirclientscreen.cpp278
-rw-r--r--src/plugins/platforms/mirclient/qmirclientscreen.h41
-rw-r--r--src/plugins/platforms/mirclient/qmirclientscreenobserver.cpp161
-rw-r--r--src/plugins/platforms/mirclient/qmirclientscreenobserver.h78
-rw-r--r--src/plugins/platforms/mirclient/qmirclientwindow.cpp819
-rw-r--r--src/plugins/platforms/mirclient/qmirclientwindow.h50
31 files changed, 2182 insertions, 1067 deletions
diff --git a/src/gui/configure.json b/src/gui/configure.json
index f862b5866c..5ab0b98c69 100644
--- a/src/gui/configure.json
+++ b/src/gui/configure.json
@@ -168,7 +168,7 @@
"label": "Mir client libraries",
"test": "qpa/mirclient",
"sources": [
- { "type": "pkgConfig", "args": "egl mirclient ubuntu-platform-api" }
+ { "type": "pkgConfig", "args": "egl mirclient ubuntu-platform-api libcontent-hub" }
]
},
"mtdev": {
diff --git a/src/plugins/platforms/mirclient/mirclient.pro b/src/plugins/platforms/mirclient/mirclient.pro
index 0ba63601a9..d2da7e6ca0 100644
--- a/src/plugins/platforms/mirclient/mirclient.pro
+++ b/src/plugins/platforms/mirclient/mirclient.pro
@@ -5,6 +5,9 @@ QT += \
theme_support-private eventdispatcher_support-private \
fontdatabase_support-private egl_support-private
+qtHaveModule(linuxaccessibility_support-private): \
+ QT += linuxaccessibility_support-private
+
DEFINES += MESA_EGL_NO_X11_HEADERS
# CONFIG += c++11 # only enables C++0x
QMAKE_CXXFLAGS += -fvisibility=hidden -fvisibility-inlines-hidden -std=c++11 -Werror -Wall
@@ -13,9 +16,12 @@ QMAKE_LFLAGS += -std=c++11 -Wl,-no-undefined
QMAKE_USE_PRIVATE += mirclient
SOURCES = \
+ qmirclientappstatecontroller.cpp \
qmirclientbackingstore.cpp \
qmirclientclipboard.cpp \
qmirclientcursor.cpp \
+ qmirclientdebugextension.cpp \
+ qmirclientdesktopwindow.cpp \
qmirclientglcontext.cpp \
qmirclientinput.cpp \
qmirclientintegration.cpp \
@@ -23,13 +29,17 @@ SOURCES = \
qmirclientplatformservices.cpp \
qmirclientplugin.cpp \
qmirclientscreen.cpp \
+ qmirclientscreenobserver.cpp \
qmirclienttheme.cpp \
qmirclientwindow.cpp
HEADERS = \
+ qmirclientappstatecontroller.h \
qmirclientbackingstore.h \
qmirclientclipboard.h \
qmirclientcursor.h \
+ qmirclientdebugextension.h \
+ qmirclientdesktopwindow.h \
qmirclientglcontext.h \
qmirclientinput.h \
qmirclientintegration.h \
@@ -39,9 +49,17 @@ HEADERS = \
qmirclientplatformservices.h \
qmirclientplugin.h \
qmirclientscreen.h \
+ qmirclientscreenobserver.h \
qmirclienttheme.h \
qmirclientwindow.h
+# libxkbcommon
+!qtConfig(xkbcommon-system) {
+ include(../../../3rdparty/xkbcommon.pri)
+} else {
+ QMAKE_USE += xkbcommon
+}
+
PLUGIN_TYPE = platforms
PLUGIN_CLASS_NAME = MirServerIntegrationPlugin
!equals(TARGET, $$QT_DEFAULT_QPA_PLUGIN): PLUGIN_EXTENDS = -
diff --git a/src/plugins/platforms/mirclient/qmirclientappstatecontroller.cpp b/src/plugins/platforms/mirclient/qmirclientappstatecontroller.cpp
new file mode 100644
index 0000000000..69fc9b7aa7
--- /dev/null
+++ b/src/plugins/platforms/mirclient/qmirclientappstatecontroller.cpp
@@ -0,0 +1,102 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Canonical, Ltd.
+** 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 "qmirclientappstatecontroller.h"
+
+#include <qpa/qwindowsysteminterface.h>
+
+/*
+ * QMirClientAppStateController - updates Qt's QApplication::applicationState property.
+ *
+ * Tries to avoid active-inactive-active invocations using a timer. The rapid state
+ * change can confuse some applications.
+ */
+
+QMirClientAppStateController::QMirClientAppStateController()
+ : m_suspended(false)
+ , m_lastActive(true)
+{
+ m_inactiveTimer.setSingleShot(true);
+ m_inactiveTimer.setInterval(10);
+ QObject::connect(&m_inactiveTimer, &QTimer::timeout, []()
+ {
+ QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationInactive);
+ });
+}
+
+void QMirClientAppStateController::setSuspended()
+{
+ m_inactiveTimer.stop();
+ if (!m_suspended) {
+ m_suspended = true;
+
+ QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationSuspended);
+ }
+}
+
+void QMirClientAppStateController::setResumed()
+{
+ m_inactiveTimer.stop();
+ if (m_suspended) {
+ m_suspended = false;
+
+ if (m_lastActive) {
+ QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationActive);
+ } else {
+ QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationInactive);
+ }
+ }
+}
+
+void QMirClientAppStateController::setWindowFocused(bool focused)
+{
+ if (m_suspended) {
+ return;
+ }
+
+ if (focused) {
+ m_inactiveTimer.stop();
+ QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationActive);
+ } else {
+ m_inactiveTimer.start();
+ }
+
+ m_lastActive = focused;
+}
diff --git a/src/plugins/platforms/mirclient/qmirclientappstatecontroller.h b/src/plugins/platforms/mirclient/qmirclientappstatecontroller.h
new file mode 100644
index 0000000000..b3aa0022d9
--- /dev/null
+++ b/src/plugins/platforms/mirclient/qmirclientappstatecontroller.h
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Canonical, Ltd.
+** 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 QMIRCLIENTAPPSTATECONTROLLER_H
+#define QMIRCLIENTAPPSTATECONTROLLER_H
+
+#include <QTimer>
+
+class QMirClientAppStateController
+{
+public:
+ QMirClientAppStateController();
+
+ void setSuspended();
+ void setResumed();
+
+ void setWindowFocused(bool focused);
+
+private:
+ bool m_suspended;
+ bool m_lastActive;
+ QTimer m_inactiveTimer;
+};
+
+#endif // QMIRCLIENTAPPSTATECONTROLLER_H
diff --git a/src/plugins/platforms/mirclient/qmirclientbackingstore.cpp b/src/plugins/platforms/mirclient/qmirclientbackingstore.cpp
index a4bb8864ab..51363619d9 100644
--- a/src/plugins/platforms/mirclient/qmirclientbackingstore.cpp
+++ b/src/plugins/platforms/mirclient/qmirclientbackingstore.cpp
@@ -61,6 +61,7 @@ QMirClientBackingStore::QMirClientBackingStore(QWindow* window)
QMirClientBackingStore::~QMirClientBackingStore()
{
+ mContext->makeCurrent(window()); // needed as QOpenGLTexture destructor assumes current context
}
void QMirClientBackingStore::flush(QWindow* window, const QRegion& region, const QPoint& offset)
@@ -76,7 +77,6 @@ void QMirClientBackingStore::flush(QWindow* window, const QRegion& region, const
mBlitter->create();
mBlitter->bind();
- mBlitter->setRedBlueSwizzle(true);
mBlitter->blit(mTexture->textureId(), QMatrix4x4(), QOpenGLTextureBlitter::OriginTopLeft);
mBlitter->release();
@@ -137,7 +137,9 @@ void QMirClientBackingStore::beginPaint(const QRegion& region)
void QMirClientBackingStore::resize(const QSize& size, const QRegion& /*staticContents*/)
{
- mImage = QImage(size, QImage::Format_RGB32);
+ mImage = QImage(size, QImage::Format_RGBA8888);
+
+ mContext->makeCurrent(window());
if (mTexture->isCreated())
mTexture->destroy();
@@ -147,3 +149,9 @@ QPaintDevice* QMirClientBackingStore::paintDevice()
{
return &mImage;
}
+
+QImage QMirClientBackingStore::toImage() const
+{
+ // used by QPlatformBackingStore::composeAndFlush
+ return mImage;
+}
diff --git a/src/plugins/platforms/mirclient/qmirclientbackingstore.h b/src/plugins/platforms/mirclient/qmirclientbackingstore.h
index 0c75e182ff..7644c77df2 100644
--- a/src/plugins/platforms/mirclient/qmirclientbackingstore.h
+++ b/src/plugins/platforms/mirclient/qmirclientbackingstore.h
@@ -58,6 +58,7 @@ public:
void flush(QWindow* window, const QRegion& region, const QPoint& offset) override;
void resize(const QSize& size, const QRegion& staticContents) override;
QPaintDevice* paintDevice() override;
+ QImage toImage() const override;
protected:
void updateTexture();
diff --git a/src/plugins/platforms/mirclient/qmirclientclipboard.cpp b/src/plugins/platforms/mirclient/qmirclientclipboard.cpp
index 4dff339f21..b9fc9b3b42 100644
--- a/src/plugins/platforms/mirclient/qmirclientclipboard.cpp
+++ b/src/plugins/platforms/mirclient/qmirclientclipboard.cpp
@@ -39,41 +39,36 @@
#include "qmirclientclipboard.h"
+#include "qmirclientlogging.h"
+#include "qmirclientwindow.h"
+#include <QDBusPendingCallWatcher>
+#include <QGuiApplication>
+#include <QSignalBlocker>
#include <QtCore/QMimeData>
#include <QtCore/QStringList>
-#include <QDBusInterface>
-#include <QDBusPendingCallWatcher>
-#include <QDBusPendingReply>
-
-// FIXME(loicm) The clipboard data format is not defined by Ubuntu Platform API
-// which makes it impossible to have non-Qt applications communicate with Qt
-// applications through the clipboard API. The solution would be to have
-// Ubuntu Platform define the data format or propose an API that supports
-// embedding different mime types in the clipboard.
-// Data format:
-// number of mime types (sizeof(int))
-// data layout ((4 * sizeof(int)) * number of mime types)
-// mime type string offset (sizeof(int))
-// mime type string size (sizeof(int))
-// data offset (sizeof(int))
-// data size (sizeof(int))
-// data (n bytes)
+// content-hub
+#include <com/ubuntu/content/hub.h>
-namespace {
-
-const int maxFormatsCount = 16;
-const int maxBufferSize = 4 * 1024 * 1024; // 4 Mb
-
-}
+// get this cumbersome nested namespace out of the way
+using namespace com::ubuntu::content;
QMirClientClipboard::QMirClientClipboard()
: mMimeData(new QMimeData)
- , mIsOutdated(true)
- , mUpdatesDisabled(false)
- , mDBusSetupDone(false)
+ , mContentHub(Hub::Client::instance())
{
+ connect(mContentHub, &Hub::pasteboardChanged, this, [this]() {
+ if (mClipboardState == QMirClientClipboard::SyncedClipboard) {
+ mClipboardState = QMirClientClipboard::OutdatedClipboard;
+ emitChanged(QClipboard::Clipboard);
+ }
+ });
+
+ connect(qGuiApp, &QGuiApplication::applicationStateChanged,
+ this, &QMirClientClipboard::onApplicationStateChanged);
+
+ requestMimeData();
}
QMirClientClipboard::~QMirClientClipboard()
@@ -81,236 +76,106 @@ QMirClientClipboard::~QMirClientClipboard()
delete mMimeData;
}
-void QMirClientClipboard::requestDBusClipboardContents()
+QMimeData* QMirClientClipboard::mimeData(QClipboard::Mode mode)
{
- if (!mDBusSetupDone) {
- setupDBus();
- }
-
- if (!mPendingGetContentsCall.isNull())
- return;
+ if (mode != QClipboard::Clipboard)
+ return nullptr;
- QDBusPendingCall pendingCall = mDBusClipboard->asyncCall(QStringLiteral("GetContents"));
+ // Blocks dataChanged() signal from being emitted. Makes no sense to emit it from
+ // inside the data getter.
+ const QSignalBlocker blocker(this);
- mPendingGetContentsCall = new QDBusPendingCallWatcher(pendingCall, this);
+ if (mClipboardState == OutdatedClipboard) {
+ updateMimeData();
+ } else if (mClipboardState == SyncingClipboard) {
+ mPasteReply->waitForFinished();
+ }
- QObject::connect(mPendingGetContentsCall.data(), &QDBusPendingCallWatcher::finished,
- this, &QMirClientClipboard::onDBusClipboardGetContentsFinished);
+ return mMimeData;
}
-void QMirClientClipboard::onDBusClipboardGetContentsFinished(QDBusPendingCallWatcher* call)
+void QMirClientClipboard::setMimeData(QMimeData* mimeData, QClipboard::Mode mode)
{
- Q_ASSERT(call == mPendingGetContentsCall.data());
+ QWindow *focusWindow = QGuiApplication::focusWindow();
+ if (focusWindow && mode == QClipboard::Clipboard && mimeData != nullptr) {
+ QString surfaceId = static_cast<QMirClientWindow*>(focusWindow->handle())->persistentSurfaceId();
- QDBusPendingReply<QByteArray> reply = *call;
- if (Q_UNLIKELY(reply.isError())) {
- qCritical("QMirClientClipboard - Failed to get system clipboard contents via D-Bus. %s, %s",
- qPrintable(reply.error().name()), qPrintable(reply.error().message()));
- // TODO: Might try again later a number of times...
- } else {
- QByteArray serializedMimeData = reply.argumentAt<0>();
- updateMimeData(serializedMimeData);
- }
- call->deleteLater();
-}
+ QDBusPendingCall reply = mContentHub->createPaste(surfaceId, *mimeData);
-void QMirClientClipboard::onDBusClipboardSetContentsFinished(QDBusPendingCallWatcher *call)
-{
- QDBusPendingReply<void> reply = *call;
- if (Q_UNLIKELY(reply.isError())) {
- qCritical("QMirClientClipboard - Failed to set the system clipboard contents via D-Bus. %s, %s",
- qPrintable(reply.error().name()), qPrintable(reply.error().message()));
- // TODO: Might try again later a number of times...
- }
- call->deleteLater();
-}
-
-void QMirClientClipboard::updateMimeData(const QByteArray &serializedMimeData)
-{
- if (mUpdatesDisabled)
- return;
+ // Don't care whether it succeeded
+ QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply, this);
+ connect(watcher, &QDBusPendingCallWatcher::finished,
+ watcher, &QObject::deleteLater);
- QMimeData *newMimeData = deserializeMimeData(serializedMimeData);
- if (newMimeData) {
- delete mMimeData;
- mMimeData = newMimeData;
- mIsOutdated = false;
+ mMimeData = mimeData;
+ mClipboardState = SyncedClipboard;
emitChanged(QClipboard::Clipboard);
- } else {
- qWarning("QMirClientClipboard - Got invalid serialized mime data. Ignoring it.");
}
}
-void QMirClientClipboard::setupDBus()
-{
- QDBusConnection dbusConnection = QDBusConnection::sessionBus();
-
- bool ok = dbusConnection.connect(
- QStringLiteral("com.canonical.QtMir"),
- QStringLiteral("/com/canonical/QtMir/Clipboard"),
- QStringLiteral("com.canonical.QtMir.Clipboard"),
- QStringLiteral("ContentsChanged"),
- this, SLOT(updateMimeData(QByteArray)));
- if (Q_UNLIKELY(!ok))
- qCritical("QMirClientClipboard - Failed to connect to ContentsChanged signal form the D-Bus system clipboard.");
-
- mDBusClipboard = new QDBusInterface(QStringLiteral("com.canonical.QtMir"),
- QStringLiteral("/com/canonical/QtMir/Clipboard"),
- QStringLiteral("com.canonical.QtMir.Clipboard"),
- dbusConnection);
-
- mDBusSetupDone = true;
-}
-
-QByteArray QMirClientClipboard::serializeMimeData(QMimeData *mimeData) const
+bool QMirClientClipboard::supportsMode(QClipboard::Mode mode) const
{
- Q_ASSERT(mimeData != nullptr);
-
- const QStringList formats = mimeData->formats();
- const int formatCount = qMin(formats.size(), maxFormatsCount);
- const int headerSize = sizeof(int) + (formatCount * 4 * sizeof(int));
- int bufferSize = headerSize;
-
- for (int i = 0; i < formatCount; i++)
- bufferSize += formats[i].size() + mimeData->data(formats[i]).size();
-
- QByteArray serializedMimeData;
- if (bufferSize <= maxBufferSize) {
- // Serialize data.
- serializedMimeData.resize(bufferSize);
- {
- char *buffer = serializedMimeData.data();
- int* header = reinterpret_cast<int*>(serializedMimeData.data());
- int offset = headerSize;
- header[0] = formatCount;
- for (int i = 0; i < formatCount; i++) {
- const QByteArray data = mimeData->data(formats[i]);
- const int formatOffset = offset;
- const int formatSize = formats[i].size();
- const int dataOffset = offset + formatSize;
- const int dataSize = data.size();
- memcpy(&buffer[formatOffset], formats[i].toLatin1().data(), formatSize);
- memcpy(&buffer[dataOffset], data.data(), dataSize);
- header[i*4+1] = formatOffset;
- header[i*4+2] = formatSize;
- header[i*4+3] = dataOffset;
- header[i*4+4] = dataSize;
- offset += formatSize + dataSize;
- }
- }
- } else {
- qWarning("QMirClientClipboard: Not sending contents (%d bytes) to the global clipboard as it's"
- " bigger than the maximum allowed size of %d bytes", bufferSize, maxBufferSize);
- }
-
- return serializedMimeData;
+ return mode == QClipboard::Clipboard;
}
-QMimeData *QMirClientClipboard::deserializeMimeData(const QByteArray &serializedMimeData) const
+bool QMirClientClipboard::ownsMode(QClipboard::Mode mode) const
{
- if (static_cast<std::size_t>(serializedMimeData.size()) < sizeof(int)) {
- // Data is invalid
- return nullptr;
- }
-
- QMimeData *mimeData = new QMimeData;
-
- const char* const buffer = serializedMimeData.constData();
- const int* const header = reinterpret_cast<const int*>(serializedMimeData.constData());
-
- const int count = qMin(header[0], maxFormatsCount);
-
- for (int i = 0; i < count; i++) {
- const int formatOffset = header[i*4+1];
- const int formatSize = header[i*4+2];
- const int dataOffset = header[i*4+3];
- const int dataSize = header[i*4+4];
-
- if (formatOffset + formatSize <= serializedMimeData.size()
- && dataOffset + dataSize <= serializedMimeData.size()) {
-
- QString mimeType = QString::fromLatin1(&buffer[formatOffset], formatSize);
- QByteArray mimeDataBytes(&buffer[dataOffset], dataSize);
-
- mimeData->setData(mimeType, mimeDataBytes);
- }
- }
-
- return mimeData;
+ Q_UNUSED(mode);
+ return false;
}
-QMimeData* QMirClientClipboard::mimeData(QClipboard::Mode mode)
+void QMirClientClipboard::onApplicationStateChanged(Qt::ApplicationState state)
{
- if (mode != QClipboard::Clipboard)
- return nullptr;
-
- if (mIsOutdated && mPendingGetContentsCall.isNull()) {
- requestDBusClipboardContents();
+ if (state == Qt::ApplicationActive) {
+ // Only focused or active applications might be allowed to paste, so we probably
+ // missed changes in the clipboard while we were hidden, inactive or, more importantly,
+ // suspended.
+ requestMimeData();
}
-
- // Return whatever we have at the moment instead of blocking until we have something.
- //
- // This might be called during app startup just for the sake of checking if some
- // "Paste" UI control should be enabled or not.
- // We will emit QClipboard::changed() once we finally have something.
- return mMimeData;
}
-void QMirClientClipboard::setMimeData(QMimeData* mimeData, QClipboard::Mode mode)
+void QMirClientClipboard::updateMimeData()
{
- if (mode != QClipboard::Clipboard)
+ if (qGuiApp->applicationState() != Qt::ApplicationActive) {
+ // Don't even bother asking as content-hub would probably ignore our request (and should).
return;
-
- if (!mPendingGetContentsCall.isNull()) {
- // Ignore whatever comes from the system clipboard as we are going to change it anyway
- QObject::disconnect(mPendingGetContentsCall.data(), 0, this, 0);
- mUpdatesDisabled = true;
- mPendingGetContentsCall->waitForFinished();
- mUpdatesDisabled = false;
- delete mPendingGetContentsCall.data();
}
- if (mimeData != nullptr) {
- QByteArray serializedMimeData = serializeMimeData(mimeData);
- if (!serializedMimeData.isEmpty()) {
- setDBusClipboardContents(serializedMimeData);
- }
+ delete mMimeData;
- mMimeData = mimeData;
+ QWindow *focusWindow = QGuiApplication::focusWindow();
+ if (focusWindow) {
+ QString surfaceId = static_cast<QMirClientWindow*>(focusWindow->handle())->persistentSurfaceId();
+ mMimeData = mContentHub->latestPaste(surfaceId);
+ mClipboardState = SyncedClipboard;
emitChanged(QClipboard::Clipboard);
}
}
-bool QMirClientClipboard::supportsMode(QClipboard::Mode mode) const
-{
- return mode == QClipboard::Clipboard;
-}
-
-bool QMirClientClipboard::ownsMode(QClipboard::Mode mode) const
+void QMirClientClipboard::requestMimeData()
{
- Q_UNUSED(mode);
- return false;
-}
-
-void QMirClientClipboard::setDBusClipboardContents(const QByteArray &clipboardContents)
-{
- if (!mDBusSetupDone) {
- setupDBus();
+ if (qGuiApp->applicationState() != Qt::ApplicationActive) {
+ // Don't even bother asking as content-hub would probably ignore our request (and should).
+ return;
}
- if (!mPendingSetContentsCall.isNull()) {
- // Ignore any previous set call as we are going to overwrite it anyway
- QObject::disconnect(mPendingSetContentsCall.data(), 0, this, 0);
- mUpdatesDisabled = true;
- mPendingSetContentsCall->waitForFinished();
- mUpdatesDisabled = false;
- delete mPendingSetContentsCall.data();
+ QWindow *focusWindow = QGuiApplication::focusWindow();
+ if (!focusWindow) {
+ return;
}
- QDBusPendingCall pendingCall = mDBusClipboard->asyncCall(QStringLiteral("SetContents"), clipboardContents);
-
- mPendingSetContentsCall = new QDBusPendingCallWatcher(pendingCall, this);
+ QString surfaceId = static_cast<QMirClientWindow*>(focusWindow->handle())->persistentSurfaceId();
+ QDBusPendingCall reply = mContentHub->requestLatestPaste(surfaceId);
+ mClipboardState = SyncingClipboard;
- QObject::connect(mPendingSetContentsCall.data(), &QDBusPendingCallWatcher::finished,
- this, &QMirClientClipboard::onDBusClipboardSetContentsFinished);
+ mPasteReply = new QDBusPendingCallWatcher(reply, this);
+ connect(mPasteReply, &QDBusPendingCallWatcher::finished,
+ this, [this]() {
+ delete mMimeData;
+ mMimeData = mContentHub->paste(*mPasteReply);
+ mClipboardState = SyncedClipboard;
+ mPasteReply->deleteLater();
+ mPasteReply = nullptr;
+ emitChanged(QClipboard::Clipboard);
+ });
}
diff --git a/src/plugins/platforms/mirclient/qmirclientclipboard.h b/src/plugins/platforms/mirclient/qmirclientclipboard.h
index 1394498320..09e9bcdf38 100644
--- a/src/plugins/platforms/mirclient/qmirclientclipboard.h
+++ b/src/plugins/platforms/mirclient/qmirclientclipboard.h
@@ -45,7 +45,15 @@
#include <QMimeData>
#include <QPointer>
-class QDBusInterface;
+
+namespace com {
+ namespace ubuntu {
+ namespace content {
+ class Hub;
+ }
+ }
+}
+
class QDBusPendingCallWatcher;
class QMirClientClipboard : public QObject, public QPlatformClipboard
@@ -61,31 +69,24 @@ public:
bool supportsMode(QClipboard::Mode mode) const override;
bool ownsMode(QClipboard::Mode mode) const override;
- void requestDBusClipboardContents();
-
private Q_SLOTS:
- void onDBusClipboardGetContentsFinished(QDBusPendingCallWatcher*);
- void onDBusClipboardSetContentsFinished(QDBusPendingCallWatcher*);
- void updateMimeData(const QByteArray &serializedMimeData);
+ void onApplicationStateChanged(Qt::ApplicationState state);
private:
- void setupDBus();
-
- QByteArray serializeMimeData(QMimeData *mimeData) const;
- QMimeData *deserializeMimeData(const QByteArray &serializedMimeData) const;
-
- void setDBusClipboardContents(const QByteArray &clipboardContents);
+ void updateMimeData();
+ void requestMimeData();
QMimeData *mMimeData;
- bool mIsOutdated;
- QPointer<QDBusInterface> mDBusClipboard;
+ enum {
+ OutdatedClipboard, // Our mimeData is outdated, need to fetch latest from ContentHub
+ SyncingClipboard, // Our mimeData is outdated and we are waiting for ContentHub to reply with the latest paste
+ SyncedClipboard // Our mimeData is in sync with what ContentHub has
+ } mClipboardState{OutdatedClipboard};
- QPointer<QDBusPendingCallWatcher> mPendingGetContentsCall;
- QPointer<QDBusPendingCallWatcher> mPendingSetContentsCall;
+ com::ubuntu::content::Hub *mContentHub;
- bool mUpdatesDisabled;
- bool mDBusSetupDone;
+ QDBusPendingCallWatcher *mPasteReply{nullptr};
};
#endif // QMIRCLIENTCLIPBOARD_H
diff --git a/src/plugins/platforms/mirclient/qmirclientcursor.cpp b/src/plugins/platforms/mirclient/qmirclientcursor.cpp
index a0da3fdd77..812cde95c6 100644
--- a/src/plugins/platforms/mirclient/qmirclientcursor.cpp
+++ b/src/plugins/platforms/mirclient/qmirclientcursor.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2015 Canonical, Ltd.
+** Copyright (C) 2015-2016 Canonical, Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
@@ -45,35 +45,41 @@
#include <mir_toolkit/mir_client_library.h>
+Q_LOGGING_CATEGORY(mirclientCursor, "qt.qpa.mirclient.cursor", QtWarningMsg)
+
QMirClientCursor::QMirClientCursor(MirConnection *connection)
: mConnection(connection)
{
- mShapeToCursorName[Qt::ArrowCursor] = "left_ptr";
+ /*
+ * TODO: Add the missing cursors to Mir (LP: #1388987)
+ * Those are the ones without a mir_ prefix, which are X11 cursors
+ * and won't be understood by any shell other than Unity8.
+ */
+ mShapeToCursorName[Qt::ArrowCursor] = mir_arrow_cursor_name;
mShapeToCursorName[Qt::UpArrowCursor] = "up_arrow";
- mShapeToCursorName[Qt::CrossCursor] = "cross";
- mShapeToCursorName[Qt::WaitCursor] = "watch";
- mShapeToCursorName[Qt::IBeamCursor] = "xterm";
- mShapeToCursorName[Qt::SizeVerCursor] = "size_ver";
- mShapeToCursorName[Qt::SizeHorCursor] = "size_hor";
- mShapeToCursorName[Qt::SizeBDiagCursor] = "size_bdiag";
- mShapeToCursorName[Qt::SizeFDiagCursor] = "size_fdiag";
- mShapeToCursorName[Qt::SizeAllCursor] = "size_all";
- mShapeToCursorName[Qt::BlankCursor] = "blank";
- mShapeToCursorName[Qt::SplitVCursor] = "split_v";
- mShapeToCursorName[Qt::SplitHCursor] = "split_h";
- mShapeToCursorName[Qt::PointingHandCursor] = "hand";
+ mShapeToCursorName[Qt::CrossCursor] = mir_crosshair_cursor_name;
+ mShapeToCursorName[Qt::WaitCursor] = mir_busy_cursor_name;
+ mShapeToCursorName[Qt::IBeamCursor] = mir_caret_cursor_name;
+ mShapeToCursorName[Qt::SizeVerCursor] = mir_vertical_resize_cursor_name;
+ mShapeToCursorName[Qt::SizeHorCursor] = mir_horizontal_resize_cursor_name;
+ mShapeToCursorName[Qt::SizeBDiagCursor] = mir_diagonal_resize_bottom_to_top_cursor_name;
+ mShapeToCursorName[Qt::SizeFDiagCursor] = mir_diagonal_resize_top_to_bottom_cursor_name;
+ mShapeToCursorName[Qt::SizeAllCursor] = mir_omnidirectional_resize_cursor_name;
+ mShapeToCursorName[Qt::BlankCursor] = mir_disabled_cursor_name;
+ mShapeToCursorName[Qt::SplitVCursor] = mir_vsplit_resize_cursor_name;
+ mShapeToCursorName[Qt::SplitHCursor] = mir_hsplit_resize_cursor_name;
+ mShapeToCursorName[Qt::PointingHandCursor] = mir_pointing_hand_cursor_name;
mShapeToCursorName[Qt::ForbiddenCursor] = "forbidden";
mShapeToCursorName[Qt::WhatsThisCursor] = "whats_this";
mShapeToCursorName[Qt::BusyCursor] = "left_ptr_watch";
- mShapeToCursorName[Qt::OpenHandCursor] = "openhand";
- mShapeToCursorName[Qt::ClosedHandCursor] = "closedhand";
+ mShapeToCursorName[Qt::OpenHandCursor] = mir_open_hand_cursor_name;
+ mShapeToCursorName[Qt::ClosedHandCursor] = mir_closed_hand_cursor_name;
mShapeToCursorName[Qt::DragCopyCursor] = "dnd-copy";
mShapeToCursorName[Qt::DragMoveCursor] = "dnd-move";
mShapeToCursorName[Qt::DragLinkCursor] = "dnd-link";
}
namespace {
-#if !defined(QT_NO_DEBUG)
const char *qtCursorShapeToStr(Qt::CursorShape shape)
{
switch (shape) {
@@ -127,7 +133,6 @@ const char *qtCursorShapeToStr(Qt::CursorShape shape)
return "???";
}
}
-#endif // !defined(QT_NO_DEBUG)
} // anonymous namespace
void QMirClientCursor::changeCursor(QCursor *windowCursor, QWindow *window)
@@ -144,7 +149,7 @@ void QMirClientCursor::changeCursor(QCursor *windowCursor, QWindow *window)
if (windowCursor) {
- DLOG("[ubuntumirclient QPA] changeCursor shape=%s, window=%p\n", qtCursorShapeToStr(windowCursor->shape()), window);
+ qCDebug(mirclientCursor, "changeCursor shape=%s, window=%p", qtCursorShapeToStr(windowCursor->shape()), window);
if (!windowCursor->pixmap().isNull()) {
configureMirCursorWithPixmapQCursor(surface, *windowCursor);
} else if (windowCursor->shape() == Qt::BitmapCursor) {
diff --git a/src/plugins/platforms/mirclient/qmirclientcursor.h b/src/plugins/platforms/mirclient/qmirclientcursor.h
index 4ecc3d97ee..c5de23b272 100644
--- a/src/plugins/platforms/mirclient/qmirclientcursor.h
+++ b/src/plugins/platforms/mirclient/qmirclientcursor.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2015 Canonical, Ltd.
+** Copyright (C) 2015-2016 Canonical, Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
diff --git a/src/plugins/platforms/mirclient/qmirclientdebugextension.cpp b/src/plugins/platforms/mirclient/qmirclientdebugextension.cpp
new file mode 100644
index 0000000000..9aa934083d
--- /dev/null
+++ b/src/plugins/platforms/mirclient/qmirclientdebugextension.cpp
@@ -0,0 +1,79 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Canonical, Ltd.
+** 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 "qmirclientdebugextension.h"
+
+#include "qmirclientlogging.h"
+
+// mir client debug
+#include <mir_toolkit/debug/surface.h>
+
+Q_LOGGING_CATEGORY(mirclientDebug, "qt.qpa.mirclient.debug")
+
+QMirClientDebugExtension::QMirClientDebugExtension()
+ : m_mirclientDebug(QStringLiteral("mirclient-debug-extension"), 1)
+ , m_mapper(nullptr)
+{
+ qCDebug(mirclientDebug) << "NOTICE: Loading mirclient-debug-extension";
+ m_mapper = (MapperPrototype) m_mirclientDebug.resolve("mir_debug_surface_coords_to_screen");
+
+ if (!m_mirclientDebug.isLoaded()) {
+ qCWarning(mirclientDebug) << "ERROR: mirclient-debug-extension failed to load:"
+ << m_mirclientDebug.errorString();
+ } else if (!m_mapper) {
+ qCWarning(mirclientDebug) << "ERROR: unable to find required symbols in mirclient-debug-extension:"
+ << m_mirclientDebug.errorString();
+ }
+}
+
+QPoint QMirClientDebugExtension::mapSurfacePointToScreen(MirSurface *surface, const QPoint &point)
+{
+ if (!m_mapper) {
+ return point;
+ }
+
+ QPoint mappedPoint;
+ bool status = m_mapper(surface, point.x(), point.y(), &mappedPoint.rx(), &mappedPoint.ry());
+ if (status) {
+ return mappedPoint;
+ } else {
+ return point;
+ }
+}
diff --git a/src/plugins/platforms/mirclient/qmirclientdebugextension.h b/src/plugins/platforms/mirclient/qmirclientdebugextension.h
new file mode 100644
index 0000000000..0596561d77
--- /dev/null
+++ b/src/plugins/platforms/mirclient/qmirclientdebugextension.h
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Canonical, Ltd.
+** 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 QMIRCLIENTDEBUGEXTENSION_H
+#define QMIRCLIENTDEBUGEXTENSION_H
+
+#include <QPoint>
+#include <QLibrary>
+struct MirSurface;
+
+typedef bool (*MapperPrototype)(MirSurface* surface, int x, int y, int* screenX, int* screenY);
+
+
+class QMirClientDebugExtension
+{
+public:
+ QMirClientDebugExtension();
+
+ QPoint mapSurfacePointToScreen(MirSurface *, const QPoint &point);
+
+private:
+ QLibrary m_mirclientDebug;
+ MapperPrototype m_mapper;
+};
+
+#endif // QMIRCLIENTDEBUGEXTENSION_H
diff --git a/src/plugins/platforms/mirclient/qmirclientdesktopwindow.cpp b/src/plugins/platforms/mirclient/qmirclientdesktopwindow.cpp
new file mode 100644
index 0000000000..123f805c25
--- /dev/null
+++ b/src/plugins/platforms/mirclient/qmirclientdesktopwindow.cpp
@@ -0,0 +1,50 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Canonical, Ltd.
+** 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 "qmirclientdesktopwindow.h"
+
+// local
+#include "qmirclientlogging.h"
+
+QMirClientDesktopWindow::QMirClientDesktopWindow(QWindow *window)
+ : QPlatformWindow(window)
+{
+ qCDebug(mirclient, "QMirClientDesktopWindow(window=%p)", window);
+}
diff --git a/src/plugins/platforms/mirclient/qmirclientdesktopwindow.h b/src/plugins/platforms/mirclient/qmirclientdesktopwindow.h
new file mode 100644
index 0000000000..3ba54db826
--- /dev/null
+++ b/src/plugins/platforms/mirclient/qmirclientdesktopwindow.h
@@ -0,0 +1,53 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Canonical, Ltd.
+** 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 QMIRCLIENTDESKTOPWINDOW_H
+#define QMIRCLIENTDESKTOPWINDOW_H
+
+#include <qpa/qplatformwindow.h>
+
+// TODO Implement it. For now it's just an empty, dummy class.
+class QMirClientDesktopWindow : public QPlatformWindow
+{
+public:
+ QMirClientDesktopWindow(QWindow*);
+};
+
+#endif // QMIRCLIENTDESKTOPWINDOW_H
diff --git a/src/plugins/platforms/mirclient/qmirclientglcontext.cpp b/src/plugins/platforms/mirclient/qmirclientglcontext.cpp
index 38eb0a4609..fc7d90d5ec 100644
--- a/src/plugins/platforms/mirclient/qmirclientglcontext.cpp
+++ b/src/plugins/platforms/mirclient/qmirclientglcontext.cpp
@@ -39,123 +39,94 @@
#include "qmirclientglcontext.h"
-#include "qmirclientwindow.h"
#include "qmirclientlogging.h"
+#include "qmirclientwindow.h"
+
+#include <QOpenGLFramebufferObject>
#include <QtEglSupport/private/qeglconvenience_p.h>
+#include <QtEglSupport/private/qeglpbuffer_p.h>
#include <QtGui/private/qopenglcontext_p.h>
-#include <dlfcn.h>
-
-#if !defined(QT_NO_DEBUG)
-static void printOpenGLESConfig() {
- static bool once = true;
- if (once) {
- const char* string = (const char*) glGetString(GL_VENDOR);
- LOG("OpenGL ES vendor: %s", string);
- string = (const char*) glGetString(GL_RENDERER);
- LOG("OpenGL ES renderer: %s", string);
- string = (const char*) glGetString(GL_VERSION);
- LOG("OpenGL ES version: %s", string);
- string = (const char*) glGetString(GL_SHADING_LANGUAGE_VERSION);
- LOG("OpenGL ES Shading Language version: %s", string);
- string = (const char*) glGetString(GL_EXTENSIONS);
- LOG("OpenGL ES extensions: %s", string);
- once = false;
- }
-}
-#endif
-static EGLenum api_in_use()
+Q_LOGGING_CATEGORY(mirclientGraphics, "qt.qpa.mirclient.graphics", QtWarningMsg)
+
+namespace {
+
+void printEglConfig(EGLDisplay display, EGLConfig config)
{
-#ifdef QTUBUNTU_USE_OPENGL
- return EGL_OPENGL_API;
-#else
- return EGL_OPENGL_ES_API;
-#endif
+ Q_ASSERT(display != EGL_NO_DISPLAY);
+ Q_ASSERT(config != nullptr);
+
+ const char *string = eglQueryString(display, EGL_VENDOR);
+ qCDebug(mirclientGraphics, "EGL vendor: %s", string);
+
+ string = eglQueryString(display, EGL_VERSION);
+ qCDebug(mirclientGraphics, "EGL version: %s", string);
+
+ string = eglQueryString(display, EGL_EXTENSIONS);
+ qCDebug(mirclientGraphics, "EGL extensions: %s", string);
+
+ qCDebug(mirclientGraphics, "EGL configuration attributes:");
+ q_printEglConfig(display, config);
}
-QMirClientOpenGLContext::QMirClientOpenGLContext(QMirClientScreen* screen, QMirClientOpenGLContext* share)
+} // anonymous namespace
+
+QMirClientOpenGLContext::QMirClientOpenGLContext(const QSurfaceFormat &format, QPlatformOpenGLContext *share,
+ EGLDisplay display)
+ : QEGLPlatformContext(format, share, display, 0)
{
- ASSERT(screen != NULL);
- mEglDisplay = screen->eglDisplay();
- mScreen = screen;
-
- // Create an OpenGL ES 2 context.
- QVector<EGLint> attribs;
- attribs.append(EGL_CONTEXT_CLIENT_VERSION);
- attribs.append(2);
- attribs.append(EGL_NONE);
- ASSERT(eglBindAPI(api_in_use()) == EGL_TRUE);
-
- mEglContext = eglCreateContext(mEglDisplay, screen->eglConfig(), share ? share->eglContext() : EGL_NO_CONTEXT,
- attribs.constData());
- DASSERT(mEglContext != EGL_NO_CONTEXT);
+ if (mirclientGraphics().isDebugEnabled()) {
+ printEglConfig(display, eglConfig());
+ }
}
-QMirClientOpenGLContext::~QMirClientOpenGLContext()
+static bool needsFBOReadBackWorkaround()
{
- ASSERT(eglDestroyContext(mEglDisplay, mEglContext) == EGL_TRUE);
+ static bool set = false;
+ static bool needsWorkaround = false;
+
+ if (Q_UNLIKELY(!set)) {
+ const char *rendererString = reinterpret_cast<const char *>(glGetString(GL_RENDERER));
+ needsWorkaround = qstrncmp(rendererString, "Mali-400", 8) == 0
+ || qstrncmp(rendererString, "Mali-T7", 7) == 0
+ || qstrncmp(rendererString, "PowerVR Rogue G6200", 19) == 0;
+ set = true;
+ }
+
+ return needsWorkaround;
}
bool QMirClientOpenGLContext::makeCurrent(QPlatformSurface* surface)
{
- DASSERT(surface->surface()->surfaceType() == QSurface::OpenGLSurface);
- EGLSurface eglSurface = static_cast<QMirClientWindow*>(surface)->eglSurface();
-#if defined(QT_NO_DEBUG)
- eglBindAPI(api_in_use());
- eglMakeCurrent(mEglDisplay, eglSurface, eglSurface, mEglContext);
-#else
- ASSERT(eglBindAPI(api_in_use()) == EGL_TRUE);
- ASSERT(eglMakeCurrent(mEglDisplay, eglSurface, eglSurface, mEglContext) == EGL_TRUE);
- printOpenGLESConfig();
-#endif
-
- // When running on the emulator, shaders will be compiled using a thin wrapper around the desktop drivers.
- // These wrappers might not support the precision qualifiers, so set the workaround flag to true.
- const char *rendererString = reinterpret_cast<const char *>(glGetString(GL_RENDERER));
- if (rendererString != 0 && qstrncmp(rendererString, "Android Emulator", 16) == 0) {
+ const bool ret = QEGLPlatformContext::makeCurrent(surface);
+
+ if (Q_LIKELY(ret)) {
QOpenGLContextPrivate *ctx_d = QOpenGLContextPrivate::get(context());
- ctx_d->workaround_missingPrecisionQualifiers = true;
+ if (!ctx_d->workaround_brokenFBOReadBack && needsFBOReadBackWorkaround()) {
+ ctx_d->workaround_brokenFBOReadBack = true;
+ }
}
-
- return true;
+ return ret;
}
-void QMirClientOpenGLContext::doneCurrent()
+// Following method used internally in the base class QEGLPlatformContext to access
+// the egl surface of a QPlatformSurface/QMirClientWindow
+EGLSurface QMirClientOpenGLContext::eglSurfaceForPlatformSurface(QPlatformSurface *surface)
{
-#if defined(QT_NO_DEBUG)
- eglBindAPI(api_in_use());
- eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
-#else
- ASSERT(eglBindAPI(api_in_use()) == EGL_TRUE);
- ASSERT(eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT) == EGL_TRUE);
-#endif
+ if (surface->surface()->surfaceClass() == QSurface::Window) {
+ return static_cast<QMirClientWindow *>(surface)->eglSurface();
+ } else {
+ return static_cast<QEGLPbuffer *>(surface)->pbuffer();
+ }
}
void QMirClientOpenGLContext::swapBuffers(QPlatformSurface* surface)
{
- QMirClientWindow *ubuntuWindow = static_cast<QMirClientWindow*>(surface);
-
- EGLSurface eglSurface = ubuntuWindow->eglSurface();
-#if defined(QT_NO_DEBUG)
- eglBindAPI(api_in_use());
- eglSwapBuffers(mEglDisplay, eglSurface);
-#else
- ASSERT(eglBindAPI(api_in_use()) == EGL_TRUE);
- ASSERT(eglSwapBuffers(mEglDisplay, eglSurface) == EGL_TRUE);
-#endif
-
- ubuntuWindow->onSwapBuffersDone();
-}
+ QEGLPlatformContext::swapBuffers(surface);
-QFunctionPointer QMirClientOpenGLContext::getProcAddress(const char *procName)
-{
-#if defined(QT_NO_DEBUG)
- eglBindAPI(api_in_use());
-#else
- ASSERT(eglBindAPI(api_in_use()) == EGL_TRUE);
-#endif
- QFunctionPointer proc = (QFunctionPointer) eglGetProcAddress(procName);
- if (!proc)
- proc = (QFunctionPointer) dlsym(RTLD_DEFAULT, procName);
- return proc;
+ if (surface->surface()->surfaceClass() == QSurface::Window) {
+ // notify window on swap completion
+ auto platformWindow = static_cast<QMirClientWindow *>(surface);
+ platformWindow->onSwapBuffersDone();
+ }
}
diff --git a/src/plugins/platforms/mirclient/qmirclientglcontext.h b/src/plugins/platforms/mirclient/qmirclientglcontext.h
index eac0b78c4e..92331a6fb1 100644
--- a/src/plugins/platforms/mirclient/qmirclientglcontext.h
+++ b/src/plugins/platforms/mirclient/qmirclientglcontext.h
@@ -42,28 +42,22 @@
#define QMIRCLIENTGLCONTEXT_H
#include <qpa/qplatformopenglcontext.h>
-#include "qmirclientscreen.h"
+#include <QtEglSupport/private/qeglplatformcontext_p.h>
-class QMirClientOpenGLContext : public QPlatformOpenGLContext
+#include <EGL/egl.h>
+
+class QMirClientOpenGLContext : public QEGLPlatformContext
{
public:
- QMirClientOpenGLContext(QMirClientScreen* screen, QMirClientOpenGLContext* share);
- virtual ~QMirClientOpenGLContext();
-
- // QPlatformOpenGLContext methods.
- QSurfaceFormat format() const override { return mScreen->surfaceFormat(); }
- void swapBuffers(QPlatformSurface* surface) override;
- bool makeCurrent(QPlatformSurface* surface) override;
- void doneCurrent() override;
- bool isValid() const override { return mEglContext != EGL_NO_CONTEXT; }
- QFunctionPointer getProcAddress(const char *procName) override;
+ QMirClientOpenGLContext(const QSurfaceFormat &format, QPlatformOpenGLContext *share,
+ EGLDisplay display);
- EGLContext eglContext() const { return mEglContext; }
+ // QEGLPlatformContext methods.
+ void swapBuffers(QPlatformSurface *surface) final;
+ bool makeCurrent(QPlatformSurface *surface) final;
-private:
- QMirClientScreen* mScreen;
- EGLContext mEglContext;
- EGLDisplay mEglDisplay;
+protected:
+ EGLSurface eglSurfaceForPlatformSurface(QPlatformSurface *surface) final;
};
#endif // QMIRCLIENTGLCONTEXT_H
diff --git a/src/plugins/platforms/mirclient/qmirclientinput.cpp b/src/plugins/platforms/mirclient/qmirclientinput.cpp
index b3b21ae0e3..ea13f3cc17 100644
--- a/src/plugins/platforms/mirclient/qmirclientinput.cpp
+++ b/src/plugins/platforms/mirclient/qmirclientinput.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2014-2015 Canonical, Ltd.
+** Copyright (C) 2014-2016 Canonical, Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
@@ -48,21 +48,23 @@
#include "qmirclientorientationchangeevent_p.h"
// Qt
-#if !defined(QT_NO_DEBUG)
#include <QtCore/QThread>
-#endif
#include <QtCore/qglobal.h>
#include <QtCore/QCoreApplication>
-#include <private/qguiapplication_p.h>
+#include <QtGui/private/qguiapplication_p.h>
#include <qpa/qplatforminputcontext.h>
#include <qpa/qwindowsysteminterface.h>
+#include <QTextCodec>
#include <xkbcommon/xkbcommon.h>
#include <xkbcommon/xkbcommon-keysyms.h>
#include <mir_toolkit/mir_client_library.h>
-#define LOG_EVENTS 0
+Q_LOGGING_CATEGORY(mirclientInput, "qt.qpa.mirclient.input", QtWarningMsg)
+
+namespace
+{
// XKB Keysyms which do not map directly to Qt types (i.e. Unicode points)
static const uint32_t KeyTable[] = {
@@ -134,6 +136,27 @@ static const uint32_t KeyTable[] = {
XKB_KEY_MultipleCandidate, Qt::Key_MultipleCandidate,
XKB_KEY_PreviousCandidate, Qt::Key_PreviousCandidate,
+ // dead keys
+ XKB_KEY_dead_grave, Qt::Key_Dead_Grave,
+ XKB_KEY_dead_acute, Qt::Key_Dead_Acute,
+ XKB_KEY_dead_circumflex, Qt::Key_Dead_Circumflex,
+ XKB_KEY_dead_tilde, Qt::Key_Dead_Tilde,
+ XKB_KEY_dead_macron, Qt::Key_Dead_Macron,
+ XKB_KEY_dead_breve, Qt::Key_Dead_Breve,
+ XKB_KEY_dead_abovedot, Qt::Key_Dead_Abovedot,
+ XKB_KEY_dead_diaeresis, Qt::Key_Dead_Diaeresis,
+ XKB_KEY_dead_abovering, Qt::Key_Dead_Abovering,
+ XKB_KEY_dead_doubleacute, Qt::Key_Dead_Doubleacute,
+ XKB_KEY_dead_caron, Qt::Key_Dead_Caron,
+ XKB_KEY_dead_cedilla, Qt::Key_Dead_Cedilla,
+ XKB_KEY_dead_ogonek, Qt::Key_Dead_Ogonek,
+ XKB_KEY_dead_iota, Qt::Key_Dead_Iota,
+ XKB_KEY_dead_voiced_sound, Qt::Key_Dead_Voiced_Sound,
+ XKB_KEY_dead_semivoiced_sound, Qt::Key_Dead_Semivoiced_Sound,
+ XKB_KEY_dead_belowdot, Qt::Key_Dead_Belowdot,
+ XKB_KEY_dead_hook, Qt::Key_Dead_Hook,
+ XKB_KEY_dead_horn, Qt::Key_Dead_Horn,
+
XKB_KEY_Mode_switch, Qt::Key_Mode_switch,
XKB_KEY_script_switch, Qt::Key_Mode_switch,
XKB_KEY_XF86AudioRaiseVolume, Qt::Key_VolumeUp,
@@ -144,14 +167,37 @@ static const uint32_t KeyTable[] = {
0, 0
};
-class QMirClientEvent : public QEvent
+Qt::WindowState mirSurfaceStateToWindowState(MirSurfaceState state)
+{
+ switch (state) {
+ case mir_surface_state_fullscreen:
+ return Qt::WindowFullScreen;
+ case mir_surface_state_maximized:
+ case mir_surface_state_vertmaximized:
+ case mir_surface_state_horizmaximized:
+ return Qt::WindowMaximized;
+ case mir_surface_state_minimized:
+ return Qt::WindowMinimized;
+ case mir_surface_state_hidden:
+ // We should be handling this state separately.
+ Q_ASSERT(false);
+ case mir_surface_state_restored:
+ case mir_surface_state_unknown:
+ default:
+ return Qt::WindowNoState;
+ }
+}
+
+} // namespace
+
+class UbuntuEvent : public QEvent
{
public:
- QMirClientEvent(QMirClientWindow* window, const MirEvent *event, QEvent::Type type)
+ UbuntuEvent(QMirClientWindow* window, const MirEvent *event, QEvent::Type type)
: QEvent(type), window(window) {
nativeEvent = mir_event_ref(event);
}
- ~QMirClientEvent()
+ ~UbuntuEvent()
{
mir_event_unref(nativeEvent);
}
@@ -166,7 +212,7 @@ QMirClientInput::QMirClientInput(QMirClientClientIntegration* integration)
, mEventFilterType(static_cast<QMirClientNativeInterface*>(
integration->nativeInterface())->genericEventFilterType())
, mEventType(static_cast<QEvent::Type>(QEvent::registerEventType()))
- , mLastFocusedWindow(nullptr)
+ , mLastInputWindow(nullptr)
{
// Initialize touch device.
mTouchDevice = new QTouchDevice;
@@ -182,42 +228,47 @@ QMirClientInput::~QMirClientInput()
// Qt will take care of deleting mTouchDevice.
}
-#if (LOG_EVENTS != 0)
static const char* nativeEventTypeToStr(MirEventType t)
{
switch (t)
{
case mir_event_type_key:
- return "mir_event_type_key";
+ return "key";
case mir_event_type_motion:
- return "mir_event_type_motion";
+ return "motion";
case mir_event_type_surface:
- return "mir_event_type_surface";
+ return "surface";
case mir_event_type_resize:
- return "mir_event_type_resize";
+ return "resize";
case mir_event_type_prompt_session_state_change:
- return "mir_event_type_prompt_session_state_change";
+ return "prompt_session_state_change";
case mir_event_type_orientation:
- return "mir_event_type_orientation";
+ return "orientation";
case mir_event_type_close_surface:
- return "mir_event_type_close_surface";
+ return "close_surface";
case mir_event_type_input:
- return "mir_event_type_input";
+ return "input";
+ case mir_event_type_keymap:
+ return "keymap";
+ case mir_event_type_input_configuration:
+ return "input_configuration";
+ case mir_event_type_surface_output:
+ return "surface_output";
+ case mir_event_type_input_device_state:
+ return "input_device_state";
default:
- DLOG("Invalid event type %d", t);
- return "invalid";
+ return "unknown";
}
}
-#endif // LOG_EVENTS != 0
void QMirClientInput::customEvent(QEvent* event)
{
- DASSERT(QThread::currentThread() == thread());
- QMirClientEvent* ubuntuEvent = static_cast<QMirClientEvent*>(event);
+ Q_ASSERT(QThread::currentThread() == thread());
+ UbuntuEvent* ubuntuEvent = static_cast<UbuntuEvent*>(event);
const MirEvent *nativeEvent = ubuntuEvent->nativeEvent;
if ((ubuntuEvent->window == nullptr) || (ubuntuEvent->window->window() == nullptr)) {
- qWarning("Attempted to deliver an event to a non-existent window, ignoring.");
+ qCWarning(mirclient) << "Attempted to deliver an event to a non-existent window, ignoring.";
return;
}
@@ -226,13 +277,11 @@ void QMirClientInput::customEvent(QEvent* event)
if (QWindowSystemInterface::handleNativeEvent(
ubuntuEvent->window->window(), mEventFilterType,
const_cast<void *>(static_cast<const void *>(nativeEvent)), &result) == true) {
- DLOG("event filtered out by native interface");
+ qCDebug(mirclient, "event filtered out by native interface");
return;
}
- #if (LOG_EVENTS != 0)
- LOG("QMirClientInput::customEvent(type=%s)", nativeEventTypeToStr(mir_event_get_type(nativeEvent)));
- #endif
+ qCDebug(mirclientInput, "customEvent(type=%s)", nativeEventTypeToStr(mir_event_get_type(nativeEvent)));
// Event dispatching.
switch (mir_event_get_type(nativeEvent))
@@ -242,43 +291,30 @@ void QMirClientInput::customEvent(QEvent* event)
break;
case mir_event_type_resize:
{
- Q_ASSERT(ubuntuEvent->window->screen() == mIntegration->screen());
-
auto resizeEvent = mir_event_get_resize_event(nativeEvent);
- mIntegration->screen()->handleWindowSurfaceResize(
- mir_resize_event_get_width(resizeEvent),
- mir_resize_event_get_height(resizeEvent));
+ // Enable workaround for Screen rotation
+ auto const targetWindow = ubuntuEvent->window;
+ if (targetWindow) {
+ auto const screen = static_cast<QMirClientScreen*>(targetWindow->screen());
+ if (screen) {
+ screen->handleWindowSurfaceResize(
+ mir_resize_event_get_width(resizeEvent),
+ mir_resize_event_get_height(resizeEvent));
+ }
- ubuntuEvent->window->handleSurfaceResized(mir_resize_event_get_width(resizeEvent),
- mir_resize_event_get_height(resizeEvent));
+ targetWindow->handleSurfaceResized(
+ mir_resize_event_get_width(resizeEvent),
+ mir_resize_event_get_height(resizeEvent));
+ }
break;
}
case mir_event_type_surface:
- {
- auto surfaceEvent = mir_event_get_surface_event(nativeEvent);
- if (mir_surface_event_get_attribute(surfaceEvent) == mir_surface_attrib_focus) {
- const bool focused = mir_surface_event_get_attribute_value(surfaceEvent) == mir_surface_focused;
- // Mir may have sent a pair of focus lost/gained events, so we need to "peek" into the queue
- // so that we don't deactivate windows prematurely.
- if (focused) {
- mPendingFocusGainedEvents--;
- ubuntuEvent->window->handleSurfaceFocused();
- QWindowSystemInterface::handleWindowActivated(ubuntuEvent->window->window(), Qt::ActiveWindowFocusReason);
-
- // NB: Since processing of system events is queued, never check qGuiApp->applicationState()
- // as it might be outdated. Always call handleApplicationStateChanged() with the latest
- // state regardless.
- QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationActive);
-
- } else if (!mPendingFocusGainedEvents) {
- DLOG("[ubuntumirclient QPA] No windows have focus");
- QWindowSystemInterface::handleWindowActivated(nullptr, Qt::ActiveWindowFocusReason);
- QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationInactive);
- }
- }
+ handleSurfaceEvent(ubuntuEvent->window, mir_event_get_surface_event(nativeEvent));
+ break;
+ case mir_event_type_surface_output:
+ handleSurfaceOutputEvent(ubuntuEvent->window, mir_event_get_surface_output_event(nativeEvent));
break;
- }
case mir_event_type_orientation:
dispatchOrientationEvent(ubuntuEvent->window->window(), mir_event_get_orientation_event(nativeEvent));
break;
@@ -286,7 +322,7 @@ void QMirClientInput::customEvent(QEvent* event)
QWindowSystemInterface::handleCloseEvent(ubuntuEvent->window->window());
break;
default:
- DLOG("unhandled event type: %d", static_cast<int>(mir_event_get_type(nativeEvent)));
+ qCDebug(mirclient, "unhandled event type: %d", static_cast<int>(mir_event_get_type(nativeEvent)));
}
}
@@ -294,22 +330,11 @@ void QMirClientInput::postEvent(QMirClientWindow *platformWindow, const MirEvent
{
QWindow *window = platformWindow->window();
- const auto eventType = mir_event_get_type(event);
- if (mir_event_type_surface == eventType) {
- auto surfaceEvent = mir_event_get_surface_event(event);
- if (mir_surface_attrib_focus == mir_surface_event_get_attribute(surfaceEvent)) {
- const bool focused = mir_surface_event_get_attribute_value(surfaceEvent) == mir_surface_focused;
- if (focused) {
- mPendingFocusGainedEvents++;
- }
- }
- }
-
- QCoreApplication::postEvent(this, new QMirClientEvent(
+ QCoreApplication::postEvent(this, new UbuntuEvent(
platformWindow, event, mEventType));
if ((window->flags().testFlag(Qt::WindowTransparentForInput)) && window->parent()) {
- QCoreApplication::postEvent(this, new QMirClientEvent(
+ QCoreApplication::postEvent(this, new UbuntuEvent(
static_cast<QMirClientWindow*>(platformWindow->QPlatformWindow::parent()),
event, mEventType));
}
@@ -365,15 +390,17 @@ void QMirClientInput::dispatchTouchEvent(QMirClientWindow *window, const MirInpu
switch (touch_action)
{
case mir_touch_action_down:
- mLastFocusedWindow = window;
+ mLastInputWindow = window;
touchPoint.state = Qt::TouchPointPressed;
break;
case mir_touch_action_up:
touchPoint.state = Qt::TouchPointReleased;
break;
case mir_touch_action_change:
- default:
touchPoint.state = Qt::TouchPointMoved;
+ break;
+ default:
+ Q_UNREACHABLE();
}
touchPoints.append(touchPoint);
@@ -384,22 +411,26 @@ void QMirClientInput::dispatchTouchEvent(QMirClientWindow *window, const MirInpu
mTouchDevice, touchPoints);
}
-static uint32_t translateKeysym(uint32_t sym, char *string, size_t size)
-{
- Q_UNUSED(size);
- string[0] = '\0';
+static uint32_t translateKeysym(uint32_t sym, const QString &text) {
+ int code = 0;
- if (sym >= XKB_KEY_F1 && sym <= XKB_KEY_F35)
+ QTextCodec *systemCodec = QTextCodec::codecForLocale();
+ if (sym < 128 || (sym < 256 && systemCodec->mibEnum() == 4)) {
+ // upper-case key, if known
+ code = isprint((int)sym) ? toupper((int)sym) : 0;
+ } else if (sym >= XKB_KEY_F1 && sym <= XKB_KEY_F35) {
return Qt::Key_F1 + (int(sym) - XKB_KEY_F1);
-
- for (int i = 0; KeyTable[i]; i += 2) {
- if (sym == KeyTable[i])
- return KeyTable[i + 1];
+ } else if (text.length() == 1 && text.unicode()->unicode() > 0x1f
+ && text.unicode()->unicode() != 0x7f
+ && !(sym >= XKB_KEY_dead_grave && sym <= XKB_KEY_dead_currency)) {
+ code = text.unicode()->toUpper().unicode();
+ } else {
+ for (int i = 0; KeyTable[i]; i += 2)
+ if (sym == KeyTable[i])
+ code = KeyTable[i + 1];
}
- string[0] = sym;
- string[1] = '\0';
- return toupper(sym);
+ return code;
}
namespace
@@ -413,12 +444,15 @@ Qt::KeyboardModifiers qt_modifiers_from_mir(MirInputEventModifiers modifiers)
if (modifiers & mir_input_event_modifier_ctrl) {
q_modifiers |= Qt::ControlModifier;
}
- if (modifiers & mir_input_event_modifier_alt) {
+ if (modifiers & mir_input_event_modifier_alt_left) {
q_modifiers |= Qt::AltModifier;
}
if (modifiers & mir_input_event_modifier_meta) {
q_modifiers |= Qt::MetaModifier;
}
+ if (modifiers & mir_input_event_modifier_alt_right) {
+ q_modifiers |= Qt::GroupSwitchModifier;
+ }
return q_modifiers;
}
}
@@ -429,34 +463,43 @@ void QMirClientInput::dispatchKeyEvent(QMirClientWindow *window, const MirInputE
ulong timestamp = mir_input_event_get_event_time(event) / 1000000;
xkb_keysym_t xk_sym = mir_keyboard_event_key_code(key_event);
+ quint32 scan_code = mir_keyboard_event_scan_code(key_event);
+ quint32 native_modifiers = mir_keyboard_event_modifiers(key_event);
// Key modifier and unicode index mapping.
- auto modifiers = qt_modifiers_from_mir(mir_keyboard_event_modifiers(key_event));
+ auto modifiers = qt_modifiers_from_mir(native_modifiers);
MirKeyboardAction action = mir_keyboard_event_action(key_event);
QEvent::Type keyType = action == mir_keyboard_action_up
? QEvent::KeyRelease : QEvent::KeyPress;
if (action == mir_keyboard_action_down)
- mLastFocusedWindow = window;
+ mLastInputWindow = window;
- char s[2];
- int sym = translateKeysym(xk_sym, s, sizeof(s));
- QString text = QString::fromLatin1(s);
+ QString text;
+ QVarLengthArray<char, 32> chars(32);
+ {
+ int result = xkb_keysym_to_utf8(xk_sym, chars.data(), chars.size());
+
+ if (result > 0) {
+ text = QString::fromUtf8(chars.constData());
+ }
+ }
+ int sym = translateKeysym(xk_sym, text);
bool is_auto_rep = action == mir_keyboard_action_repeat;
QPlatformInputContext *context = QGuiApplicationPrivate::platformIntegration()->inputContext();
if (context) {
- QKeyEvent qKeyEvent(keyType, sym, modifiers, text, is_auto_rep);
+ QKeyEvent qKeyEvent(keyType, sym, modifiers, scan_code, xk_sym, native_modifiers, text, is_auto_rep);
qKeyEvent.setTimestamp(timestamp);
if (context->filterEvent(&qKeyEvent)) {
- DLOG("key event filtered out by input context");
+ qCDebug(mirclient, "key event filtered out by input context");
return;
}
}
- QWindowSystemInterface::handleKeyEvent(window->window(), timestamp, keyType, sym, modifiers, text, is_auto_rep);
+ QWindowSystemInterface::handleExtendedKeyEvent(window->window(), timestamp, keyType, sym, modifiers, scan_code, xk_sym, native_modifiers, text, is_auto_rep);
}
namespace
@@ -481,14 +524,17 @@ Qt::MouseButtons extract_buttons(const MirPointerEvent *pev)
void QMirClientInput::dispatchPointerEvent(QMirClientWindow *platformWindow, const MirInputEvent *ev)
{
- auto window = platformWindow->window();
- auto timestamp = mir_input_event_get_event_time(ev) / 1000000;
+ const auto window = platformWindow->window();
+ const auto timestamp = mir_input_event_get_event_time(ev) / 1000000;
+
+ const auto pev = mir_input_event_get_pointer_event(ev);
+ const auto action = mir_pointer_event_action(pev);
+
+ const auto modifiers = qt_modifiers_from_mir(mir_pointer_event_modifiers(pev));
+ const auto localPoint = QPointF(mir_pointer_event_axis_value(pev, mir_pointer_axis_x),
+ mir_pointer_event_axis_value(pev, mir_pointer_axis_y));
- auto pev = mir_input_event_get_pointer_event(ev);
- auto action = mir_pointer_event_action(pev);
- auto localPoint = QPointF(mir_pointer_event_axis_value(pev, mir_pointer_axis_x),
- mir_pointer_event_axis_value(pev, mir_pointer_axis_y));
- auto modifiers = qt_modifiers_from_mir(mir_pointer_event_modifiers(pev));
+ mLastInputWindow = platformWindow;
switch (action) {
case mir_pointer_action_button_up:
@@ -499,7 +545,8 @@ void QMirClientInput::dispatchPointerEvent(QMirClientWindow *platformWindow, con
const float vDelta = mir_pointer_event_axis_value(pev, mir_pointer_axis_vscroll);
if (hDelta != 0 || vDelta != 0) {
- const QPoint angleDelta = QPoint(hDelta * 15, vDelta * 15);
+ // QWheelEvent::DefaultDeltasPerStep = 120 but doesn't exist on vivid
+ const QPoint angleDelta(120 * hDelta, 120 * vDelta);
QWindowSystemInterface::handleWheelEvent(window, timestamp, localPoint, window->position() + localPoint,
QPoint(), angleDelta, modifiers, Qt::ScrollUpdate);
}
@@ -515,42 +562,32 @@ void QMirClientInput::dispatchPointerEvent(QMirClientWindow *platformWindow, con
QWindowSystemInterface::handleLeaveEvent(window);
break;
default:
- DLOG("Unrecognized pointer event");
+ Q_UNREACHABLE();
}
}
-#if (LOG_EVENTS != 0)
static const char* nativeOrientationDirectionToStr(MirOrientation orientation)
{
switch (orientation) {
case mir_orientation_normal:
return "Normal";
- break;
case mir_orientation_left:
return "Left";
- break;
case mir_orientation_inverted:
return "Inverted";
- break;
case mir_orientation_right:
return "Right";
- break;
- default:
- return "INVALID!";
}
+ Q_UNREACHABLE();
}
-#endif
void QMirClientInput::dispatchOrientationEvent(QWindow *window, const MirOrientationEvent *event)
{
MirOrientation mir_orientation = mir_orientation_event_get_direction(event);
- #if (LOG_EVENTS != 0)
- // Orientation event logging.
- LOG("ORIENTATION direction: %s", nativeOrientationDirectionToStr(mir_orientation));
- #endif
+ qCDebug(mirclientInput, "orientation direction: %s", nativeOrientationDirectionToStr(mir_orientation));
if (!window->screen()) {
- DLOG("Window has no associated screen, dropping orientation event");
+ qCDebug(mirclient, "Window has no associated screen, dropping orientation event");
return;
}
@@ -569,7 +606,7 @@ void QMirClientInput::dispatchOrientationEvent(QWindow *window, const MirOrienta
orientation = OrientationChangeEvent::RightUp;
break;
default:
- DLOG("No such orientation %d", mir_orientation);
+ qCDebug(mirclient, "No such orientation %d", mir_orientation);
return;
}
@@ -581,3 +618,61 @@ void QMirClientInput::dispatchOrientationEvent(QWindow *window, const MirOrienta
new OrientationChangeEvent(OrientationChangeEvent::mType, orientation));
}
+void QMirClientInput::handleSurfaceEvent(const QPointer<QMirClientWindow> &window, const MirSurfaceEvent *event)
+{
+ auto surfaceEventAttribute = mir_surface_event_get_attribute(event);
+
+ switch (surfaceEventAttribute) {
+ case mir_surface_attrib_focus: {
+ window->handleSurfaceFocusChanged(
+ mir_surface_event_get_attribute_value(event) == mir_surface_focused);
+ break;
+ }
+ case mir_surface_attrib_visibility: {
+ window->handleSurfaceExposeChange(
+ mir_surface_event_get_attribute_value(event) == mir_surface_visibility_exposed);
+ break;
+ }
+ // Remaining attributes are ones client sets for server, and server should not override them
+ case mir_surface_attrib_state: {
+ MirSurfaceState state = static_cast<MirSurfaceState>(mir_surface_event_get_attribute_value(event));
+
+ if (state == mir_surface_state_hidden) {
+ window->handleSurfaceVisibilityChanged(false);
+ } else {
+ // it's visible!
+ window->handleSurfaceVisibilityChanged(true);
+ window->handleSurfaceStateChanged(mirSurfaceStateToWindowState(state));
+ }
+ break;
+ }
+ case mir_surface_attrib_type:
+ case mir_surface_attrib_swapinterval:
+ case mir_surface_attrib_dpi:
+ case mir_surface_attrib_preferred_orientation:
+ case mir_surface_attribs:
+ break;
+ }
+}
+
+void QMirClientInput::handleSurfaceOutputEvent(const QPointer<QMirClientWindow> &window, const MirSurfaceOutputEvent *event)
+{
+ const uint32_t outputId = mir_surface_output_event_get_output_id(event);
+ const int dpi = mir_surface_output_event_get_dpi(event);
+ const MirFormFactor formFactor = mir_surface_output_event_get_form_factor(event);
+ const float scale = mir_surface_output_event_get_scale(event);
+
+ const auto screenObserver = mIntegration->screenObserver();
+ QMirClientScreen *screen = screenObserver->findScreenWithId(outputId);
+ if (!screen) {
+ qCWarning(mirclient) << "Mir notified window" << window->window() << "on an unknown screen with id" << outputId;
+ return;
+ }
+
+ screenObserver->handleScreenPropertiesChange(screen, dpi, formFactor, scale);
+ window->handleScreenPropertiesChange(formFactor, scale);
+
+ if (window->screen() != screen) {
+ QWindowSystemInterface::handleWindowScreenChanged(window->window(), screen->screen());
+ }
+}
diff --git a/src/plugins/platforms/mirclient/qmirclientinput.h b/src/plugins/platforms/mirclient/qmirclientinput.h
index 3600ae7ece..263cb5e54e 100644
--- a/src/plugins/platforms/mirclient/qmirclientinput.h
+++ b/src/plugins/platforms/mirclient/qmirclientinput.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2014-2015 Canonical, Ltd.
+** Copyright (C) 2014-2016 Canonical, Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
@@ -43,7 +43,6 @@
// Qt
#include <qpa/qwindowsysteminterface.h>
-#include <QAtomicInt>
#include <mir_toolkit/mir_client_library.h>
@@ -63,7 +62,7 @@ public:
void postEvent(QMirClientWindow* window, const MirEvent *event);
QMirClientClientIntegration* integration() const { return mIntegration; }
- QMirClientWindow *lastFocusedWindow() const {return mLastFocusedWindow; }
+ QMirClientWindow *lastInputWindow() const {return mLastInputWindow; }
protected:
void dispatchKeyEvent(QMirClientWindow *window, const MirInputEvent *event);
@@ -72,6 +71,8 @@ protected:
void dispatchInputEvent(QMirClientWindow *window, const MirInputEvent *event);
void dispatchOrientationEvent(QWindow* window, const MirOrientationEvent *event);
+ void handleSurfaceEvent(const QPointer<QMirClientWindow> &window, const MirSurfaceEvent *event);
+ void handleSurfaceOutputEvent(const QPointer<QMirClientWindow> &window, const MirSurfaceOutputEvent *event);
private:
QMirClientClientIntegration* mIntegration;
@@ -79,8 +80,7 @@ private:
const QByteArray mEventFilterType;
const QEvent::Type mEventType;
- QMirClientWindow *mLastFocusedWindow;
- QAtomicInt mPendingFocusGainedEvents;
+ QMirClientWindow *mLastInputWindow;
};
#endif // QMIRCLIENTINPUT_H
diff --git a/src/plugins/platforms/mirclient/qmirclientintegration.cpp b/src/plugins/platforms/mirclient/qmirclientintegration.cpp
index 2c8740f070..eef96ee3de 100644
--- a/src/plugins/platforms/mirclient/qmirclientintegration.cpp
+++ b/src/plugins/platforms/mirclient/qmirclientintegration.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2014-2015 Canonical, Ltd.
+** Copyright (C) 2014-2016 Canonical, Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
@@ -42,6 +42,8 @@
#include "qmirclientintegration.h"
#include "qmirclientbackingstore.h"
#include "qmirclientclipboard.h"
+#include "qmirclientdebugextension.h"
+#include "qmirclientdesktopwindow.h"
#include "qmirclientglcontext.h"
#include "qmirclientinput.h"
#include "qmirclientlogging.h"
@@ -51,56 +53,62 @@
#include "qmirclientwindow.h"
// Qt
+#include <QFileInfo>
#include <QGuiApplication>
-#include <private/qguiapplication_p.h>
#include <qpa/qplatformnativeinterface.h>
#include <qpa/qplatforminputcontextfactory_p.h>
#include <qpa/qplatforminputcontext.h>
+#include <QtEglSupport/private/qeglconvenience_p.h>
+#include <QtEglSupport/private/qeglpbuffer_p.h>
#include <QtFontDatabaseSupport/private/qgenericunixfontdatabase_p.h>
#include <QtEventDispatcherSupport/private/qgenericunixeventdispatcher_p.h>
+#ifndef QT_NO_ACCESSIBILITY
+#include <qpa/qplatformaccessibility.h>
+#ifndef QT_NO_ACCESSIBILITY_ATSPI_BRIDGE
+#include <QtLinuxAccessibilitySupport/private/bridge_p.h>
+#endif
+#endif
+
#include <QOpenGLContext>
+#include <QOffscreenSurface>
// platform-api
#include <ubuntu/application/lifecycle_delegate.h>
#include <ubuntu/application/id.h>
#include <ubuntu/application/options.h>
-static void resumedCallback(const UApplicationOptions *options, void* context)
+static void resumedCallback(const UApplicationOptions */*options*/, void* context)
{
- Q_UNUSED(options)
- Q_UNUSED(context)
- DASSERT(context != NULL);
- if (qGuiApp->focusWindow()) {
- QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationActive);
- } else {
- QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationInactive);
- }
+ auto integration = static_cast<QMirClientClientIntegration*>(context);
+ integration->appStateController()->setResumed();
}
-static void aboutToStopCallback(UApplicationArchive *archive, void* context)
+static void aboutToStopCallback(UApplicationArchive */*archive*/, void* context)
{
- Q_UNUSED(archive)
- DASSERT(context != NULL);
- QMirClientClientIntegration* integration = static_cast<QMirClientClientIntegration*>(context);
- QPlatformInputContext *inputContext = integration->inputContext();
+ auto integration = static_cast<QMirClientClientIntegration*>(context);
+ auto inputContext = integration->inputContext();
if (inputContext) {
inputContext->hideInputPanel();
} else {
- qWarning("QMirClientClientIntegration aboutToStopCallback(): no input context");
+ qCWarning(mirclient) << "aboutToStopCallback(): no input context";
}
- QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationSuspended);
+ integration->appStateController()->setSuspended();
}
-QMirClientClientIntegration::QMirClientClientIntegration()
+QMirClientClientIntegration::QMirClientClientIntegration(int argc, char **argv)
: QPlatformIntegration()
- , mNativeInterface(new QMirClientNativeInterface)
+ , mNativeInterface(new QMirClientNativeInterface(this))
, mFontDb(new QGenericUnixFontDatabase)
, mServices(new QMirClientPlatformServices)
- , mClipboard(new QMirClientClipboard)
+ , mAppStateController(new QMirClientAppStateController)
, mScaleFactor(1.0)
{
- setupOptions();
- setupDescription();
+ {
+ QStringList args = QCoreApplication::arguments();
+ setupOptions(args);
+ QByteArray sessionName = generateSessionName(args);
+ setupDescription(sessionName);
+ }
// Create new application instance
mInstance = u_application_instance_new_from_description_with_options(mDesc, mOptions);
@@ -110,19 +118,48 @@ QMirClientClientIntegration::QMirClientClientIntegration()
"running, and the correct socket is being used and is accessible. The shell may have\n"
"rejected the incoming connection, so check its log file");
- mNativeInterface->setMirConnection(u_application_instance_get_mir_connection(mInstance));
+ mMirConnection = u_application_instance_get_mir_connection(mInstance);
+
+ // Choose the default surface format suited to the Mir platform
+ QSurfaceFormat defaultFormat;
+ defaultFormat.setRedBufferSize(8);
+ defaultFormat.setGreenBufferSize(8);
+ defaultFormat.setBlueBufferSize(8);
+ QSurfaceFormat::setDefaultFormat(defaultFormat);
+
+ // Initialize EGL.
+ mEglNativeDisplay = mir_connection_get_egl_native_display(mMirConnection);
+ ASSERT((mEglDisplay = eglGetDisplay(mEglNativeDisplay)) != EGL_NO_DISPLAY);
+ ASSERT(eglInitialize(mEglDisplay, nullptr, nullptr) == EGL_TRUE);
+
+ // Has debug mode been requsted, either with "-testability" switch or QT_LOAD_TESTABILITY env var
+ bool testability = qEnvironmentVariableIsSet("QT_LOAD_TESTABILITY");
+ for (int i=1; !testability && i<argc; i++) {
+ if (strcmp(argv[i], "-testability") == 0) {
+ testability = true;
+ }
+ }
+ if (testability) {
+ mDebugExtension.reset(new QMirClientDebugExtension);
+ }
+}
- // Create default screen.
- screenAdded(new QMirClientScreen(u_application_instance_get_mir_connection(mInstance)));
+void QMirClientClientIntegration::initialize()
+{
+ // Init the ScreenObserver
+ mScreenObserver.reset(new QMirClientScreenObserver(mMirConnection));
+ connect(mScreenObserver.data(), &QMirClientScreenObserver::screenAdded,
+ [this](QMirClientScreen *screen) { this->screenAdded(screen); });
+ connect(mScreenObserver.data(), &QMirClientScreenObserver::screenRemoved,
+ this, &QMirClientClientIntegration::destroyScreen);
+
+ Q_FOREACH (auto screen, mScreenObserver->screens()) {
+ screenAdded(screen);
+ }
// Initialize input.
- if (qEnvironmentVariableIsEmpty("QTUBUNTU_NO_INPUT")) {
- mInput = new QMirClientInput(this);
- mInputContext = QPlatformInputContextFactory::create();
- } else {
- mInput = nullptr;
- mInputContext = nullptr;
- }
+ mInput = new QMirClientInput(this);
+ mInputContext = QPlatformInputContextFactory::create();
// compute the scale factor
const int defaultGridUnit = 8;
@@ -140,10 +177,9 @@ QMirClientClientIntegration::QMirClientClientIntegration()
QMirClientClientIntegration::~QMirClientClientIntegration()
{
+ eglTerminate(mEglDisplay);
delete mInput;
delete mInputContext;
- for (QScreen *screen : QGuiApplication::screens())
- QPlatformIntegration::destroyScreen(screen->handle());
delete mServices;
}
@@ -152,9 +188,8 @@ QPlatformServices *QMirClientClientIntegration::services() const
return mServices;
}
-void QMirClientClientIntegration::setupOptions()
+void QMirClientClientIntegration::setupOptions(QStringList &args)
{
- QStringList args = QCoreApplication::arguments();
int argc = args.size() + 1;
char **argv = new char*[argc];
for (int i = 0; i < argc - 1; i++)
@@ -168,10 +203,11 @@ void QMirClientClientIntegration::setupOptions()
delete [] argv;
}
-void QMirClientClientIntegration::setupDescription()
+void QMirClientClientIntegration::setupDescription(QByteArray &sessionName)
{
mDesc = u_application_description_new();
- UApplicationId* id = u_application_id_new_from_stringn("QtUbuntu", 8);
+
+ UApplicationId* id = u_application_id_new_from_stringn(sessionName.data(), sessionName.count());
u_application_description_set_application_id(mDesc, id);
UApplicationLifecycleDelegate* delegate = u_application_lifecycle_delegate_new();
@@ -181,43 +217,66 @@ void QMirClientClientIntegration::setupDescription()
u_application_description_set_application_lifecycle_delegate(mDesc, delegate);
}
-QPlatformWindow* QMirClientClientIntegration::createPlatformWindow(QWindow* window) const
+QByteArray QMirClientClientIntegration::generateSessionName(QStringList &args)
{
- return const_cast<QMirClientClientIntegration*>(this)->createPlatformWindow(window);
+ // Try to come up with some meaningful session name to uniquely identify this session,
+ // helping with shell debugging
+
+ if (args.count() == 0) {
+ return QByteArray("QtUbuntu");
+ } if (args[0].contains("qmlscene")) {
+ return generateSessionNameFromQmlFile(args);
+ } else {
+ // use the executable name
+ QFileInfo fileInfo(args[0]);
+ return fileInfo.fileName().toLocal8Bit();
+ }
}
-QPlatformWindow* QMirClientClientIntegration::createPlatformWindow(QWindow* window)
+QByteArray QMirClientClientIntegration::generateSessionNameFromQmlFile(QStringList &args)
{
- return new QMirClientWindow(window, mClipboard, screen(),
- mInput, u_application_instance_get_mir_connection(mInstance));
+ Q_FOREACH (QString arg, args) {
+ if (arg.endsWith(".qml")) {
+ QFileInfo fileInfo(arg);
+ return fileInfo.fileName().toLocal8Bit();
+ }
+ }
+
+ // give up
+ return "qmlscene";
}
-QMirClientScreen *QMirClientClientIntegration::screen() const
+QPlatformWindow* QMirClientClientIntegration::createPlatformWindow(QWindow* window) const
{
- return static_cast<QMirClientScreen *>(QGuiApplication::primaryScreen()->handle());
+ if (window->type() == Qt::Desktop) {
+ // Desktop windows should not be backed up by a mir surface as they don't draw anything (nor should).
+ return new QMirClientDesktopWindow(window);
+ } else {
+ return new QMirClientWindow(window, mInput, mNativeInterface, mAppStateController.data(),
+ mEglDisplay, mMirConnection, mDebugExtension.data());
+ }
}
bool QMirClientClientIntegration::hasCapability(QPlatformIntegration::Capability cap) const
{
switch (cap) {
- case ThreadedPixmaps:
- return true;
-
- case OpenGL:
- return true;
-
- case ApplicationState:
- return true;
-
case ThreadedOpenGL:
if (qEnvironmentVariableIsEmpty("QTUBUNTU_NO_THREADED_OPENGL")) {
return true;
} else {
- DLOG("ubuntumirclient: disabled threaded OpenGL");
+ qCDebug(mirclient, "disabled threaded OpenGL");
return false;
}
+
+ case ThreadedPixmaps:
+ case OpenGL:
+ case ApplicationState:
case MultipleWindows:
case NonFullScreenWindows:
+#if QT_VERSION > QT_VERSION_CHECK(5, 5, 0)
+ case SwitchableWidgetComposition:
+#endif
+ case RasterGLSurface: // needed for QQuickWidget
return true;
default:
return QPlatformIntegration::hasCapability(cap);
@@ -237,14 +296,25 @@ QPlatformBackingStore* QMirClientClientIntegration::createPlatformBackingStore(Q
QPlatformOpenGLContext* QMirClientClientIntegration::createPlatformOpenGLContext(
QOpenGLContext* context) const
{
- return const_cast<QMirClientClientIntegration*>(this)->createPlatformOpenGLContext(context);
-}
-
-QPlatformOpenGLContext* QMirClientClientIntegration::createPlatformOpenGLContext(
- QOpenGLContext* context)
-{
- return new QMirClientOpenGLContext(static_cast<QMirClientScreen*>(context->screen()->handle()),
- static_cast<QMirClientOpenGLContext*>(context->shareHandle()));
+ QSurfaceFormat format(context->format());
+
+ auto platformContext = new QMirClientOpenGLContext(format, context->shareHandle(), mEglDisplay);
+ if (!platformContext->isValid()) {
+ // Older Intel Atom-based devices only support OpenGL 1.4 compatibility profile but by default
+ // QML asks for at least OpenGL 2.0. The XCB GLX backend ignores this request and returns a
+ // 1.4 context, but the XCB EGL backend tries to honor it, and fails. The 1.4 context appears to
+ // have sufficient capabilities on MESA (i915) to render correctly however. So reduce the default
+ // requested OpenGL version to 1.0 to ensure EGL will give us a working context (lp:1549455).
+ static const bool isMesa = QString(eglQueryString(mEglDisplay, EGL_VENDOR)).contains(QStringLiteral("Mesa"));
+ if (isMesa) {
+ qCDebug(mirclientGraphics, "Attempting to choose OpenGL 1.4 context which may suit Mesa");
+ format.setMajorVersion(1);
+ format.setMinorVersion(4);
+ delete platformContext;
+ platformContext = new QMirClientOpenGLContext(format, context->shareHandle(), mEglDisplay);
+ }
+ }
+ return platformContext;
}
QStringList QMirClientClientIntegration::themeNames() const
@@ -277,10 +347,65 @@ QVariant QMirClientClientIntegration::styleHint(StyleHint hint) const
QPlatformClipboard* QMirClientClientIntegration::clipboard() const
{
- return mClipboard.data();
+ static QPlatformClipboard *clipboard = nullptr;
+ if (!clipboard) {
+ clipboard = new QMirClientClipboard;
+ }
+ return clipboard;
}
QPlatformNativeInterface* QMirClientClientIntegration::nativeInterface() const
{
return mNativeInterface;
}
+
+QPlatformOffscreenSurface *QMirClientClientIntegration::createPlatformOffscreenSurface(
+ QOffscreenSurface *surface) const
+{
+ return new QEGLPbuffer(mEglDisplay, surface->requestedFormat(), surface);
+}
+
+void QMirClientClientIntegration::destroyScreen(QMirClientScreen *screen)
+{
+ // FIXME: on deleting a screen while a Window is on it, Qt will automatically
+ // move the window to the primaryScreen(). This will trigger a screenChanged
+ // signal, causing things like QQuickScreenAttached to re-fetch screen properties
+ // like DPI and physical size. However this is crashing, as Qt is calling virtual
+ // functions on QPlatformScreen, for reasons unclear. As workaround, move window
+ // to primaryScreen() before deleting the screen. Might be QTBUG-38650
+
+ QScreen *primaryScreen = QGuiApplication::primaryScreen();
+ if (screen != primaryScreen->handle()) {
+ uint32_t movedWindowCount = 0;
+ Q_FOREACH (QWindow *w, QGuiApplication::topLevelWindows()) {
+ if (w->screen()->handle() == screen) {
+ QWindowSystemInterface::handleWindowScreenChanged(w, primaryScreen);
+ ++movedWindowCount;
+ }
+ }
+ if (movedWindowCount > 0) {
+ QWindowSystemInterface::flushWindowSystemEvents();
+ }
+ }
+
+ qCDebug(mirclient) << "Removing Screen with id" << screen->mirOutputId() << "and geometry" << screen->geometry();
+#if QT_VERSION < QT_VERSION_CHECK(5, 5, 0)
+ delete screen;
+#else
+ QPlatformIntegration::destroyScreen(screen);
+#endif
+}
+
+#ifndef QT_NO_ACCESSIBILITY
+QPlatformAccessibility *QMirClientClientIntegration::accessibility() const
+{
+#if !defined(QT_NO_ACCESSIBILITY_ATSPI_BRIDGE)
+ if (!mAccessibility) {
+ Q_ASSERT_X(QCoreApplication::eventDispatcher(), "QMirClientIntegration",
+ "Initializing accessibility without event-dispatcher!");
+ mAccessibility.reset(new QSpiAccessibleBridge());
+ }
+#endif
+ return mAccessibility.data();
+}
+#endif
diff --git a/src/plugins/platforms/mirclient/qmirclientintegration.h b/src/plugins/platforms/mirclient/qmirclientintegration.h
index 97317a310a..035117f4da 100644
--- a/src/plugins/platforms/mirclient/qmirclientintegration.h
+++ b/src/plugins/platforms/mirclient/qmirclientintegration.h
@@ -44,20 +44,28 @@
#include <qpa/qplatformintegration.h>
#include <QSharedPointer>
+#include "qmirclientappstatecontroller.h"
#include "qmirclientplatformservices.h"
+#include "qmirclientscreenobserver.h"
// platform-api
#include <ubuntu/application/description.h>
#include <ubuntu/application/instance.h>
-class QMirClientClipboard;
+#include <EGL/egl.h>
+
+class QMirClientDebugExtension;
class QMirClientInput;
class QMirClientNativeInterface;
class QMirClientScreen;
+class MirConnection;
+
+class QMirClientClientIntegration : public QObject, public QPlatformIntegration
+{
+ Q_OBJECT
-class QMirClientClientIntegration : public QPlatformIntegration {
public:
- QMirClientClientIntegration();
+ QMirClientClientIntegration(int argc, char **argv);
virtual ~QMirClientClientIntegration();
// QPlatformIntegration methods.
@@ -74,14 +82,26 @@ public:
QPlatformWindow* createPlatformWindow(QWindow* window) const override;
QPlatformInputContext* inputContext() const override { return mInputContext; }
QPlatformClipboard* clipboard() const override;
+ void initialize() override;
+ QPlatformOffscreenSurface *createPlatformOffscreenSurface(QOffscreenSurface *surface) const override;
+ QPlatformAccessibility *accessibility() const override;
+
+ // New methods.
+ MirConnection *mirConnection() const { return mMirConnection; }
+ EGLDisplay eglDisplay() const { return mEglDisplay; }
+ EGLNativeDisplayType eglNativeDisplay() const { return mEglNativeDisplay; }
+ QMirClientAppStateController *appStateController() const { return mAppStateController.data(); }
+ QMirClientScreenObserver *screenObserver() const { return mScreenObserver.data(); }
+ QMirClientDebugExtension *debugExtension() const { return mDebugExtension.data(); }
- QPlatformOpenGLContext* createPlatformOpenGLContext(QOpenGLContext* context);
- QPlatformWindow* createPlatformWindow(QWindow* window);
- QMirClientScreen* screen() const;
+private Q_SLOTS:
+ void destroyScreen(QMirClientScreen *screen);
private:
- void setupOptions();
- void setupDescription();
+ void setupOptions(QStringList &args);
+ void setupDescription(QByteArray &sessionName);
+ static QByteArray generateSessionName(QStringList &args);
+ static QByteArray generateSessionNameFromQmlFile(QStringList &args);
QMirClientNativeInterface* mNativeInterface;
QPlatformFontDatabase* mFontDb;
@@ -90,13 +110,22 @@ private:
QMirClientInput* mInput;
QPlatformInputContext* mInputContext;
- QSharedPointer<QMirClientClipboard> mClipboard;
+ mutable QScopedPointer<QPlatformAccessibility> mAccessibility;
+ QScopedPointer<QMirClientDebugExtension> mDebugExtension;
+ QScopedPointer<QMirClientScreenObserver> mScreenObserver;
+ QScopedPointer<QMirClientAppStateController> mAppStateController;
qreal mScaleFactor;
+ MirConnection *mMirConnection;
+
// Platform API stuff
UApplicationOptions* mOptions;
UApplicationDescription* mDesc;
UApplicationInstance* mInstance;
+
+ // EGL related
+ EGLDisplay mEglDisplay{EGL_NO_DISPLAY};
+ EGLNativeDisplayType mEglNativeDisplay;
};
#endif // QMIRCLIENTINTEGRATION_H
diff --git a/src/plugins/platforms/mirclient/qmirclientlogging.h b/src/plugins/platforms/mirclient/qmirclientlogging.h
index 0eb3adbdc7..4921864ced 100644
--- a/src/plugins/platforms/mirclient/qmirclientlogging.h
+++ b/src/plugins/platforms/mirclient/qmirclientlogging.h
@@ -41,23 +41,15 @@
#ifndef QMIRCLIENTLOGGING_H
#define QMIRCLIENTLOGGING_H
-// Logging and assertion macros.
-#define LOG(...) qDebug(__VA_ARGS__)
-#define LOG_IF(cond,...) do { if (cond) qDebug(__VA_ARGS__); } while (0)
+#include <QLoggingCategory>
+
#define ASSERT(cond) ((!(cond)) ? qt_assert(#cond,__FILE__,__LINE__) : qt_noop())
-#define NOT_REACHED() qt_assert("Not reached!",__FILE__,__LINE__)
-// Logging and assertion macros are compiled out for release builds.
-#if !defined(QT_NO_DEBUG)
-#define DLOG(...) LOG(__VA_ARGS__)
-#define DLOG_IF(cond,...) LOG_IF((cond), __VA_ARGS__)
-#define DASSERT(cond) ASSERT((cond))
-#define DNOT_REACHED() NOT_REACHED()
-#else
-#define DLOG(...) qt_noop()
-#define DLOG_IF(cond,...) qt_noop()
-#define DASSERT(cond) qt_noop()
-#define DNOT_REACHED() qt_noop()
-#endif
+Q_DECLARE_LOGGING_CATEGORY(mirclient)
+Q_DECLARE_LOGGING_CATEGORY(mirclientBufferSwap)
+Q_DECLARE_LOGGING_CATEGORY(mirclientInput)
+Q_DECLARE_LOGGING_CATEGORY(mirclientGraphics)
+Q_DECLARE_LOGGING_CATEGORY(mirclientCursor)
+Q_DECLARE_LOGGING_CATEGORY(mirclientDebug)
#endif // QMIRCLIENTLOGGING_H
diff --git a/src/plugins/platforms/mirclient/qmirclientnativeinterface.cpp b/src/plugins/platforms/mirclient/qmirclientnativeinterface.cpp
index 033df6ba11..b85e6fedfa 100644
--- a/src/plugins/platforms/mirclient/qmirclientnativeinterface.cpp
+++ b/src/plugins/platforms/mirclient/qmirclientnativeinterface.cpp
@@ -42,32 +42,36 @@
#include "qmirclientnativeinterface.h"
#include "qmirclientscreen.h"
#include "qmirclientglcontext.h"
+#include "qmirclientwindow.h"
// Qt
-#include <private/qguiapplication_p.h>
+#include <QtGui/private/qguiapplication_p.h>
#include <QtGui/qopenglcontext.h>
#include <QtGui/qscreen.h>
#include <QtCore/QMap>
-class QMirClientResourceMap : public QMap<QByteArray, QMirClientNativeInterface::ResourceType>
+class UbuntuResourceMap : public QMap<QByteArray, QMirClientNativeInterface::ResourceType>
{
public:
- QMirClientResourceMap()
+ UbuntuResourceMap()
: QMap<QByteArray, QMirClientNativeInterface::ResourceType>() {
insert("egldisplay", QMirClientNativeInterface::EglDisplay);
insert("eglcontext", QMirClientNativeInterface::EglContext);
insert("nativeorientation", QMirClientNativeInterface::NativeOrientation);
insert("display", QMirClientNativeInterface::Display);
insert("mirconnection", QMirClientNativeInterface::MirConnection);
+ insert("mirsurface", QMirClientNativeInterface::MirSurface);
+ insert("scale", QMirClientNativeInterface::Scale);
+ insert("formfactor", QMirClientNativeInterface::FormFactor);
}
};
-Q_GLOBAL_STATIC(QMirClientResourceMap, ubuntuResourceMap)
+Q_GLOBAL_STATIC(UbuntuResourceMap, ubuntuResourceMap)
-QMirClientNativeInterface::QMirClientNativeInterface()
- : mGenericEventFilterType(QByteArrayLiteral("Event"))
+QMirClientNativeInterface::QMirClientNativeInterface(const QMirClientClientIntegration *integration)
+ : mIntegration(integration)
+ , mGenericEventFilterType(QByteArrayLiteral("Event"))
, mNativeOrientation(nullptr)
- , mMirConnection(nullptr)
{
}
@@ -88,7 +92,7 @@ void* QMirClientNativeInterface::nativeResourceForIntegration(const QByteArray &
const ResourceType resourceType = ubuntuResourceMap()->value(lowerCaseResource);
if (resourceType == QMirClientNativeInterface::MirConnection) {
- return mMirConnection;
+ return mIntegration->mirConnection();
} else {
return nullptr;
}
@@ -119,14 +123,11 @@ void* QMirClientNativeInterface::nativeResourceForWindow(const QByteArray& resou
if (!ubuntuResourceMap()->contains(kLowerCaseResource))
return NULL;
const ResourceType kResourceType = ubuntuResourceMap()->value(kLowerCaseResource);
- if (kResourceType == QMirClientNativeInterface::EglDisplay) {
- if (window) {
- return static_cast<QMirClientScreen*>(window->screen()->handle())->eglDisplay();
- } else {
- return static_cast<QMirClientScreen*>(
- QGuiApplication::primaryScreen()->handle())->eglDisplay();
- }
- } else if (kResourceType == QMirClientNativeInterface::NativeOrientation) {
+
+ switch (kResourceType) {
+ case EglDisplay:
+ return mIntegration->eglDisplay();
+ case NativeOrientation:
// Return the device's native screen orientation.
if (window) {
QMirClientScreen *ubuntuScreen = static_cast<QMirClientScreen*>(window->screen()->handle());
@@ -136,8 +137,19 @@ void* QMirClientNativeInterface::nativeResourceForWindow(const QByteArray& resou
mNativeOrientation = new Qt::ScreenOrientation(platformScreen->nativeOrientation());
}
return mNativeOrientation;
- } else {
- return NULL;
+ case MirSurface:
+ if (window) {
+ auto ubuntuWindow = static_cast<QMirClientWindow*>(window->handle());
+ if (ubuntuWindow) {
+ return ubuntuWindow->mirSurface();
+ } else {
+ return nullptr;
+ }
+ } else {
+ return nullptr;
+ }
+ default:
+ return nullptr;
}
}
@@ -147,10 +159,59 @@ void* QMirClientNativeInterface::nativeResourceForScreen(const QByteArray& resou
if (!ubuntuResourceMap()->contains(kLowerCaseResource))
return NULL;
const ResourceType kResourceType = ubuntuResourceMap()->value(kLowerCaseResource);
+ if (!screen)
+ screen = QGuiApplication::primaryScreen();
+ auto ubuntuScreen = static_cast<QMirClientScreen*>(screen->handle());
if (kResourceType == QMirClientNativeInterface::Display) {
- if (!screen)
- screen = QGuiApplication::primaryScreen();
- return static_cast<QMirClientScreen*>(screen->handle())->eglNativeDisplay();
+ return mIntegration->eglNativeDisplay();
+ // Changes to the following properties are emitted via the QMirClientNativeInterface::screenPropertyChanged
+ // signal fired by QMirClientScreen. Connect to this signal for these properties updates.
+ // WARNING: code highly thread unsafe!
+ } else if (kResourceType == QMirClientNativeInterface::Scale) {
+ // In application code, read with:
+ // float scale = *reinterpret_cast<float*>(nativeResourceForScreen("scale", screen()));
+ return &ubuntuScreen->mScale;
+ } else if (kResourceType == QMirClientNativeInterface::FormFactor) {
+ return &ubuntuScreen->mFormFactor;
} else
return NULL;
}
+
+// Changes to these properties are emitted via the QMirClientNativeInterface::windowPropertyChanged
+// signal fired by QMirClientWindow. Connect to this signal for these properties updates.
+QVariantMap QMirClientNativeInterface::windowProperties(QPlatformWindow *window) const
+{
+ QVariantMap propertyMap;
+ auto w = static_cast<QMirClientWindow*>(window);
+ if (w) {
+ propertyMap.insert("scale", w->scale());
+ propertyMap.insert("formFactor", w->formFactor());
+ }
+ return propertyMap;
+}
+
+QVariant QMirClientNativeInterface::windowProperty(QPlatformWindow *window, const QString &name) const
+{
+ auto w = static_cast<QMirClientWindow*>(window);
+ if (!w) {
+ return QVariant();
+ }
+
+ if (name == QStringLiteral("scale")) {
+ return w->scale();
+ } else if (name == QStringLiteral("formFactor")) {
+ return w->formFactor();
+ } else {
+ return QVariant();
+ }
+}
+
+QVariant QMirClientNativeInterface::windowProperty(QPlatformWindow *window, const QString &name, const QVariant &defaultValue) const
+{
+ QVariant returnVal = windowProperty(window, name);
+ if (!returnVal.isValid()) {
+ return defaultValue;
+ } else {
+ return returnVal;
+ }
+}
diff --git a/src/plugins/platforms/mirclient/qmirclientnativeinterface.h b/src/plugins/platforms/mirclient/qmirclientnativeinterface.h
index 78a440e956..eb601de301 100644
--- a/src/plugins/platforms/mirclient/qmirclientnativeinterface.h
+++ b/src/plugins/platforms/mirclient/qmirclientnativeinterface.h
@@ -43,11 +43,16 @@
#include <qpa/qplatformnativeinterface.h>
+#include "qmirclientintegration.h"
+
+class QPlatformScreen;
+
class QMirClientNativeInterface : public QPlatformNativeInterface {
+ Q_OBJECT
public:
- enum ResourceType { EglDisplay, EglContext, NativeOrientation, Display, MirConnection };
+ enum ResourceType { EglDisplay, EglContext, NativeOrientation, Display, MirConnection, MirSurface, Scale, FormFactor };
- QMirClientNativeInterface();
+ QMirClientNativeInterface(const QMirClientClientIntegration *integration);
~QMirClientNativeInterface();
// QPlatformNativeInterface methods.
@@ -59,14 +64,20 @@ public:
void* nativeResourceForScreen(const QByteArray& resourceString,
QScreen* screen) override;
+ QVariantMap windowProperties(QPlatformWindow *window) const override;
+ QVariant windowProperty(QPlatformWindow *window, const QString &name) const override;
+ QVariant windowProperty(QPlatformWindow *window, const QString &name, const QVariant &defaultValue) const override;
+
// New methods.
const QByteArray& genericEventFilterType() const { return mGenericEventFilterType; }
- void setMirConnection(void *mirConnection) { mMirConnection = mirConnection; }
+
+Q_SIGNALS: // New signals
+ void screenPropertyChanged(QPlatformScreen *screen, const QString &propertyName);
private:
+ const QMirClientClientIntegration *mIntegration;
const QByteArray mGenericEventFilterType;
Qt::ScreenOrientation* mNativeOrientation;
- void *mMirConnection;
};
#endif // QMIRCLIENTNATIVEINTERFACE_H
diff --git a/src/plugins/platforms/mirclient/qmirclientplugin.cpp b/src/plugins/platforms/mirclient/qmirclientplugin.cpp
index 201b68c304..fc44edfe40 100644
--- a/src/plugins/platforms/mirclient/qmirclientplugin.cpp
+++ b/src/plugins/platforms/mirclient/qmirclientplugin.cpp
@@ -40,19 +40,16 @@
#include "qmirclientplugin.h"
#include "qmirclientintegration.h"
+#include "qmirclientlogging.h"
-QStringList QMirClientIntegrationPlugin::keys() const
-{
- QStringList list;
- list << QStringLiteral("mirclient");
- return list;
-}
+Q_LOGGING_CATEGORY(mirclient, "qt.qpa.mirclient", QtWarningMsg)
-QPlatformIntegration* QMirClientIntegrationPlugin::create(const QString &system,
- const QStringList &)
+QPlatformIntegration *QMirClientIntegrationPlugin::create(const QString &system,
+ const QStringList &/*paramList*/,
+ int &argc, char **argv)
{
if (system.toLower() == QLatin1String("mirclient")) {
- return new QMirClientClientIntegration;
+ return new QMirClientClientIntegration(argc, argv);
} else {
return 0;
}
diff --git a/src/plugins/platforms/mirclient/qmirclientplugin.h b/src/plugins/platforms/mirclient/qmirclientplugin.h
index 8b5da6e4f6..207d97b5af 100644
--- a/src/plugins/platforms/mirclient/qmirclientplugin.h
+++ b/src/plugins/platforms/mirclient/qmirclientplugin.h
@@ -49,8 +49,8 @@ class QMirClientIntegrationPlugin : public QPlatformIntegrationPlugin
Q_PLUGIN_METADATA(IID QPlatformIntegrationFactoryInterface_iid FILE "mirclient.json")
public:
- QStringList keys() const;
- QPlatformIntegration* create(const QString&, const QStringList&);
+ QPlatformIntegration *create(const QString &system, const QStringList &paramList,
+ int &argc, char **argv) override;
};
#endif // QMIRCLIENTPLUGIN_H
diff --git a/src/plugins/platforms/mirclient/qmirclientscreen.cpp b/src/plugins/platforms/mirclient/qmirclientscreen.cpp
index 0a2253e9e2..cc8db830aa 100644
--- a/src/plugins/platforms/mirclient/qmirclientscreen.cpp
+++ b/src/plugins/platforms/mirclient/qmirclientscreen.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2014-2015 Canonical, Ltd.
+** Copyright (C) 2014-2016 Canonical, Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
@@ -42,11 +42,12 @@
#include "qmirclientscreen.h"
#include "qmirclientlogging.h"
#include "qmirclientorientationchangeevent_p.h"
+#include "qmirclientnativeinterface.h"
#include <mir_toolkit/mir_client_library.h>
// Qt
-#include <QCoreApplication>
+#include <QGuiApplication>
#include <QtCore/qmath.h>
#include <QScreen>
#include <QThread>
@@ -55,9 +56,7 @@
#include <memory>
-static const int kSwapInterval = 1;
-
-#if !defined(QT_NO_DEBUG)
+static const int overrideDevicePixelRatio = qgetenv("QT_DEVICE_PIXEL_RATIO").toInt();
static const char *orientationToStr(Qt::ScreenOrientation orientation) {
switch (orientation) {
@@ -71,173 +70,33 @@ static const char *orientationToStr(Qt::ScreenOrientation orientation) {
return "inverted portrait";
case Qt::InvertedLandscapeOrientation:
return "inverted landscape";
- default:
- return "INVALID!";
}
+ Q_UNREACHABLE();
}
-static void printEglConfig(EGLDisplay display, EGLConfig config) {
- DASSERT(display != EGL_NO_DISPLAY);
- DASSERT(config != nullptr);
- static const struct { const EGLint attrib; const char* name; } kAttribs[] = {
- { EGL_BUFFER_SIZE, "EGL_BUFFER_SIZE" },
- { EGL_ALPHA_SIZE, "EGL_ALPHA_SIZE" },
- { EGL_BLUE_SIZE, "EGL_BLUE_SIZE" },
- { EGL_GREEN_SIZE, "EGL_GREEN_SIZE" },
- { EGL_RED_SIZE, "EGL_RED_SIZE" },
- { EGL_DEPTH_SIZE, "EGL_DEPTH_SIZE" },
- { EGL_STENCIL_SIZE, "EGL_STENCIL_SIZE" },
- { EGL_CONFIG_CAVEAT, "EGL_CONFIG_CAVEAT" },
- { EGL_CONFIG_ID, "EGL_CONFIG_ID" },
- { EGL_LEVEL, "EGL_LEVEL" },
- { EGL_MAX_PBUFFER_HEIGHT, "EGL_MAX_PBUFFER_HEIGHT" },
- { EGL_MAX_PBUFFER_PIXELS, "EGL_MAX_PBUFFER_PIXELS" },
- { EGL_MAX_PBUFFER_WIDTH, "EGL_MAX_PBUFFER_WIDTH" },
- { EGL_NATIVE_RENDERABLE, "EGL_NATIVE_RENDERABLE" },
- { EGL_NATIVE_VISUAL_ID, "EGL_NATIVE_VISUAL_ID" },
- { EGL_NATIVE_VISUAL_TYPE, "EGL_NATIVE_VISUAL_TYPE" },
- { EGL_SAMPLES, "EGL_SAMPLES" },
- { EGL_SAMPLE_BUFFERS, "EGL_SAMPLE_BUFFERS" },
- { EGL_SURFACE_TYPE, "EGL_SURFACE_TYPE" },
- { EGL_TRANSPARENT_TYPE, "EGL_TRANSPARENT_TYPE" },
- { EGL_TRANSPARENT_BLUE_VALUE, "EGL_TRANSPARENT_BLUE_VALUE" },
- { EGL_TRANSPARENT_GREEN_VALUE, "EGL_TRANSPARENT_GREEN_VALUE" },
- { EGL_TRANSPARENT_RED_VALUE, "EGL_TRANSPARENT_RED_VALUE" },
- { EGL_BIND_TO_TEXTURE_RGB, "EGL_BIND_TO_TEXTURE_RGB" },
- { EGL_BIND_TO_TEXTURE_RGBA, "EGL_BIND_TO_TEXTURE_RGBA" },
- { EGL_MIN_SWAP_INTERVAL, "EGL_MIN_SWAP_INTERVAL" },
- { EGL_MAX_SWAP_INTERVAL, "EGL_MAX_SWAP_INTERVAL" },
- { -1, NULL }
- };
- const char* string = eglQueryString(display, EGL_VENDOR);
- LOG("EGL vendor: %s", string);
- string = eglQueryString(display, EGL_VERSION);
- LOG("EGL version: %s", string);
- string = eglQueryString(display, EGL_EXTENSIONS);
- LOG("EGL extensions: %s", string);
- LOG("EGL configuration attibutes:");
- for (int index = 0; kAttribs[index].attrib != -1; index++) {
- EGLint value;
- if (eglGetConfigAttrib(display, config, kAttribs[index].attrib, &value))
- LOG(" %s: %d", kAttribs[index].name, static_cast<int>(value));
- }
-}
-#endif
-
-
const QEvent::Type OrientationChangeEvent::mType =
static_cast<QEvent::Type>(QEvent::registerEventType());
-static const MirDisplayOutput *find_active_output(
- const MirDisplayConfiguration *conf)
-{
- const MirDisplayOutput *output = NULL;
- for (uint32_t d = 0; d < conf->num_outputs; d++)
- {
- const MirDisplayOutput *out = conf->outputs + d;
-
- if (out->used &&
- out->connected &&
- out->num_modes &&
- out->current_mode < out->num_modes)
- {
- output = out;
- break;
- }
- }
-
- return output;
-}
-QMirClientScreen::QMirClientScreen(MirConnection *connection)
- : mFormat(QImage::Format_RGB32)
+QMirClientScreen::QMirClientScreen(const MirOutput *output, MirConnection *connection)
+ : mDevicePixelRatio(1.0)
+ , mFormat(QImage::Format_RGB32)
, mDepth(32)
+ , mDpi{0}
+ , mFormFactor{mir_form_factor_unknown}
+ , mScale{1.0}
, mOutputId(0)
- , mSurfaceFormat()
- , mEglDisplay(EGL_NO_DISPLAY)
- , mEglConfig(nullptr)
, mCursor(connection)
{
- // Initialize EGL.
- ASSERT(eglBindAPI(EGL_OPENGL_ES_API) == EGL_TRUE);
-
- mEglNativeDisplay = mir_connection_get_egl_native_display(connection);
- ASSERT((mEglDisplay = eglGetDisplay(mEglNativeDisplay)) != EGL_NO_DISPLAY);
- ASSERT(eglInitialize(mEglDisplay, nullptr, nullptr) == EGL_TRUE);
-
- // Configure EGL buffers format.
- mSurfaceFormat.setRedBufferSize(8);
- mSurfaceFormat.setGreenBufferSize(8);
- mSurfaceFormat.setBlueBufferSize(8);
- mSurfaceFormat.setAlphaBufferSize(8);
- mSurfaceFormat.setDepthBufferSize(24);
- mSurfaceFormat.setStencilBufferSize(8);
- if (!qEnvironmentVariableIsEmpty("QTUBUNTU_MULTISAMPLE")) {
- mSurfaceFormat.setSamples(4);
- DLOG("ubuntumirclient: setting MSAA to 4 samples");
- }
-#ifdef QTUBUNTU_USE_OPENGL
- mSurfaceFormat.setRenderableType(QSurfaceFormat::OpenGL);
-#else
- mSurfaceFormat.setRenderableType(QSurfaceFormat::OpenGLES);
-#endif
- mEglConfig = q_configFromGLFormat(mEglDisplay, mSurfaceFormat, true);
-
- #if !defined(QT_NO_DEBUG)
- printEglConfig(mEglDisplay, mEglConfig);
- #endif
-
- // Set vblank swap interval.
- int swapInterval = kSwapInterval;
- QByteArray swapIntervalString = qgetenv("QTUBUNTU_SWAPINTERVAL");
- if (!swapIntervalString.isEmpty()) {
- bool ok;
- swapInterval = swapIntervalString.toInt(&ok);
- if (!ok)
- swapInterval = kSwapInterval;
- }
- DLOG("ubuntumirclient: setting swap interval to %d", swapInterval);
- eglSwapInterval(mEglDisplay, swapInterval);
-
- // Get screen resolution.
- auto configDeleter = [](MirDisplayConfiguration *config) { mir_display_config_destroy(config); };
- using configUp = std::unique_ptr<MirDisplayConfiguration, decltype(configDeleter)>;
- configUp displayConfig(mir_connection_create_display_config(connection), configDeleter);
- ASSERT(displayConfig != nullptr);
-
- auto const displayOutput = find_active_output(displayConfig.get());
- ASSERT(displayOutput != nullptr);
-
- mOutputId = displayOutput->output_id;
-
- mPhysicalSize = QSizeF(displayOutput->physical_width_mm, displayOutput->physical_height_mm);
- DLOG("ubuntumirclient: screen physical size: %.2fx%.2f", mPhysicalSize.width(), mPhysicalSize.height());
-
- const MirDisplayMode *mode = &displayOutput->modes[displayOutput->current_mode];
- const int kScreenWidth = mode->horizontal_resolution;
- const int kScreenHeight = mode->vertical_resolution;
- DASSERT(kScreenWidth > 0 && kScreenHeight > 0);
-
- DLOG("ubuntumirclient: screen resolution: %dx%d", kScreenWidth, kScreenHeight);
-
- mGeometry = QRect(0, 0, kScreenWidth, kScreenHeight);
-
- DLOG("QQMirClientScreen::QQMirClientScreen (this=%p)", this);
-
- // Set the default orientation based on the initial screen dimmensions.
- mNativeOrientation = (mGeometry.width() >= mGeometry.height()) ? Qt::LandscapeOrientation : Qt::PortraitOrientation;
-
- // If it's a landscape device (i.e. some tablets), start in landscape, otherwise portrait
- mCurrentOrientation = (mNativeOrientation == Qt::LandscapeOrientation) ? Qt::LandscapeOrientation : Qt::PortraitOrientation;
+ setMirOutput(output);
}
QMirClientScreen::~QMirClientScreen()
{
- eglTerminate(mEglDisplay);
}
void QMirClientScreen::customEvent(QEvent* event) {
- DASSERT(QThread::currentThread() == thread());
+ Q_ASSERT(QThread::currentThread() == thread());
OrientationChangeEvent* oReadingEvent = static_cast<OrientationChangeEvent*>(event);
switch (oReadingEvent->mOrientation) {
@@ -261,14 +120,10 @@ void QMirClientScreen::customEvent(QEvent* event) {
Qt::InvertedLandscapeOrientation : Qt::InvertedPortraitOrientation;
break;
}
- default: {
- DLOG("QMirClientScreen::customEvent - Unknown orientation.");
- return;
- }
}
// Raise the event signal so that client apps know the orientation changed
- DLOG("QMirClientScreen::customEvent - handling orientation change to %s", orientationToStr(mCurrentOrientation));
+ qCDebug(mirclient, "QMirClientScreen::customEvent - handling orientation change to %s", orientationToStr(mCurrentOrientation));
QWindowSystemInterface::handleScreenOrientationChange(screen(), mCurrentOrientation);
}
@@ -289,7 +144,7 @@ void QMirClientScreen::handleWindowSurfaceResize(int windowWidth, int windowHeig
mGeometry.setWidth(currGeometry.height());
mGeometry.setHeight(currGeometry.width());
- DLOG("QMirClientScreen::handleWindowSurfaceResize - new screen geometry (w=%d, h=%d)",
+ qCDebug(mirclient, "QMirClientScreen::handleWindowSurfaceResize - new screen geometry (w=%d, h=%d)",
mGeometry.width(), mGeometry.height());
QWindowSystemInterface::handleScreenGeometryChange(screen(),
mGeometry /* newGeometry */,
@@ -300,7 +155,108 @@ void QMirClientScreen::handleWindowSurfaceResize(int windowWidth, int windowHeig
} else {
mCurrentOrientation = Qt::LandscapeOrientation;
}
- DLOG("QMirClientScreen::handleWindowSurfaceResize - new orientation %s",orientationToStr(mCurrentOrientation));
+ qCDebug(mirclient, "QMirClientScreen::handleWindowSurfaceResize - new orientation %s",orientationToStr(mCurrentOrientation));
QWindowSystemInterface::handleScreenOrientationChange(screen(), mCurrentOrientation);
}
}
+
+void QMirClientScreen::setMirOutput(const MirOutput *output)
+{
+ // Physical screen size (in mm)
+ mPhysicalSize.setWidth(mir_output_get_physical_width_mm(output));
+ mPhysicalSize.setHeight(mir_output_get_physical_height_mm(output));
+
+ // Pixel Format
+// mFormat = qImageFormatFromMirPixelFormat(mir_output_get_current_pixel_format(output)); // GERRY: TODO
+
+ // Pixel depth
+ mDepth = 8 * MIR_BYTES_PER_PIXEL(mir_output_get_current_pixel_format(output));
+
+ // Mode = Resolution & refresh rate
+ const MirOutputMode *mode = mir_output_get_current_mode(output);
+ mNativeGeometry.setX(mir_output_get_position_x(output));
+ mNativeGeometry.setY(mir_output_get_position_y(output));
+ mNativeGeometry.setWidth(mir_output_mode_get_width(mode));
+ mNativeGeometry.setHeight(mir_output_mode_get_height(mode));
+
+ mRefreshRate = mir_output_mode_get_refresh_rate(mode);
+
+ // UI scale & DPR
+ mScale = mir_output_get_scale_factor(output);
+ if (overrideDevicePixelRatio > 0) {
+ mDevicePixelRatio = overrideDevicePixelRatio;
+ } else {
+ mDevicePixelRatio = 1.0; // FIXME - need to determine suitable DPR for the specified scale
+ }
+
+ mFormFactor = mir_output_get_form_factor(output);
+
+ mOutputId = mir_output_get_id(output);
+
+ mGeometry.setX(mNativeGeometry.x());
+ mGeometry.setY(mNativeGeometry.y());
+ mGeometry.setWidth(mNativeGeometry.width());
+ mGeometry.setHeight(mNativeGeometry.height());
+
+ // Set the default orientation based on the initial screen dimensions.
+ mNativeOrientation = (mGeometry.width() >= mGeometry.height()) ? Qt::LandscapeOrientation : Qt::PortraitOrientation;
+
+ // If it's a landscape device (i.e. some tablets), start in landscape, otherwise portrait
+ mCurrentOrientation = (mNativeOrientation == Qt::LandscapeOrientation) ? Qt::LandscapeOrientation : Qt::PortraitOrientation;
+}
+
+void QMirClientScreen::updateMirOutput(const MirOutput *output)
+{
+ auto oldRefreshRate = mRefreshRate;
+ auto oldScale = mScale;
+ auto oldFormFactor = mFormFactor;
+ auto oldGeometry = mGeometry;
+
+ setMirOutput(output);
+
+ // Emit change signals in particular order
+ if (oldGeometry != mGeometry) {
+ QWindowSystemInterface::handleScreenGeometryChange(screen(),
+ mGeometry /* newGeometry */,
+ mGeometry /* newAvailableGeometry */);
+ }
+
+ if (!qFuzzyCompare(mRefreshRate, oldRefreshRate)) {
+ QWindowSystemInterface::handleScreenRefreshRateChange(screen(), mRefreshRate);
+ }
+
+ auto nativeInterface = static_cast<QMirClientNativeInterface *>(qGuiApp->platformNativeInterface());
+ if (!qFuzzyCompare(mScale, oldScale)) {
+ nativeInterface->screenPropertyChanged(this, QStringLiteral("scale"));
+ }
+ if (mFormFactor != oldFormFactor) {
+ nativeInterface->screenPropertyChanged(this, QStringLiteral("formFactor"));
+ }
+}
+
+void QMirClientScreen::setAdditionalMirDisplayProperties(float scale, MirFormFactor formFactor, int dpi)
+{
+ if (mDpi != dpi) {
+ mDpi = dpi;
+ QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(screen(), dpi, dpi);
+ }
+
+ auto nativeInterface = static_cast<QMirClientNativeInterface *>(qGuiApp->platformNativeInterface());
+ if (!qFuzzyCompare(mScale, scale)) {
+ mScale = scale;
+ nativeInterface->screenPropertyChanged(this, QStringLiteral("scale"));
+ }
+ if (mFormFactor != formFactor) {
+ mFormFactor = formFactor;
+ nativeInterface->screenPropertyChanged(this, QStringLiteral("formFactor"));
+ }
+}
+
+QDpi QMirClientScreen::logicalDpi() const
+{
+ if (mDpi > 0) {
+ return QDpi(mDpi, mDpi);
+ } else {
+ return QPlatformScreen::logicalDpi();
+ }
+}
diff --git a/src/plugins/platforms/mirclient/qmirclientscreen.h b/src/plugins/platforms/mirclient/qmirclientscreen.h
index b050836ada..b31cba1964 100644
--- a/src/plugins/platforms/mirclient/qmirclientscreen.h
+++ b/src/plugins/platforms/mirclient/qmirclientscreen.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2014-2015 Canonical, Ltd.
+** Copyright (C) 2014-2016 Canonical, Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
@@ -43,17 +43,19 @@
#include <qpa/qplatformscreen.h>
#include <QSurfaceFormat>
-#include <EGL/egl.h>
+
+#include <mir_toolkit/common.h> // just for MirFormFactor enum
#include "qmirclientcursor.h"
struct MirConnection;
+struct MirOutput;
class QMirClientScreen : public QObject, public QPlatformScreen
{
Q_OBJECT
public:
- QMirClientScreen(MirConnection *connection);
+ QMirClientScreen(const MirOutput *output, MirConnection *connection);
virtual ~QMirClientScreen();
// QPlatformScreen methods.
@@ -62,34 +64,43 @@ public:
QRect geometry() const override { return mGeometry; }
QRect availableGeometry() const override { return mGeometry; }
QSizeF physicalSize() const override { return mPhysicalSize; }
+ qreal devicePixelRatio() const override { return mDevicePixelRatio; }
+ QDpi logicalDpi() const override;
Qt::ScreenOrientation nativeOrientation() const override { return mNativeOrientation; }
Qt::ScreenOrientation orientation() const override { return mNativeOrientation; }
QPlatformCursor *cursor() const override { return const_cast<QMirClientCursor*>(&mCursor); }
- // New methods.
- QSurfaceFormat surfaceFormat() const { return mSurfaceFormat; }
- EGLDisplay eglDisplay() const { return mEglDisplay; }
- EGLConfig eglConfig() const { return mEglConfig; }
- EGLNativeDisplayType eglNativeDisplay() const { return mEglNativeDisplay; }
+ // Additional Screen properties from Mir
+ int mirOutputId() const { return mOutputId; }
+ MirFormFactor formFactor() const { return mFormFactor; }
+ float scale() const { return mScale; }
+
+ // Internally used methods
+ void updateMirOutput(const MirOutput *output);
+ void setAdditionalMirDisplayProperties(float scale, MirFormFactor formFactor, int dpi);
void handleWindowSurfaceResize(int width, int height);
- uint32_t mirOutputId() const { return mOutputId; }
// QObject methods.
void customEvent(QEvent* event) override;
private:
- QRect mGeometry;
+ void setMirOutput(const MirOutput *output);
+
+ QRect mGeometry, mNativeGeometry;
QSizeF mPhysicalSize;
+ qreal mDevicePixelRatio;
Qt::ScreenOrientation mNativeOrientation;
Qt::ScreenOrientation mCurrentOrientation;
QImage::Format mFormat;
int mDepth;
- uint32_t mOutputId;
- QSurfaceFormat mSurfaceFormat;
- EGLDisplay mEglDisplay;
- EGLConfig mEglConfig;
- EGLNativeDisplayType mEglNativeDisplay;
+ int mDpi;
+ qreal mRefreshRate;
+ MirFormFactor mFormFactor;
+ float mScale;
+ int mOutputId;
QMirClientCursor mCursor;
+
+ friend class QMirClientNativeInterface;
};
#endif // QMIRCLIENTSCREEN_H
diff --git a/src/plugins/platforms/mirclient/qmirclientscreenobserver.cpp b/src/plugins/platforms/mirclient/qmirclientscreenobserver.cpp
new file mode 100644
index 0000000000..792aeca351
--- /dev/null
+++ b/src/plugins/platforms/mirclient/qmirclientscreenobserver.cpp
@@ -0,0 +1,161 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Canonical, Ltd.
+** 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 "qmirclientscreenobserver.h"
+#include "qmirclientscreen.h"
+#include "qmirclientwindow.h"
+#include "qmirclientlogging.h"
+
+// Qt
+#include <QMetaObject>
+#include <QPointer>
+
+// Mir
+#include <mirclient/mir_toolkit/mir_connection.h>
+#include <mirclient/mir_toolkit/mir_display_configuration.h>
+
+#include <memory>
+
+namespace {
+ static void displayConfigurationChangedCallback(MirConnection */*connection*/, void* context)
+ {
+ ASSERT(context != NULL);
+ QMirClientScreenObserver *observer = static_cast<QMirClientScreenObserver *>(context);
+ QMetaObject::invokeMethod(observer, "update");
+ }
+
+ const char *mirFormFactorToStr(MirFormFactor formFactor)
+ {
+ switch (formFactor) {
+ case mir_form_factor_unknown: return "unknown";
+ case mir_form_factor_phone: return "phone";
+ case mir_form_factor_tablet: return "tablet";
+ case mir_form_factor_monitor: return "monitor";
+ case mir_form_factor_tv: return "tv";
+ case mir_form_factor_projector: return "projector";
+ }
+ Q_UNREACHABLE();
+ }
+} // anonymous namespace
+
+QMirClientScreenObserver::QMirClientScreenObserver(MirConnection *mirConnection)
+ : mMirConnection(mirConnection)
+{
+ mir_connection_set_display_config_change_callback(mirConnection, ::displayConfigurationChangedCallback, this);
+ update();
+}
+
+void QMirClientScreenObserver::update()
+{
+ // Wrap MirDisplayConfiguration to always delete when out of scope
+ auto configDeleter = [](MirDisplayConfig *config) { mir_display_config_release(config); };
+ using configUp = std::unique_ptr<MirDisplayConfig, decltype(configDeleter)>;
+ configUp displayConfig(mir_connection_create_display_configuration(mMirConnection), configDeleter);
+
+ // Mir only tells us something changed, it is up to us to figure out what.
+ QList<QMirClientScreen*> newScreenList;
+ QList<QMirClientScreen*> oldScreenList = mScreenList;
+ mScreenList.clear();
+
+ for (int i = 0; i < mir_display_config_get_num_outputs(displayConfig.get()); i++) {
+ const MirOutput *output = mir_display_config_get_output(displayConfig.get(), i);
+ if (mir_output_is_enabled(output)) {
+ QMirClientScreen *screen = findScreenWithId(oldScreenList, mir_output_get_id(output));
+ if (screen) { // we've already set up this display before
+ screen->updateMirOutput(output);
+ oldScreenList.removeAll(screen);
+ } else {
+ // new display, so create QMirClientScreen for it
+ screen = new QMirClientScreen(output, mMirConnection);
+ newScreenList.append(screen);
+ qCDebug(mirclient) << "Added Screen with id" << mir_output_get_id(output)
+ << "and geometry" << screen->geometry();
+ }
+ mScreenList.append(screen);
+ }
+ }
+
+ // Announce old & unused Screens, should be deleted by the slot
+ Q_FOREACH (const auto screen, oldScreenList) {
+ Q_EMIT screenRemoved(screen);
+ }
+
+ /*
+ * Mir's MirDisplayOutput does not include formFactor or scale for some reason, but Qt
+ * will want that information on creating the QScreen. Only way we get that info is when
+ * Mir positions a Window on that Screen. See "handleScreenPropertiesChange" method
+ */
+
+ // Announce new Screens
+ Q_FOREACH (const auto screen, newScreenList) {
+ Q_EMIT screenAdded(screen);
+ }
+
+ qCDebug(mirclient) << "=======================================";
+ for (auto screen: mScreenList) {
+ qCDebug(mirclient) << screen << "- id:" << screen->mirOutputId()
+ << "geometry:" << screen->geometry()
+ << "form factor:" << mirFormFactorToStr(screen->formFactor())
+ << "scale:" << screen->scale();
+ }
+ qCDebug(mirclient) << "=======================================";
+}
+
+QMirClientScreen *QMirClientScreenObserver::findScreenWithId(int id)
+{
+ return findScreenWithId(mScreenList, id);
+}
+
+QMirClientScreen *QMirClientScreenObserver::findScreenWithId(const QList<QMirClientScreen *> &list, int id)
+{
+ Q_FOREACH (const auto screen, list) {
+ if (screen->mirOutputId() == id) {
+ return screen;
+ }
+ }
+ return nullptr;
+}
+
+void QMirClientScreenObserver::handleScreenPropertiesChange(QMirClientScreen *screen, int dpi,
+ MirFormFactor formFactor, float scale)
+{
+ screen->setAdditionalMirDisplayProperties(scale, formFactor, dpi);
+}
+
diff --git a/src/plugins/platforms/mirclient/qmirclientscreenobserver.h b/src/plugins/platforms/mirclient/qmirclientscreenobserver.h
new file mode 100644
index 0000000000..ad927319c1
--- /dev/null
+++ b/src/plugins/platforms/mirclient/qmirclientscreenobserver.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Canonical, Ltd.
+** 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 QMIRCLIENTSCREENOBSERVER_H
+#define QMIRCLIENTSCREENOBSERVER_H
+
+#include <QObject>
+
+#include <mir_toolkit/mir_connection.h>
+
+class QMirClientScreen;
+
+class QMirClientScreenObserver : public QObject
+{
+ Q_OBJECT
+
+public:
+ QMirClientScreenObserver(MirConnection *connection);
+
+ QList<QMirClientScreen*> screens() const { return mScreenList; }
+ QMirClientScreen *findScreenWithId(int id);
+
+ void handleScreenPropertiesChange(QMirClientScreen *screen, int dpi,
+ MirFormFactor formFactor, float scale);
+
+Q_SIGNALS:
+ void screenAdded(QMirClientScreen *screen);
+ void screenRemoved(QMirClientScreen *screen);
+
+private Q_SLOTS:
+ void update();
+
+private:
+ QMirClientScreen *findScreenWithId(const QList<QMirClientScreen *> &list, int id);
+ void removeScreen(QMirClientScreen *screen);
+
+ MirConnection *mMirConnection;
+ QList<QMirClientScreen*> mScreenList;
+};
+
+#endif // QMIRCLIENTSCREENOBSERVER_H
diff --git a/src/plugins/platforms/mirclient/qmirclientwindow.cpp b/src/plugins/platforms/mirclient/qmirclientwindow.cpp
index 60b9cd7900..adc8ae652a 100644
--- a/src/plugins/platforms/mirclient/qmirclientwindow.cpp
+++ b/src/plugins/platforms/mirclient/qmirclientwindow.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2014-2015 Canonical, Ltd.
+** Copyright (C) 2014-2016 Canonical, Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
@@ -40,26 +40,33 @@
// Local
#include "qmirclientwindow.h"
-#include "qmirclientclipboard.h"
+#include "qmirclientdebugextension.h"
+#include "qmirclientnativeinterface.h"
#include "qmirclientinput.h"
+#include "qmirclientintegration.h"
#include "qmirclientscreen.h"
#include "qmirclientlogging.h"
#include <mir_toolkit/mir_client_library.h>
+#include <mir_toolkit/version.h>
// Qt
#include <qpa/qwindowsysteminterface.h>
#include <QMutexLocker>
#include <QSize>
#include <QtMath>
+#include <QtEglSupport/private/qeglconvenience_p.h>
// Platform API
#include <ubuntu/application/instance.h>
#include <EGL/egl.h>
+Q_LOGGING_CATEGORY(mirclientBufferSwap, "qt.qpa.mirclient.bufferSwap", QtWarningMsg)
+
namespace
{
+const Qt::WindowType LowChromeWindowHint = (Qt::WindowType)0x00800000;
// FIXME: this used to be defined by platform-api, but it's been removed in v3. Change ubuntu-keyboard to use
// a different enum for window roles.
@@ -87,40 +94,110 @@ EGLNativeWindowType nativeWindowFor(MirSurface *surf)
return reinterpret_cast<EGLNativeWindowType>(mir_buffer_stream_get_egl_native_window(stream));
}
-MirSurfaceState qtWindowStateToMirSurfaceState(Qt::WindowState state)
+const char *qtWindowStateToStr(Qt::WindowState state)
{
switch (state) {
case Qt::WindowNoState:
- return mir_surface_state_restored;
+ return "NoState";
case Qt::WindowFullScreen:
- return mir_surface_state_fullscreen;
+ return "FullScreen";
case Qt::WindowMaximized:
- return mir_surface_state_maximized;
+ return "Maximized";
case Qt::WindowMinimized:
- return mir_surface_state_minimized;
- default:
- LOG("Unexpected Qt::WindowState: %d", state);
- return mir_surface_state_restored;
+ return "Minimized";
+ case Qt::WindowActive:
+ return "Active";
}
+ Q_UNREACHABLE();
}
-#if !defined(QT_NO_DEBUG)
-const char *qtWindowStateToStr(Qt::WindowState state)
+const char *mirSurfaceStateToStr(MirSurfaceState surfaceState)
+{
+ switch (surfaceState) {
+ case mir_surface_state_unknown: return "unknown";
+ case mir_surface_state_restored: return "restored";
+ case mir_surface_state_minimized: return "minimized";
+ case mir_surface_state_maximized: return "vertmaximized";
+ case mir_surface_state_vertmaximized: return "vertmaximized";
+ case mir_surface_state_fullscreen: return "fullscreen";
+ case mir_surface_state_horizmaximized: return "horizmaximized";
+ case mir_surface_state_hidden: return "hidden";
+ case mir_surface_states: Q_UNREACHABLE();
+ }
+ Q_UNREACHABLE();
+}
+
+const char *mirPixelFormatToStr(MirPixelFormat pixelFormat)
+{
+ switch (pixelFormat) {
+ case mir_pixel_format_invalid: return "invalid";
+ case mir_pixel_format_abgr_8888: return "ABGR8888";
+ case mir_pixel_format_xbgr_8888: return "XBGR8888";
+ case mir_pixel_format_argb_8888: return "ARGB8888";
+ case mir_pixel_format_xrgb_8888: return "XRGB8888";
+ case mir_pixel_format_bgr_888: return "BGR888";
+ case mir_pixel_format_rgb_888: return "RGB888";
+ case mir_pixel_format_rgb_565: return "RGB565";
+ case mir_pixel_format_rgba_5551: return "RGBA5551";
+ case mir_pixel_format_rgba_4444: return "RGBA4444";
+ case mir_pixel_formats: Q_UNREACHABLE();
+ }
+ Q_UNREACHABLE();
+}
+
+const char *mirSurfaceTypeToStr(MirSurfaceType type)
+{
+ switch (type) {
+ case mir_surface_type_normal: return "Normal"; /**< AKA "regular" */
+ case mir_surface_type_utility: return "Utility"; /**< AKA "floating regular" */
+ case mir_surface_type_dialog: return "Dialog";
+ case mir_surface_type_gloss: return "Gloss";
+ case mir_surface_type_freestyle: return "Freestyle";
+ case mir_surface_type_menu: return "Menu";
+ case mir_surface_type_inputmethod: return "Input Method"; /**< AKA "OSK" or handwriting etc. */
+ case mir_surface_type_satellite: return "Satellite"; /**< AKA "toolbox"/"toolbar" */
+ case mir_surface_type_tip: return "Tip"; /**< AKA "tooltip" */
+ case mir_surface_types: Q_UNREACHABLE();
+ }
+ return "";
+}
+
+MirSurfaceState qtWindowStateToMirSurfaceState(Qt::WindowState state)
{
switch (state) {
case Qt::WindowNoState:
- return "NoState";
+ case Qt::WindowActive:
+ return mir_surface_state_restored;
case Qt::WindowFullScreen:
- return "FullScreen";
+ return mir_surface_state_fullscreen;
case Qt::WindowMaximized:
- return "Maximized";
+ return mir_surface_state_maximized;
case Qt::WindowMinimized:
- return "Minimized";
+ return mir_surface_state_minimized;
+ }
+ return mir_surface_state_unknown; // should never be reached
+}
+
+MirSurfaceType qtWindowTypeToMirSurfaceType(Qt::WindowType type)
+{
+ switch (type & Qt::WindowType_Mask) {
+ case Qt::Dialog:
+ return mir_surface_type_dialog;
+ case Qt::Sheet:
+ case Qt::Drawer:
+ return mir_surface_type_utility;
+ case Qt::Popup:
+ case Qt::Tool:
+ return mir_surface_type_menu;
+ case Qt::ToolTip:
+ return mir_surface_type_tip;
+ case Qt::SplashScreen:
+ return mir_surface_type_freestyle;
+ case Qt::Window:
default:
- return "!?";
+ return mir_surface_type_normal;
}
}
-#endif
WId makeId()
{
@@ -128,14 +205,6 @@ WId makeId()
return id++;
}
-MirPixelFormat defaultPixelFormatFor(MirConnection *connection)
-{
- MirPixelFormat format;
- unsigned int nformats;
- mir_connection_get_available_surface_formats(connection, &format, 1, &nformats);
- return format;
-}
-
UAUiWindowRole roleFor(QWindow *window)
{
QVariant roleVariant = window->property("role");
@@ -155,52 +224,93 @@ QMirClientWindow *transientParentFor(QWindow *window)
return parent ? static_cast<QMirClientWindow *>(parent->handle()) : nullptr;
}
-Spec makeSurfaceSpec(QWindow *window, QMirClientInput *input, MirConnection *connection)
-{
- const auto geom = window->geometry();
- const int width = geom.width() > 0 ? geom.width() : 1;
- const int height = geom.height() > 0 ? geom.height() : 1;
- const auto pixelFormat = defaultPixelFormatFor(connection);
-
- if (U_ON_SCREEN_KEYBOARD_ROLE == roleFor(window)) {
- DLOG("[ubuntumirclient QPA] makeSurfaceSpec(window=%p) - creating input method surface (width=%d, height=%d", window, width, height);
- return Spec{mir_connection_create_spec_for_input_method(connection, width, height, pixelFormat)};
- }
-
- const Qt::WindowType type = window->type();
- if (type == Qt::Popup) {
- auto parent = transientParentFor(window);
- if (parent == nullptr) {
- //NOTE: We cannot have a parentless popup -
- //try using the last surface to receive input as that will most likely be
- //the one that caused this popup to be created
- parent = input->lastFocusedWindow();
- }
- if (parent) {
- auto pos = geom.topLeft();
- pos -= parent->geometry().topLeft();
- MirRectangle location{pos.x(), pos.y(), 0, 0};
- DLOG("[ubuntumirclient QPA] makeSurfaceSpec(window=%p) - creating menu surface(width:%d, height:%d)", window, width, height);
- return Spec{mir_connection_create_spec_for_menu(
- connection, width, height, pixelFormat, parent->mirSurface(),
- &location, mir_edge_attachment_any)};
- } else {
- DLOG("[ubuntumirclient QPA] makeSurfaceSpec(window=%p) - cannot create a menu without a parent!", window);
- }
- } else if (type == Qt::Dialog) {
- auto parent = transientParentFor(window);
- if (parent) {
- // Modal dialog
- DLOG("[ubuntumirclient QPA] makeSurfaceSpec(window=%p) - creating modal dialog (width=%d, height=%d", window, width, height);
- return Spec{mir_connection_create_spec_for_modal_dialog(connection, width, height, pixelFormat, parent->mirSurface())};
- } else {
- // TODO: do Qt parentless dialogs have the same semantics as mir?
- DLOG("[ubuntumirclient QPA] makeSurfaceSpec(window=%p) - creating parentless dialog (width=%d, height=%d)", window, width, height);
- return Spec{mir_connection_create_spec_for_dialog(connection, width, height, pixelFormat)};
- }
- }
- DLOG("[ubuntumirclient QPA] makeSurfaceSpec(window=%p) - creating normal surface(type=0x%x, width=%d, height=%d)", window, type, width, height);
- return Spec{mir_connection_create_spec_for_normal_surface(connection, width, height, pixelFormat)};
+bool requiresParent(const MirSurfaceType type)
+{
+ switch (type) {
+ case mir_surface_type_dialog: //FIXME - not quite what the specification dictates, but is what Mir's api dictates
+ case mir_surface_type_utility:
+ case mir_surface_type_gloss:
+ case mir_surface_type_menu:
+ case mir_surface_type_satellite:
+ case mir_surface_type_tip:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool requiresParent(const Qt::WindowType type)
+{
+ return requiresParent(qtWindowTypeToMirSurfaceType(type));
+}
+
+bool isMovable(const Qt::WindowType type)
+{
+ auto mirType = qtWindowTypeToMirSurfaceType(type);
+ switch (mirType) {
+ case mir_surface_type_menu:
+ case mir_surface_type_tip:
+ return true;
+ default:
+ return false;
+ }
+}
+
+Spec makeSurfaceSpec(QWindow *window, MirPixelFormat pixelFormat, QMirClientWindow *parentWindowHandle,
+ MirConnection *connection)
+{
+ const auto geometry = window->geometry();
+ const int width = geometry.width() > 0 ? geometry.width() : 1;
+ const int height = geometry.height() > 0 ? geometry.height() : 1;
+ auto type = qtWindowTypeToMirSurfaceType(window->type());
+
+ if (U_ON_SCREEN_KEYBOARD_ROLE == roleFor(window)) {
+ type = mir_surface_type_inputmethod;
+ }
+
+ MirRectangle location{geometry.x(), geometry.y(), 0, 0};
+ MirSurface *parent = nullptr;
+ if (parentWindowHandle) {
+ parent = parentWindowHandle->mirSurface();
+ // Qt uses absolute positioning, but Mir positions surfaces relative to parent.
+ location.top -= parentWindowHandle->geometry().top();
+ location.left -= parentWindowHandle->geometry().left();
+ }
+
+ Spec spec;
+
+ switch (type) {
+ case mir_surface_type_menu:
+ spec = Spec{mir_connection_create_spec_for_menu(connection, width, height, pixelFormat, parent,
+ &location, mir_edge_attachment_any)};
+ break;
+ case mir_surface_type_dialog:
+ spec = Spec{mir_connection_create_spec_for_modal_dialog(connection, width, height, pixelFormat, parent)};
+ break;
+ case mir_surface_type_utility:
+ spec = Spec{mir_connection_create_spec_for_dialog(connection, width, height, pixelFormat)};
+ break;
+ case mir_surface_type_tip:
+#if MIR_CLIENT_VERSION < MIR_VERSION_NUMBER(3, 4, 0)
+ spec = Spec{mir_connection_create_spec_for_tooltip(connection, width, height, pixelFormat, parent,
+ &location)};
+#else
+ spec = Spec{mir_connection_create_spec_for_tip(connection, width, height, pixelFormat, parent,
+ &location, mir_edge_attachment_any)};
+#endif
+ break;
+ case mir_surface_type_inputmethod:
+ spec = Spec{mir_connection_create_spec_for_input_method(connection, width, height, pixelFormat)};
+ break;
+ default:
+ spec = Spec{mir_connection_create_spec_for_normal_surface(connection, width, height, pixelFormat)};
+ break;
+ }
+
+ qCDebug(mirclient, "makeSurfaceSpec(window=%p): %s spec (type=0x%x, position=(%d, %d)px, size=(%dx%d)px)",
+ window, mirSurfaceTypeToStr(type), window->type(), location.left, location.top, width, height);
+
+ return std::move(spec);
}
void setSizingConstraints(MirSurfaceSpec *spec, const QSize& minSize, const QSize& maxSize, const QSize& increment)
@@ -221,16 +331,30 @@ void setSizingConstraints(MirSurfaceSpec *spec, const QSize& minSize, const QSiz
}
}
-MirSurface *createMirSurface(QWindow *window, QMirClientScreen *screen, QMirClientInput *input, MirConnection *connection)
+MirSurface *createMirSurface(QWindow *window, int mirOutputId, QMirClientWindow *parentWindowHandle,
+ MirPixelFormat pixelFormat, MirConnection *connection,
+ mir_surface_event_callback inputCallback, void *inputContext)
{
- auto spec = makeSurfaceSpec(window, input, connection);
+ auto spec = makeSurfaceSpec(window, pixelFormat, parentWindowHandle, connection);
+
+ // Install event handler as early as possible
+ mir_surface_spec_set_event_handler(spec.get(), inputCallback, inputContext);
+
const auto title = window->title().toUtf8();
mir_surface_spec_set_name(spec.get(), title.constData());
setSizingConstraints(spec.get(), window->minimumSize(), window->maximumSize(), window->sizeIncrement());
if (window->windowState() == Qt::WindowFullScreen) {
- mir_surface_spec_set_fullscreen_on_output(spec.get(), screen->mirOutputId());
+ mir_surface_spec_set_fullscreen_on_output(spec.get(), mirOutputId);
+ }
+
+ if (window->flags() & LowChromeWindowHint) {
+ mir_surface_spec_set_shell_chrome(spec.get(), mir_shell_chrome_low);
+ }
+
+ if (!window->isVisible()) {
+ mir_surface_spec_set_state(spec.get(), mir_surface_state_hidden);
}
auto surface = mir_surface_create_sync(spec.get());
@@ -238,83 +362,45 @@ MirSurface *createMirSurface(QWindow *window, QMirClientScreen *screen, QMirClie
return surface;
}
-// FIXME - in order to work around https://bugs.launchpad.net/mir/+bug/1346633
-// we need to guess the panel height (3GU)
-int panelHeight()
-{
- if (qEnvironmentVariableIsSet("QT_MIRCLIENT_IGNORE_PANEL"))
- return 0;
- const int defaultGridUnit = 8;
- int gridUnit = defaultGridUnit;
- QByteArray gridUnitString = qgetenv("GRID_UNIT_PX");
- if (!gridUnitString.isEmpty()) {
- bool ok;
- gridUnit = gridUnitString.toInt(&ok);
- if (!ok) {
- gridUnit = defaultGridUnit;
+QMirClientWindow *getParentIfNecessary(QWindow *window, QMirClientInput *input)
+{
+ QMirClientWindow *parentWindowHandle = nullptr;
+ if (requiresParent(window->type())) {
+ parentWindowHandle = transientParentFor(window);
+ if (parentWindowHandle == nullptr) {
+ // NOTE: Mir requires this surface have a parent. Try using the last surface to receive input as that will
+ // most likely be the one that caused this surface to be created
+ parentWindowHandle = input->lastInputWindow();
}
}
- return gridUnit * 3;
+ return parentWindowHandle;
}
-} //namespace
-
-class QMirClientSurface
+MirPixelFormat disableAlphaBufferIfPossible(MirPixelFormat pixelFormat)
{
-public:
- QMirClientSurface(QMirClientWindow *platformWindow, QMirClientScreen *screen, QMirClientInput *input, MirConnection *connection)
- : mWindow(platformWindow->window())
- , mPlatformWindow(platformWindow)
- , mInput(input)
- , mConnection(connection)
- , mMirSurface(createMirSurface(mWindow, screen, input, connection))
- , mEglDisplay(screen->eglDisplay())
- , mEglSurface(eglCreateWindowSurface(mEglDisplay, screen->eglConfig(), nativeWindowFor(mMirSurface), nullptr))
- , mVisible(false)
- , mNeedsRepaint(false)
- , mParented(mWindow->transientParent() || mWindow->parent())
- , mWindowState(mWindow->windowState())
-
- {
- mir_surface_set_event_handler(mMirSurface, surfaceEventCallback, this);
-
- // Window manager can give us a final size different from what we asked for
- // so let's check what we ended up getting
- MirSurfaceParameters parameters;
- mir_surface_get_parameters(mMirSurface, &parameters);
-
- auto geom = mWindow->geometry();
- geom.setWidth(parameters.width);
- geom.setHeight(parameters.height);
- if (mWindowState == Qt::WindowFullScreen) {
- geom.setY(0);
- } else {
- geom.setY(panelHeight());
- }
+ switch (pixelFormat) {
+ case mir_pixel_format_abgr_8888:
+ return mir_pixel_format_xbgr_8888;
+ case mir_pixel_format_argb_8888:
+ return mir_pixel_format_xrgb_8888;
+ default: // can do nothing, leave it alone
+ return pixelFormat;
+ }
+}
+} //namespace
- // Assume that the buffer size matches the surface size at creation time
- mBufferSize = geom.size();
- platformWindow->QPlatformWindow::setGeometry(geom);
- QWindowSystemInterface::handleGeometryChange(mWindow, geom);
- DLOG("[ubuntumirclient QPA] created surface at (%d, %d) with size (%d, %d), title '%s', role: '%d'\n",
- geom.x(), geom.y(), geom.width(), geom.height(), mWindow->title().toUtf8().constData(), roleFor(mWindow));
- }
- ~QMirClientSurface()
- {
- if (mEglSurface != EGL_NO_SURFACE)
- eglDestroySurface(mEglDisplay, mEglSurface);
- if (mMirSurface)
- mir_surface_release_sync(mMirSurface);
- }
+class UbuntuSurface
+{
+public:
+ UbuntuSurface(QMirClientWindow *platformWindow, EGLDisplay display, QMirClientInput *input, MirConnection *connection);
+ ~UbuntuSurface();
- QMirClientSurface(QMirClientSurface const&) = delete;
- QMirClientSurface& operator=(QMirClientSurface const&) = delete;
+ UbuntuSurface(const UbuntuSurface &) = delete;
+ UbuntuSurface& operator=(const UbuntuSurface &) = delete;
- void resize(const QSize& newSize);
- void setState(Qt::WindowState newState);
- void setVisible(bool state);
+ void updateGeometry(const QRect &newGeometry);
void updateTitle(const QString& title);
void setSizingConstraints(const QSize& minSize, const QSize& maxSize, const QSize& increment);
@@ -322,76 +408,151 @@ public:
void handleSurfaceResized(int width, int height);
int needsRepaint() const;
+ MirSurfaceState state() const { return mir_surface_get_state(mMirSurface); }
+ void setState(MirSurfaceState state);
+
+ MirSurfaceType type() const { return mir_surface_get_type(mMirSurface); }
+
+ void setShellChrome(MirShellChrome shellChrome);
+
EGLSurface eglSurface() const { return mEglSurface; }
MirSurface *mirSurface() const { return mMirSurface; }
+ void setSurfaceParent(MirSurface*);
+ bool hasParent() const { return mParented; }
+
+ QSurfaceFormat format() const { return mFormat; }
+
+ bool mNeedsExposeCatchup;
+
+ QString persistentSurfaceId();
+
private:
static void surfaceEventCallback(MirSurface* surface, const MirEvent *event, void* context);
void postEvent(const MirEvent *event);
- void updateSurface();
QWindow * const mWindow;
QMirClientWindow * const mPlatformWindow;
QMirClientInput * const mInput;
MirConnection * const mConnection;
+ QMirClientWindow * mParentWindowHandle{nullptr};
- MirSurface * const mMirSurface;
+ MirSurface* mMirSurface;
const EGLDisplay mEglDisplay;
- const EGLSurface mEglSurface;
+ EGLSurface mEglSurface;
- bool mVisible;
bool mNeedsRepaint;
bool mParented;
- Qt::WindowState mWindowState;
QSize mBufferSize;
+ QSurfaceFormat mFormat;
+ MirPixelFormat mPixelFormat;
QMutex mTargetSizeMutex;
QSize mTargetSize;
+ MirShellChrome mShellChrome;
+ QString mPersistentIdStr;
};
-void QMirClientSurface::resize(const QSize& size)
-{
- DLOG("[ubuntumirclient QPA] resize(window=%p, width=%d, height=%d)", mWindow, size.width(), size.height());
-
- if (mWindowState == Qt::WindowFullScreen || mWindowState == Qt::WindowMaximized) {
- DLOG("[ubuntumirclient QPA] resize(window=%p) - not resizing, window is maximized or fullscreen", mWindow);
- return;
+UbuntuSurface::UbuntuSurface(QMirClientWindow *platformWindow, EGLDisplay display, QMirClientInput *input, MirConnection *connection)
+ : mWindow(platformWindow->window())
+ , mPlatformWindow(platformWindow)
+ , mInput(input)
+ , mConnection(connection)
+ , mEglDisplay(display)
+ , mNeedsRepaint(false)
+ , mParented(mWindow->transientParent() || mWindow->parent())
+ , mFormat(mWindow->requestedFormat())
+ , mShellChrome(mWindow->flags() & LowChromeWindowHint ? mir_shell_chrome_low : mir_shell_chrome_normal)
+{
+ // Have Qt choose most suitable EGLConfig for the requested surface format, and update format to reflect it
+ EGLConfig config = q_configFromGLFormat(display, mFormat, true);
+ if (config == 0) {
+ // Older Intel Atom-based devices only support OpenGL 1.4 compatibility profile but by default
+ // QML asks for at least OpenGL 2.0. The XCB GLX backend ignores this request and returns a
+ // 1.4 context, but the XCB EGL backend tries to honor it, and fails. The 1.4 context appears to
+ // have sufficient capabilities on MESA (i915) to render correctly however. So reduce the default
+ // requested OpenGL version to 1.0 to ensure EGL will give us a working context (lp:1549455).
+ static const bool isMesa = QString(eglQueryString(display, EGL_VENDOR)).contains(QStringLiteral("Mesa"));
+ if (isMesa) {
+ qCDebug(mirclientGraphics, "Attempting to choose OpenGL 1.4 context which may suit Mesa");
+ mFormat.setMajorVersion(1);
+ mFormat.setMinorVersion(4);
+ config = q_configFromGLFormat(display, mFormat, true);
+ }
+ }
+ if (config == 0) {
+ qCritical() << "Qt failed to choose a suitable EGLConfig to suit the surface format" << mFormat;
}
- if (size.isEmpty()) {
- DLOG("[ubuntumirclient QPA] resize(window=%p) - not resizing, size is empty", mWindow);
- return;
+ mFormat = q_glFormatFromConfig(display, config, mFormat);
+
+ // Have Mir decide the pixel format most suited to the chosen EGLConfig. This is the only way
+ // Mir will know what EGLConfig has been chosen - it cannot deduce it from the buffers.
+ mPixelFormat = mir_connection_get_egl_pixel_format(connection, display, config);
+ // But the chosen EGLConfig might have an alpha buffer enabled, even if not requested by the client.
+ // If that's the case, try to edit the chosen pixel format in order to disable the alpha buffer.
+ // This is an optimization for the compositor, as it can avoid blending this surface.
+ if (mWindow->requestedFormat().alphaBufferSize() < 0) {
+ mPixelFormat = disableAlphaBufferIfPossible(mPixelFormat);
}
- Spec spec{mir_connection_create_spec_for_changes(mConnection)};
- mir_surface_spec_set_width(spec.get(), size.width());
- mir_surface_spec_set_height(spec.get(), size.height());
- mir_surface_apply_spec(mMirSurface, spec.get());
+ const auto outputId = static_cast<QMirClientScreen *>(mWindow->screen()->handle())->mirOutputId();
+
+ mParentWindowHandle = getParentIfNecessary(mWindow, input);
+
+ mMirSurface = createMirSurface(mWindow, outputId, mParentWindowHandle, mPixelFormat, connection, surfaceEventCallback, this);
+ mEglSurface = eglCreateWindowSurface(mEglDisplay, config, nativeWindowFor(mMirSurface), nullptr);
+
+ mNeedsExposeCatchup = mir_surface_get_visibility(mMirSurface) == mir_surface_visibility_occluded;
+
+ // Window manager can give us a final size different from what we asked for
+ // so let's check what we ended up getting
+ MirSurfaceParameters parameters;
+ mir_surface_get_parameters(mMirSurface, &parameters);
+
+ auto geom = mWindow->geometry();
+ geom.setWidth(parameters.width);
+ geom.setHeight(parameters.height);
+
+ // Assume that the buffer size matches the surface size at creation time
+ mBufferSize = geom.size();
+ platformWindow->QPlatformWindow::setGeometry(geom);
+ QWindowSystemInterface::handleGeometryChange(mWindow, geom);
+
+ qCDebug(mirclient) << "Created surface with geometry:" << geom << "title:" << mWindow->title()
+ << "role:" << roleFor(mWindow);
+ qCDebug(mirclientGraphics)
+ << "Requested format:" << mWindow->requestedFormat()
+ << "\nActual format:" << mFormat
+ << "with associated Mir pixel format:" << mirPixelFormatToStr(mPixelFormat);
}
-void QMirClientSurface::setState(Qt::WindowState newState)
+UbuntuSurface::~UbuntuSurface()
{
- mir_wait_for(mir_surface_set_state(mMirSurface, qtWindowStateToMirSurfaceState(newState)));
- mWindowState = newState;
+ if (mEglSurface != EGL_NO_SURFACE)
+ eglDestroySurface(mEglDisplay, mEglSurface);
+ if (mMirSurface) {
+ mir_surface_release_sync(mMirSurface);
+ }
}
-void QMirClientSurface::setVisible(bool visible)
+void UbuntuSurface::updateGeometry(const QRect &newGeometry)
{
- if (mVisible == visible)
- return;
-
- mVisible = visible;
+ qCDebug(mirclient,"updateGeometry(window=%p, width=%d, height=%d)", mWindow,
+ newGeometry.width(), newGeometry.height());
- if (mVisible)
- updateSurface();
-
- // TODO: Use the new mir_surface_state_hidden state instead of mir_surface_state_minimized.
- // Will have to change qtmir and unity8 for that.
- const auto newState = visible ? qtWindowStateToMirSurfaceState(mWindowState) : mir_surface_state_minimized;
- mir_wait_for(mir_surface_set_state(mMirSurface, newState));
+ Spec spec;
+ if (isMovable(mWindow->type())) {
+ spec = Spec{makeSurfaceSpec(mWindow, mPixelFormat, mParentWindowHandle, mConnection)};
+ } else {
+ spec = Spec{mir_connection_create_spec_for_changes(mConnection)};
+ mir_surface_spec_set_width(spec.get(), newGeometry.width());
+ mir_surface_spec_set_height(spec.get(), newGeometry.height());
+ }
+ mir_surface_apply_spec(mMirSurface, spec.get());
}
-void QMirClientSurface::updateTitle(const QString& newTitle)
+void UbuntuSurface::updateTitle(const QString& newTitle)
{
const auto title = newTitle.toUtf8();
Spec spec{mir_connection_create_spec_for_changes(mConnection)};
@@ -399,14 +560,14 @@ void QMirClientSurface::updateTitle(const QString& newTitle)
mir_surface_apply_spec(mMirSurface, spec.get());
}
-void QMirClientSurface::setSizingConstraints(const QSize& minSize, const QSize& maxSize, const QSize& increment)
+void UbuntuSurface::setSizingConstraints(const QSize& minSize, const QSize& maxSize, const QSize& increment)
{
Spec spec{mir_connection_create_spec_for_changes(mConnection)};
::setSizingConstraints(spec.get(), minSize, maxSize, increment);
mir_surface_apply_spec(mMirSurface, spec.get());
}
-void QMirClientSurface::handleSurfaceResized(int width, int height)
+void UbuntuSurface::handleSurfaceResized(int width, int height)
{
QMutexLocker lock(&mTargetSizeMutex);
@@ -419,7 +580,7 @@ void QMirClientSurface::handleSurfaceResized(int width, int height)
mNeedsRepaint = mTargetSize.width() == width && mTargetSize.height() == height;
}
-int QMirClientSurface::needsRepaint() const
+int UbuntuSurface::needsRepaint() const
{
if (mNeedsRepaint) {
if (mTargetSize != mBufferSize) {
@@ -436,12 +597,26 @@ int QMirClientSurface::needsRepaint() const
return 0;
}
-void QMirClientSurface::onSwapBuffersDone()
+void UbuntuSurface::setState(MirSurfaceState state)
+{
+ mir_wait_for(mir_surface_set_state(mMirSurface, state));
+}
+
+void UbuntuSurface::setShellChrome(MirShellChrome chrome)
+{
+ if (chrome != mShellChrome) {
+ auto spec = Spec{mir_connection_create_spec_for_changes(mConnection)};
+ mir_surface_spec_set_shell_chrome(spec.get(), chrome);
+ mir_surface_apply_spec(mMirSurface, spec.get());
+
+ mShellChrome = chrome;
+ }
+}
+
+void UbuntuSurface::onSwapBuffersDone()
{
-#if !defined(QT_NO_DEBUG)
static int sFrameNumber = 0;
++sFrameNumber;
-#endif
EGLint eglSurfaceWidth = -1;
EGLint eglSurfaceHeight = -1;
@@ -452,7 +627,7 @@ void QMirClientSurface::onSwapBuffersDone()
if (validSize && (mBufferSize.width() != eglSurfaceWidth || mBufferSize.height() != eglSurfaceHeight)) {
- DLOG("[ubuntumirclient QPA] onSwapBuffersDone(window=%p) [%d] - size changed (%d, %d) => (%d, %d)",
+ qCDebug(mirclientBufferSwap, "onSwapBuffersDone(window=%p) [%d] - size changed (%d, %d) => (%d, %d)",
mWindow, sFrameNumber, mBufferSize.width(), mBufferSize.height(), eglSurfaceWidth, eglSurfaceHeight);
mBufferSize.rwidth() = eglSurfaceWidth;
@@ -464,23 +639,21 @@ void QMirClientSurface::onSwapBuffersDone()
mPlatformWindow->QPlatformWindow::setGeometry(newGeometry);
QWindowSystemInterface::handleGeometryChange(mWindow, newGeometry);
} else {
-#if 0
- DLOG("[ubuntumirclient QPA] onSwapBuffersDone(window=%p) [%d] - buffer size (%d,%d)",
+ qCDebug(mirclientBufferSwap, "onSwapBuffersDone(window=%p) [%d] - buffer size (%d,%d)",
mWindow, sFrameNumber, mBufferSize.width(), mBufferSize.height());
-#endif
}
}
-void QMirClientSurface::surfaceEventCallback(MirSurface *surface, const MirEvent *event, void* context)
+void UbuntuSurface::surfaceEventCallback(MirSurface *surface, const MirEvent *event, void* context)
{
Q_UNUSED(surface);
Q_ASSERT(context != nullptr);
- auto s = static_cast<QMirClientSurface *>(context);
+ auto s = static_cast<UbuntuSurface *>(context);
s->postEvent(event);
}
-void QMirClientSurface::postEvent(const MirEvent *event)
+void UbuntuSurface::postEvent(const MirEvent *event)
{
if (mir_event_type_resize == mir_event_get_type(event)) {
// TODO: The current event queue just accumulates all resize events;
@@ -490,7 +663,7 @@ void QMirClientSurface::postEvent(const MirEvent *event)
const auto resizeEvent = mir_event_get_resize_event(event);
const auto width = mir_resize_event_get_width(resizeEvent);
const auto height = mir_resize_event_get_height(resizeEvent);
- DLOG("[ubuntumirclient QPA] resizeEvent(window=%p, width=%d, height=%d)", mWindow, width, height);
+ qCDebug(mirclient, "resizeEvent(window=%p, width=%d, height=%d)", mWindow, width, height);
QMutexLocker lock(&mTargetSizeMutex);
mTargetSize.rwidth() = width;
@@ -500,44 +673,57 @@ void QMirClientSurface::postEvent(const MirEvent *event)
mInput->postEvent(mPlatformWindow, event);
}
-void QMirClientSurface::updateSurface()
+void UbuntuSurface::setSurfaceParent(MirSurface* parent)
{
- DLOG("[ubuntumirclient QPA] updateSurface(window=%p)", mWindow);
+ qCDebug(mirclient, "setSurfaceParent(window=%p)", mWindow);
- if (!mParented && mWindow->type() == Qt::Dialog) {
- // The dialog may have been parented after creation time
- // so morph it into a modal dialog
- auto parent = transientParentFor(mWindow);
- if (parent) {
- DLOG("[ubuntumirclient QPA] updateSurface(window=%p) dialog now parented", mWindow);
- mParented = true;
- Spec spec{mir_connection_create_spec_for_changes(mConnection)};
- mir_surface_spec_set_parent(spec.get(), parent->mirSurface());
- mir_surface_apply_spec(mMirSurface, spec.get());
- }
+ mParented = true;
+ Spec spec{mir_connection_create_spec_for_changes(mConnection)};
+ mir_surface_spec_set_parent(spec.get(), parent);
+ mir_surface_apply_spec(mMirSurface, spec.get());
+}
+
+QString UbuntuSurface::persistentSurfaceId()
+{
+ if (mPersistentIdStr.isEmpty()) {
+ MirPersistentId* mirPermaId = mir_surface_request_persistent_id_sync(mMirSurface);
+ mPersistentIdStr = mir_persistent_id_as_string(mirPermaId);
+ mir_persistent_id_release(mirPermaId);
}
+ return mPersistentIdStr;
}
-QMirClientWindow::QMirClientWindow(QWindow *w, const QSharedPointer<QMirClientClipboard> &clipboard, QMirClientScreen *screen,
- QMirClientInput *input, MirConnection *connection)
+QMirClientWindow::QMirClientWindow(QWindow *w, QMirClientInput *input, QMirClientNativeInterface *native,
+ QMirClientAppStateController *appState, EGLDisplay eglDisplay,
+ MirConnection *mirConnection, QMirClientDebugExtension *debugExt)
: QObject(nullptr)
, QPlatformWindow(w)
, mId(makeId())
- , mClipboard(clipboard)
- , mSurface(new QMirClientSurface{this, screen, input, connection})
+ , mWindowState(w->windowState())
+ , mWindowFlags(w->flags())
+ , mWindowVisible(false)
+ , mAppStateController(appState)
+ , mDebugExtention(debugExt)
+ , mNativeInterface(native)
+ , mSurface(new UbuntuSurface{this, eglDisplay, input, mirConnection})
+ , mScale(1.0)
+ , mFormFactor(mir_form_factor_unknown)
{
- DLOG("[ubuntumirclient QPA] QMirClientWindow(window=%p, screen=%p, input=%p, surf=%p)", w, screen, input, mSurface.get());
+ mWindowExposed = mSurface->mNeedsExposeCatchup == false;
+
+ qCDebug(mirclient, "QMirClientWindow(window=%p, screen=%p, input=%p, surf=%p) with title '%s', role: '%d'",
+ w, w->screen()->handle(), input, mSurface.get(), qPrintable(window()->title()), roleFor(window()));
}
QMirClientWindow::~QMirClientWindow()
{
- DLOG("[ubuntumirclient QPA] ~QMirClientWindow(window=%p)", this);
+ qCDebug(mirclient, "~QMirClientWindow(window=%p)", this);
}
void QMirClientWindow::handleSurfaceResized(int width, int height)
{
QMutexLocker lock(&mMutex);
- DLOG("[ubuntumirclient QPA] handleSurfaceResize(window=%p, width=%d, height=%d)", window(), width, height);
+ qCDebug(mirclient, "handleSurfaceResize(window=%p, size=(%dx%d)px", window(), width, height);
mSurface->handleSurfaceResized(width, height);
@@ -547,88 +733,140 @@ void QMirClientWindow::handleSurfaceResized(int width, int height)
// updated size but it still needs re-rendering so another redraw may be needed.
// A mir API to drop the currently held buffer would help here, so that we wouldn't have to redraw twice
auto const numRepaints = mSurface->needsRepaint();
- DLOG("[ubuntumirclient QPA] handleSurfaceResize(window=%p) redraw %d times", window(), numRepaints);
+ lock.unlock();
+ qCDebug(mirclient, "handleSurfaceResize(window=%p) redraw %d times", window(), numRepaints);
for (int i = 0; i < numRepaints; i++) {
- DLOG("[ubuntumirclient QPA] handleSurfaceResize(window=%p) repainting width=%d, height=%d", window(), geometry().size().width(), geometry().size().height());
+ qCDebug(mirclient, "handleSurfaceResize(window=%p) repainting size=(%dx%d)dp", window(), geometry().size().width(), geometry().size().height());
QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(), geometry().size()));
}
}
-void QMirClientWindow::handleSurfaceFocused()
+void QMirClientWindow::handleSurfaceExposeChange(bool exposed)
{
- DLOG("[ubuntumirclient QPA] handleSurfaceFocused(window=%p)", window());
+ QMutexLocker lock(&mMutex);
+ qCDebug(mirclient, "handleSurfaceExposeChange(window=%p, exposed=%s)", window(), exposed ? "true" : "false");
+
+ mSurface->mNeedsExposeCatchup = false;
+ if (mWindowExposed == exposed) return;
+ mWindowExposed = exposed;
- // System clipboard contents might have changed while this window was unfocused and without
- // this process getting notified about it because it might have been suspended (due to
- // application lifecycle policies), thus unable to listen to any changes notified through
- // D-Bus.
- // Therefore let's ensure we are up to date with the system clipboard now that we are getting
- // focused again.
- mClipboard->requestDBusClipboardContents();
+ lock.unlock();
+ QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(), geometry().size()));
+}
+
+void QMirClientWindow::handleSurfaceFocusChanged(bool focused)
+{
+ qCDebug(mirclient, "handleSurfaceFocusChanged(window=%p, focused=%d)", window(), focused);
+ if (focused) {
+ mAppStateController->setWindowFocused(true);
+ QWindowSystemInterface::handleWindowActivated(window(), Qt::ActiveWindowFocusReason);
+ } else {
+ mAppStateController->setWindowFocused(false);
+ }
+}
+
+void QMirClientWindow::handleSurfaceVisibilityChanged(bool visible)
+{
+ qCDebug(mirclient, "handleSurfaceVisibilityChanged(window=%p, visible=%d)", window(), visible);
+
+ if (mWindowVisible == visible) return;
+ mWindowVisible = visible;
+
+ QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(), geometry().size()));
+}
+
+void QMirClientWindow::handleSurfaceStateChanged(Qt::WindowState state)
+{
+ qCDebug(mirclient, "handleSurfaceStateChanged(window=%p, %s)", window(), qtWindowStateToStr(state));
+
+ if (mWindowState == state) return;
+ mWindowState = state;
+
+ QWindowSystemInterface::handleWindowStateChanged(window(), state);
}
void QMirClientWindow::setWindowState(Qt::WindowState state)
{
QMutexLocker lock(&mMutex);
- DLOG("[ubuntumirclient QPA] setWindowState(window=%p, %s)", this, qtWindowStateToStr(state));
- mSurface->setState(state);
+ qCDebug(mirclient, "setWindowState(window=%p, %s)", this, qtWindowStateToStr(state));
+
+ if (mWindowState == state) return;
+ mWindowState = state;
+
+ lock.unlock();
+ updateSurfaceState();
+}
+
+void QMirClientWindow::setWindowFlags(Qt::WindowFlags flags)
+{
+ QMutexLocker lock(&mMutex);
+ qCDebug(mirclient, "setWindowFlags(window=%p, 0x%x)", this, (int)flags);
- updatePanelHeightHack(state);
+ if (mWindowFlags == flags) return;
+ mWindowFlags = flags;
+
+ mSurface->setShellChrome(mWindowFlags & LowChromeWindowHint ? mir_shell_chrome_low : mir_shell_chrome_normal);
}
-/*
- FIXME: Mir does not let clients know the position of their windows in the virtual
- desktop space. So we have this ugly hack that assumes a phone situation where the
- window is always on the top-left corner, right below the indicators panel if not
- in fullscreen.
- */
-void QMirClientWindow::updatePanelHeightHack(Qt::WindowState state)
+QRect QMirClientWindow::geometry() const
{
- if (state == Qt::WindowFullScreen && geometry().y() != 0) {
- QRect newGeometry = geometry();
- newGeometry.setY(0);
- QPlatformWindow::setGeometry(newGeometry);
- QWindowSystemInterface::handleGeometryChange(window(), newGeometry);
- } else if (geometry().y() == 0) {
- QRect newGeometry = geometry();
- newGeometry.setY(panelHeight());
- QPlatformWindow::setGeometry(newGeometry);
- QWindowSystemInterface::handleGeometryChange(window(), newGeometry);
+ if (mDebugExtention) {
+ auto geom = QPlatformWindow::geometry();
+ geom.moveTopLeft(mDebugExtention->mapSurfacePointToScreen(mSurface->mirSurface(), QPoint(0,0)));
+ return geom;
+ } else {
+ return QPlatformWindow::geometry();
}
}
void QMirClientWindow::setGeometry(const QRect& rect)
{
QMutexLocker lock(&mMutex);
- DLOG("[ubuntumirclient QPA] setGeometry (window=%p, x=%d, y=%d, width=%d, height=%d)",
- window(), rect.x(), rect.y(), rect.width(), rect.height());
- //NOTE: mir surfaces cannot be moved by the client so ignore the topLeft coordinates
- const auto newSize = rect.size();
- auto newGeometry = geometry();
- newGeometry.setSize(newSize);
- QPlatformWindow::setGeometry(newGeometry);
+ if (window()->windowState() == Qt::WindowFullScreen || window()->windowState() == Qt::WindowMaximized) {
+ qCDebug(mirclient, "setGeometry(window=%p) - not resizing, window is maximized or fullscreen", window());
+ return;
+ }
+
+ qCDebug(mirclient, "setGeometry (window=%p, position=(%d, %d)dp, size=(%dx%d)dp)",
+ window(), rect.x(), rect.y(), rect.width(), rect.height());
+ // Immediately update internal geometry so Qt believes position updated
+ QRect newPosition(geometry());
+ newPosition.moveTo(rect.topLeft());
+ QPlatformWindow::setGeometry(newPosition);
- mSurface->resize(newSize);
+ mSurface->updateGeometry(rect);
+ // Note: don't call handleGeometryChange here, wait to see what Mir replies with.
}
void QMirClientWindow::setVisible(bool visible)
{
QMutexLocker lock(&mMutex);
- DLOG("[ubuntumirclient QPA] setVisible (window=%p, visible=%s)", window(), visible ? "true" : "false");
-
- mSurface->setVisible(visible);
- const QRect& exposeRect = visible ? QRect(QPoint(), geometry().size()) : QRect();
+ qCDebug(mirclient, "setVisible (window=%p, visible=%s)", window(), visible ? "true" : "false");
+
+ if (mWindowVisible == visible) return;
+ mWindowVisible = visible;
+
+ if (visible) {
+ if (!mSurface->hasParent() && window()->type() == Qt::Dialog) {
+ // The dialog may have been parented after creation time
+ // so morph it into a modal dialog
+ auto parent = transientParentFor(window());
+ if (parent) {
+ mSurface->setSurfaceParent(parent->mirSurface());
+ }
+ }
+ }
lock.unlock();
- QWindowSystemInterface::handleExposeEvent(window(), exposeRect);
- QWindowSystemInterface::flushWindowSystemEvents();
+ updateSurfaceState();
+ QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(), geometry().size()));
}
void QMirClientWindow::setWindowTitle(const QString& title)
{
QMutexLocker lock(&mMutex);
- DLOG("[ubuntumirclient QPA] setWindowTitle(window=%p) title=%s)", window(), title.toUtf8().constData());
+ qCDebug(mirclient, "setWindowTitle(window=%p) title=%s)", window(), title.toUtf8().constData());
mSurface->updateTitle(title);
}
@@ -636,13 +874,33 @@ void QMirClientWindow::propagateSizeHints()
{
QMutexLocker lock(&mMutex);
const auto win = window();
- DLOG("[ubuntumirclient QPA] propagateSizeHints(window=%p) min(%d,%d), max(%d,%d) increment(%d, %d)",
- win, win->minimumSize().width(), win->minimumSize().height(),
- win->maximumSize().width(), win->maximumSize().height(),
- win->sizeIncrement().width(), win->sizeIncrement().height());
+ qCDebug(mirclient, "propagateSizeHints(window=%p) min(%d,%d), max(%d,%d) increment(%d, %d)",
+ win, win->minimumSize().width(), win->minimumSize().height(),
+ win->maximumSize().width(), win->maximumSize().height(),
+ win->sizeIncrement().width(), win->sizeIncrement().height());
mSurface->setSizingConstraints(win->minimumSize(), win->maximumSize(), win->sizeIncrement());
}
+bool QMirClientWindow::isExposed() const
+{
+ // mNeedsExposeCatchup because we need to render a frame to get the expose surface event from mir.
+ return mWindowVisible && (mWindowExposed || (mSurface && mSurface->mNeedsExposeCatchup));
+}
+
+QSurfaceFormat QMirClientWindow::format() const
+{
+ return mSurface->format();
+}
+
+QPoint QMirClientWindow::mapToGlobal(const QPoint &pos) const
+{
+ if (mDebugExtention) {
+ return mDebugExtention->mapSurfacePointToScreen(mSurface->mirSurface(), pos);
+ } else {
+ return pos;
+ }
+}
+
void* QMirClientWindow::eglSurface() const
{
return mSurface->eglSurface();
@@ -662,4 +920,43 @@ void QMirClientWindow::onSwapBuffersDone()
{
QMutexLocker lock(&mMutex);
mSurface->onSwapBuffersDone();
+
+ if (mSurface->mNeedsExposeCatchup) {
+ mSurface->mNeedsExposeCatchup = false;
+ mWindowExposed = false;
+
+ lock.unlock();
+ QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(), geometry().size()));
+ }
+}
+
+void QMirClientWindow::handleScreenPropertiesChange(MirFormFactor formFactor, float scale)
+{
+ // Update the scale & form factor native-interface properties for the windows affected
+ // as there is no convenient way to emit signals for those custom properties on a QScreen
+ if (formFactor != mFormFactor) {
+ mFormFactor = formFactor;
+ Q_EMIT mNativeInterface->windowPropertyChanged(this, QStringLiteral("formFactor"));
+ }
+
+ if (!qFuzzyCompare(scale, mScale)) {
+ mScale = scale;
+ Q_EMIT mNativeInterface->windowPropertyChanged(this, QStringLiteral("scale"));
+ }
+}
+
+void QMirClientWindow::updateSurfaceState()
+{
+ QMutexLocker lock(&mMutex);
+ MirSurfaceState newState = mWindowVisible ? qtWindowStateToMirSurfaceState(mWindowState) :
+ mir_surface_state_hidden;
+ qCDebug(mirclient, "updateSurfaceState (window=%p, surfaceState=%s)", window(), mirSurfaceStateToStr(newState));
+ if (newState != mSurface->state()) {
+ mSurface->setState(newState);
+ }
+}
+
+QString QMirClientWindow::persistentSurfaceId()
+{
+ return mSurface->persistentSurfaceId();
}
diff --git a/src/plugins/platforms/mirclient/qmirclientwindow.h b/src/plugins/platforms/mirclient/qmirclientwindow.h
index 025976c130..324e7691ff 100644
--- a/src/plugins/platforms/mirclient/qmirclientwindow.h
+++ b/src/plugins/platforms/mirclient/qmirclientwindow.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2014-2015 Canonical, Ltd.
+** Copyright (C) 2014-2016 Canonical, Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
@@ -45,44 +45,74 @@
#include <QSharedPointer>
#include <QMutex>
+#include <mir_toolkit/common.h> // needed only for MirFormFactor enum
+
#include <memory>
-class QMirClientClipboard;
+#include <EGL/egl.h>
+
+class QMirClientAppStateController;
+class QMirClientDebugExtension;
+class QMirClientNativeInterface;
class QMirClientInput;
class QMirClientScreen;
-class QMirClientSurface;
-struct MirConnection;
+class UbuntuSurface;
struct MirSurface;
+class MirConnection;
class QMirClientWindow : public QObject, public QPlatformWindow
{
Q_OBJECT
public:
- QMirClientWindow(QWindow *w, const QSharedPointer<QMirClientClipboard> &clipboard, QMirClientScreen *screen,
- QMirClientInput *input, MirConnection *mirConnection);
+ QMirClientWindow(QWindow *w, QMirClientInput *input, QMirClientNativeInterface* native,
+ QMirClientAppStateController *appState, EGLDisplay eglDisplay,
+ MirConnection *mirConnection, QMirClientDebugExtension *debugExt);
virtual ~QMirClientWindow();
// QPlatformWindow methods.
WId winId() const override;
+ QRect geometry() const override;
void setGeometry(const QRect&) override;
void setWindowState(Qt::WindowState state) override;
+ void setWindowFlags(Qt::WindowFlags flags) override;
void setVisible(bool visible) override;
void setWindowTitle(const QString &title) override;
void propagateSizeHints() override;
+ bool isExposed() const override;
+
+ QPoint mapToGlobal(const QPoint &pos) const override;
+ QSurfaceFormat format() const override;
+
+ // Additional Window properties exposed by NativeInterface
+ MirFormFactor formFactor() const { return mFormFactor; }
+ float scale() const { return mScale; }
// New methods.
void *eglSurface() const;
MirSurface *mirSurface() const;
void handleSurfaceResized(int width, int height);
- void handleSurfaceFocused();
+ void handleSurfaceExposeChange(bool exposed);
+ void handleSurfaceFocusChanged(bool focused);
+ void handleSurfaceVisibilityChanged(bool visible);
+ void handleSurfaceStateChanged(Qt::WindowState state);
void onSwapBuffersDone();
+ void handleScreenPropertiesChange(MirFormFactor formFactor, float scale);
+ QString persistentSurfaceId();
private:
- void updatePanelHeightHack(Qt::WindowState);
+ void updateSurfaceState();
mutable QMutex mMutex;
const WId mId;
- const QSharedPointer<QMirClientClipboard> mClipboard;
- std::unique_ptr<QMirClientSurface> mSurface;
+ Qt::WindowState mWindowState;
+ Qt::WindowFlags mWindowFlags;
+ bool mWindowVisible;
+ bool mWindowExposed;
+ QMirClientAppStateController *mAppStateController;
+ QMirClientDebugExtension *mDebugExtention;
+ QMirClientNativeInterface *mNativeInterface;
+ std::unique_ptr<UbuntuSurface> mSurface;
+ float mScale;
+ MirFormFactor mFormFactor;
};
#endif // QMIRCLIENTWINDOW_H