summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Olav Tvete <paul.tvete@theqtcompany.com>2015-08-05 13:03:49 +0200
committerPaul Olav Tvete <paul.tvete@theqtcompany.com>2015-08-05 11:44:46 +0000
commit39592451178f3a53b514da2af4a47da5ae590a0a (patch)
tree115f1f8101b9fa0826c4c61fe5a1746a4f4c770e
parent72680ce7e2e32222078178596bf3793e6e848a36 (diff)
Initial import of Mir client platform plugin
Pure import from http://bazaar.launchpad.net/~phablet-team/qtubuntu/trunk/ Change-Id: I32fddc13dd718bf0b2b0711d00d55bebc0510f3d Reviewed-by: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@theqtcompany.com>
-rw-r--r--src/plugins/platforms/mirclient/backingstore.cpp127
-rw-r--r--src/plugins/platforms/mirclient/backingstore.h49
-rw-r--r--src/plugins/platforms/mirclient/clipboard.cpp284
-rw-r--r--src/plugins/platforms/mirclient/clipboard.h67
-rw-r--r--src/plugins/platforms/mirclient/glcontext.cpp135
-rw-r--r--src/plugins/platforms/mirclient/glcontext.h45
-rw-r--r--src/plugins/platforms/mirclient/input.cpp499
-rw-r--r--src/plugins/platforms/mirclient/input.h57
-rw-r--r--src/plugins/platforms/mirclient/integration.cpp243
-rw-r--r--src/plugins/platforms/mirclient/integration.h78
-rw-r--r--src/plugins/platforms/mirclient/logging.h39
-rw-r--r--src/plugins/platforms/mirclient/nativeinterface.cpp113
-rw-r--r--src/plugins/platforms/mirclient/nativeinterface.h45
-rw-r--r--src/plugins/platforms/mirclient/orientationchangeevent_p.h36
-rw-r--r--src/plugins/platforms/mirclient/platformservices.cpp51
-rw-r--r--src/plugins/platforms/mirclient/platformservices.h33
-rw-r--r--src/plugins/platforms/mirclient/plugin.cpp40
-rw-r--r--src/plugins/platforms/mirclient/plugin.h32
-rw-r--r--src/plugins/platforms/mirclient/screen.cpp275
-rw-r--r--src/plugins/platforms/mirclient/screen.h63
-rw-r--r--src/plugins/platforms/mirclient/theme.cpp43
-rw-r--r--src/plugins/platforms/mirclient/theme.h33
-rw-r--r--src/plugins/platforms/mirclient/ubuntumirclient.json3
-rw-r--r--src/plugins/platforms/mirclient/ubuntumirclient.pro48
-rw-r--r--src/plugins/platforms/mirclient/window.cpp449
-rw-r--r--src/plugins/platforms/mirclient/window.h59
26 files changed, 2946 insertions, 0 deletions
diff --git a/src/plugins/platforms/mirclient/backingstore.cpp b/src/plugins/platforms/mirclient/backingstore.cpp
new file mode 100644
index 0000000000..0efde3a2f7
--- /dev/null
+++ b/src/plugins/platforms/mirclient/backingstore.cpp
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2014 Canonical, Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License version 3, as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "backingstore.h"
+#include "logging.h"
+#include <QtGui/QOpenGLContext>
+#include <QtGui/QOpenGLTexture>
+#include <QtGui/QMatrix4x4>
+#include <QtGui/private/qopengltextureblitter_p.h>
+#include <QtGui/qopenglfunctions.h>
+
+UbuntuBackingStore::UbuntuBackingStore(QWindow* window)
+ : QPlatformBackingStore(window)
+ , mContext(new QOpenGLContext)
+ , mTexture(new QOpenGLTexture(QOpenGLTexture::Target2D))
+ , mBlitter(new QOpenGLTextureBlitter)
+{
+ mContext->setFormat(window->requestedFormat());
+ mContext->setScreen(window->screen());
+ mContext->create();
+
+ window->setSurfaceType(QSurface::OpenGLSurface);
+}
+
+UbuntuBackingStore::~UbuntuBackingStore()
+{
+}
+
+void UbuntuBackingStore::flush(QWindow* window, const QRegion& region, const QPoint& offset)
+{
+ Q_UNUSED(region);
+ Q_UNUSED(offset);
+ mContext->makeCurrent(window);
+ glViewport(0, 0, window->width(), window->height());
+
+ updateTexture();
+
+ if (!mBlitter->isCreated())
+ mBlitter->create();
+
+ mBlitter->bind();
+ mBlitter->setSwizzleRB(true);
+ mBlitter->blit(mTexture->textureId(), QMatrix4x4(), QOpenGLTextureBlitter::OriginTopLeft);
+ mBlitter->release();
+
+ mContext->swapBuffers(window);
+}
+
+void UbuntuBackingStore::updateTexture()
+{
+ if (mDirty.isNull())
+ return;
+
+ if (!mTexture->isCreated()) {
+ mTexture->setMinificationFilter(QOpenGLTexture::Nearest);
+ mTexture->setMagnificationFilter(QOpenGLTexture::Nearest);
+ mTexture->setWrapMode(QOpenGLTexture::ClampToEdge);
+ mTexture->setData(mImage, QOpenGLTexture::DontGenerateMipMaps);
+ mTexture->create();
+ }
+ mTexture->bind();
+
+ QRegion fixed;
+ QRect imageRect = mImage.rect();
+
+ /* Following code taken from QEGLPlatformBackingStore under the terms of the Lesser GPL v2.1 licence
+ * Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). */
+ Q_FOREACH (const QRect &rect, mDirty.rects()) {
+ // intersect with image rect to be sure
+ QRect r = imageRect & rect;
+
+ // if the rect is wide enough it is cheaper to just extend it instead of doing an image copy
+ if (r.width() >= imageRect.width() / 2) {
+ r.setX(0);
+ r.setWidth(imageRect.width());
+ }
+
+ fixed |= r;
+ }
+
+ Q_FOREACH (const QRect &rect, fixed.rects()) {
+ // if the sub-rect is full-width we can pass the image data directly to
+ // OpenGL instead of copying, since there is no gap between scanlines
+ if (rect.width() == imageRect.width()) {
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, rect.y(), rect.width(), rect.height(), GL_RGBA, GL_UNSIGNED_BYTE,
+ mImage.constScanLine(rect.y()));
+ } else {
+ glTexSubImage2D(GL_TEXTURE_2D, 0, rect.x(), rect.y(), rect.width(), rect.height(), GL_RGBA, GL_UNSIGNED_BYTE,
+ mImage.copy(rect).constBits());
+ }
+ }
+ /* End of code taken from QEGLPlatformBackingStore */
+
+ mDirty = QRegion();
+}
+
+
+void UbuntuBackingStore::beginPaint(const QRegion& region)
+{
+ mDirty |= region;
+}
+
+void UbuntuBackingStore::resize(const QSize& size, const QRegion& /*staticContents*/)
+{
+ mImage = QImage(size, QImage::Format_RGB32);
+
+ if (mTexture->isCreated())
+ mTexture->destroy();
+}
+
+QPaintDevice* UbuntuBackingStore::paintDevice()
+{
+ return &mImage;
+}
diff --git a/src/plugins/platforms/mirclient/backingstore.h b/src/plugins/platforms/mirclient/backingstore.h
new file mode 100644
index 0000000000..5774f13fd8
--- /dev/null
+++ b/src/plugins/platforms/mirclient/backingstore.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2014 Canonical, Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License version 3, as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef UBUNTU_BACKING_STORE_H
+#define UBUNTU_BACKING_STORE_H
+
+#include <qpa/qplatformbackingstore.h>
+
+class QOpenGLContext;
+class QOpenGLTexture;
+class QOpenGLTextureBlitter;
+
+class UbuntuBackingStore : public QPlatformBackingStore
+{
+public:
+ UbuntuBackingStore(QWindow* window);
+ virtual ~UbuntuBackingStore();
+
+ // QPlatformBackingStore methods.
+ void beginPaint(const QRegion&) override;
+ void flush(QWindow* window, const QRegion& region, const QPoint& offset) override;
+ void resize(const QSize& size, const QRegion& staticContents) override;
+ QPaintDevice* paintDevice() override;
+
+protected:
+ void updateTexture();
+
+private:
+ QScopedPointer<QOpenGLContext> mContext;
+ QScopedPointer<QOpenGLTexture> mTexture;
+ QScopedPointer<QOpenGLTextureBlitter> mBlitter;
+ QImage mImage;
+ QRegion mDirty;
+};
+
+#endif // UBUNTU_BACKING_STORE_H
diff --git a/src/plugins/platforms/mirclient/clipboard.cpp b/src/plugins/platforms/mirclient/clipboard.cpp
new file mode 100644
index 0000000000..7eaca4a5a4
--- /dev/null
+++ b/src/plugins/platforms/mirclient/clipboard.cpp
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2014 Canonical, Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License version 3, as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "clipboard.h"
+
+#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)
+
+namespace {
+
+const int maxFormatsCount = 16;
+const int maxBufferSize = 4 * 1024 * 1024; // 4 Mb
+
+}
+
+UbuntuClipboard::UbuntuClipboard()
+ : mMimeData(new QMimeData)
+ , mIsOutdated(true)
+ , mUpdatesDisabled(false)
+ , mDBusSetupDone(false)
+{
+}
+
+UbuntuClipboard::~UbuntuClipboard()
+{
+ delete mMimeData;
+}
+
+void UbuntuClipboard::requestDBusClipboardContents()
+{
+ if (!mDBusSetupDone) {
+ setupDBus();
+ }
+
+ if (!mPendingGetContentsCall.isNull())
+ return;
+
+ QDBusPendingCall pendingCall = mDBusClipboard->asyncCall("GetContents");
+
+ mPendingGetContentsCall = new QDBusPendingCallWatcher(pendingCall, this);
+
+ QObject::connect(mPendingGetContentsCall.data(), SIGNAL(finished(QDBusPendingCallWatcher*)),
+ this, SLOT(onDBusClipboardGetContentsFinished(QDBusPendingCallWatcher*)));
+}
+
+void UbuntuClipboard::onDBusClipboardGetContentsFinished(QDBusPendingCallWatcher* call)
+{
+ Q_ASSERT(call == mPendingGetContentsCall.data());
+
+ QDBusPendingReply<QByteArray> reply = *call;
+ if (reply.isError()) {
+ qCritical("UbuntuClipboard - 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();
+}
+
+void UbuntuClipboard::onDBusClipboardSetContentsFinished(QDBusPendingCallWatcher *call)
+{
+ QDBusPendingReply<void> reply = *call;
+ if (reply.isError()) {
+ qCritical("UbuntuClipboard - 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 UbuntuClipboard::updateMimeData(const QByteArray &serializedMimeData)
+{
+ if (mUpdatesDisabled)
+ return;
+
+ QMimeData *newMimeData = deserializeMimeData(serializedMimeData);
+ if (newMimeData) {
+ delete mMimeData;
+ mMimeData = newMimeData;
+ mIsOutdated = false;
+ emitChanged(QClipboard::Clipboard);
+ } else {
+ qWarning("UbuntuClipboard - Got invalid serialized mime data. Ignoring it.");
+ }
+}
+
+void UbuntuClipboard::setupDBus()
+{
+ QDBusConnection dbusConnection = QDBusConnection::sessionBus();
+
+ bool ok = dbusConnection.connect(
+ "com.canonical.QtMir",
+ "/com/canonical/QtMir/Clipboard",
+ "com.canonical.QtMir.Clipboard",
+ "ContentsChanged",
+ this, SLOT(updateMimeData(QByteArray)));
+ if (!ok) {
+ qCritical("UbuntuClipboard - Failed to connect to ContentsChanged signal form the D-Bus system clipboard.");
+ }
+
+ mDBusClipboard = new QDBusInterface("com.canonical.QtMir",
+ "/com/canonical/QtMir/Clipboard",
+ "com.canonical.QtMir.Clipboard",
+ dbusConnection);
+
+ mDBusSetupDone = true;
+}
+
+QByteArray UbuntuClipboard::serializeMimeData(QMimeData *mimeData) const
+{
+ 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 int formatOffset = offset;
+ const int formatSize = formats[i].size();
+ const int dataOffset = offset + formatSize;
+ const int dataSize = mimeData->data(formats[i]).size();
+ memcpy(&buffer[formatOffset], formats[i].toLatin1().data(), formatSize);
+ memcpy(&buffer[dataOffset], mimeData->data(formats[i]).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("UbuntuClipboard: 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;
+}
+
+QMimeData *UbuntuClipboard::deserializeMimeData(const QByteArray &serializedMimeData) 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;
+}
+
+QMimeData* UbuntuClipboard::mimeData(QClipboard::Mode mode)
+{
+ if (mode != QClipboard::Clipboard)
+ return nullptr;
+
+ if (mIsOutdated && mPendingGetContentsCall.isNull()) {
+ requestDBusClipboardContents();
+ }
+
+ // 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 UbuntuClipboard::setMimeData(QMimeData* mimeData, QClipboard::Mode mode)
+{
+ if (mode != QClipboard::Clipboard)
+ 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();
+ }
+
+ QByteArray serializedMimeData = serializeMimeData(mimeData);
+ if (!serializedMimeData.isEmpty()) {
+ setDBusClipboardContents(serializedMimeData);
+ }
+
+ mMimeData = mimeData;
+ emitChanged(QClipboard::Clipboard);
+}
+
+bool UbuntuClipboard::supportsMode(QClipboard::Mode mode) const
+{
+ return mode == QClipboard::Clipboard;
+}
+
+bool UbuntuClipboard::ownsMode(QClipboard::Mode mode) const
+{
+ Q_UNUSED(mode);
+ return false;
+}
+
+void UbuntuClipboard::setDBusClipboardContents(const QByteArray &clipboardContents)
+{
+ 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();
+ }
+
+ QDBusPendingCall pendingCall = mDBusClipboard->asyncCall("SetContents", clipboardContents);
+
+ mPendingSetContentsCall = new QDBusPendingCallWatcher(pendingCall, this);
+
+ QObject::connect(mPendingSetContentsCall.data(), SIGNAL(finished(QDBusPendingCallWatcher*)),
+ this, SLOT(onDBusClipboardSetContentsFinished(QDBusPendingCallWatcher*)));
+}
diff --git a/src/plugins/platforms/mirclient/clipboard.h b/src/plugins/platforms/mirclient/clipboard.h
new file mode 100644
index 0000000000..f676ade9a9
--- /dev/null
+++ b/src/plugins/platforms/mirclient/clipboard.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2014 Canonical, Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License version 3, as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef UBUNTU_CLIPBOARD_H
+#define UBUNTU_CLIPBOARD_H
+
+#include <qpa/qplatformclipboard.h>
+
+#include <QMimeData>
+#include <QPointer>
+class QDBusInterface;
+class QDBusPendingCallWatcher;
+
+class UbuntuClipboard : public QObject, public QPlatformClipboard
+{
+ Q_OBJECT
+public:
+ UbuntuClipboard();
+ virtual ~UbuntuClipboard();
+
+ // QPlatformClipboard methods.
+ QMimeData* mimeData(QClipboard::Mode mode = QClipboard::Clipboard) override;
+ void setMimeData(QMimeData* data, QClipboard::Mode mode = QClipboard::Clipboard) override;
+ 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);
+
+private:
+ void setupDBus();
+
+ QByteArray serializeMimeData(QMimeData *mimeData) const;
+ QMimeData *deserializeMimeData(const QByteArray &serializedMimeData) const;
+
+ void setDBusClipboardContents(const QByteArray &clipboardContents);
+
+ QMimeData *mMimeData;
+ bool mIsOutdated;
+
+ QPointer<QDBusInterface> mDBusClipboard;
+
+ QPointer<QDBusPendingCallWatcher> mPendingGetContentsCall;
+ QPointer<QDBusPendingCallWatcher> mPendingSetContentsCall;
+
+ bool mUpdatesDisabled;
+ bool mDBusSetupDone;
+};
+
+#endif // UBUNTU_CLIPBOARD_H
diff --git a/src/plugins/platforms/mirclient/glcontext.cpp b/src/plugins/platforms/mirclient/glcontext.cpp
new file mode 100644
index 0000000000..ce1ecf0074
--- /dev/null
+++ b/src/plugins/platforms/mirclient/glcontext.cpp
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2014 Canonical, Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License version 3, as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "glcontext.h"
+#include "window.h"
+#include "logging.h"
+#include <QtPlatformSupport/private/qeglconvenience_p.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()
+{
+#ifdef QTUBUNTU_USE_OPENGL
+ return EGL_OPENGL_API;
+#else
+ return EGL_OPENGL_ES_API;
+#endif
+}
+
+UbuntuOpenGLContext::UbuntuOpenGLContext(UbuntuScreen* screen, UbuntuOpenGLContext* share)
+{
+ 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);
+}
+
+UbuntuOpenGLContext::~UbuntuOpenGLContext()
+{
+ ASSERT(eglDestroyContext(mEglDisplay, mEglContext) == EGL_TRUE);
+}
+
+bool UbuntuOpenGLContext::makeCurrent(QPlatformSurface* surface)
+{
+ DASSERT(surface->surface()->surfaceType() == QSurface::OpenGLSurface);
+ EGLSurface eglSurface = static_cast<UbuntuWindow*>(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
+ return true;
+}
+
+void UbuntuOpenGLContext::doneCurrent()
+{
+#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
+}
+
+void UbuntuOpenGLContext::swapBuffers(QPlatformSurface* surface)
+{
+ UbuntuWindow *ubuntuWindow = static_cast<UbuntuWindow*>(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
+
+ // "Technique" copied from mir, in examples/eglapp.c around line 96
+ EGLint newBufferWidth = -1;
+ EGLint newBufferHeight = -1;
+ /*
+ * Querying the surface (actually the current buffer) dimensions here is
+ * the only truly safe way to be sure that the dimensions we think we
+ * have are those of the buffer being rendered to. But this should be
+ * improved in future; https://bugs.launchpad.net/mir/+bug/1194384
+ */
+ eglQuerySurface(mEglDisplay, eglSurface, EGL_WIDTH, &newBufferWidth);
+ eglQuerySurface(mEglDisplay, eglSurface, EGL_HEIGHT, &newBufferHeight);
+
+ ubuntuWindow->onBuffersSwapped_threadSafe(newBufferWidth, newBufferHeight);
+}
+
+void (*UbuntuOpenGLContext::getProcAddress(const QByteArray& procName)) ()
+{
+#if defined(QT_NO_DEBUG)
+ eglBindAPI(api_in_use());
+#else
+ ASSERT(eglBindAPI(api_in_use()) == EGL_TRUE);
+#endif
+ return eglGetProcAddress(procName.constData());
+}
diff --git a/src/plugins/platforms/mirclient/glcontext.h b/src/plugins/platforms/mirclient/glcontext.h
new file mode 100644
index 0000000000..4ad8c1cde7
--- /dev/null
+++ b/src/plugins/platforms/mirclient/glcontext.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2014 Canonical, Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License version 3, as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef UBUNTU_OPENGL_CONTEXT_H
+#define UBUNTU_OPENGL_CONTEXT_H
+
+#include <qpa/qplatformopenglcontext.h>
+#include "screen.h"
+
+class UbuntuOpenGLContext : public QPlatformOpenGLContext
+{
+public:
+ UbuntuOpenGLContext(UbuntuScreen* screen, UbuntuOpenGLContext* share);
+ virtual ~UbuntuOpenGLContext();
+
+ // 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; }
+ void (*getProcAddress(const QByteArray& procName)) ();
+
+ EGLContext eglContext() const { return mEglContext; }
+
+private:
+ UbuntuScreen* mScreen;
+ EGLContext mEglContext;
+ EGLDisplay mEglDisplay;
+};
+
+#endif // UBUNTU_OPENGL_CONTEXT_H
diff --git a/src/plugins/platforms/mirclient/input.cpp b/src/plugins/platforms/mirclient/input.cpp
new file mode 100644
index 0000000000..97da21ef25
--- /dev/null
+++ b/src/plugins/platforms/mirclient/input.cpp
@@ -0,0 +1,499 @@
+/*
+ * Copyright (C) 2014-2015 Canonical, Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License version 3, as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+// Local
+#include "input.h"
+#include "integration.h"
+#include "nativeinterface.h"
+#include "screen.h"
+#include "window.h"
+#include "logging.h"
+#include "orientationchangeevent_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 <qpa/qplatforminputcontext.h>
+#include <qpa/qwindowsysteminterface.h>
+
+#include <xkbcommon/xkbcommon.h>
+#include <xkbcommon/xkbcommon-keysyms.h>
+
+#include <mir_toolkit/mir_client_library.h>
+
+#define LOG_EVENTS 0
+
+// XKB Keysyms which do not map directly to Qt types (i.e. Unicode points)
+static const uint32_t KeyTable[] = {
+ XKB_KEY_Escape, Qt::Key_Escape,
+ XKB_KEY_Tab, Qt::Key_Tab,
+ XKB_KEY_ISO_Left_Tab, Qt::Key_Backtab,
+ XKB_KEY_BackSpace, Qt::Key_Backspace,
+ XKB_KEY_Return, Qt::Key_Return,
+ XKB_KEY_Insert, Qt::Key_Insert,
+ XKB_KEY_Delete, Qt::Key_Delete,
+ XKB_KEY_Clear, Qt::Key_Delete,
+ XKB_KEY_Pause, Qt::Key_Pause,
+ XKB_KEY_Print, Qt::Key_Print,
+
+ XKB_KEY_Home, Qt::Key_Home,
+ XKB_KEY_End, Qt::Key_End,
+ XKB_KEY_Left, Qt::Key_Left,
+ XKB_KEY_Up, Qt::Key_Up,
+ XKB_KEY_Right, Qt::Key_Right,
+ XKB_KEY_Down, Qt::Key_Down,
+ XKB_KEY_Prior, Qt::Key_PageUp,
+ XKB_KEY_Next, Qt::Key_PageDown,
+
+ XKB_KEY_Shift_L, Qt::Key_Shift,
+ XKB_KEY_Shift_R, Qt::Key_Shift,
+ XKB_KEY_Shift_Lock, Qt::Key_Shift,
+ XKB_KEY_Control_L, Qt::Key_Control,
+ XKB_KEY_Control_R, Qt::Key_Control,
+ XKB_KEY_Meta_L, Qt::Key_Meta,
+ XKB_KEY_Meta_R, Qt::Key_Meta,
+ XKB_KEY_Alt_L, Qt::Key_Alt,
+ XKB_KEY_Alt_R, Qt::Key_Alt,
+ XKB_KEY_Caps_Lock, Qt::Key_CapsLock,
+ XKB_KEY_Num_Lock, Qt::Key_NumLock,
+ XKB_KEY_Scroll_Lock, Qt::Key_ScrollLock,
+ XKB_KEY_Super_L, Qt::Key_Super_L,
+ XKB_KEY_Super_R, Qt::Key_Super_R,
+ XKB_KEY_Menu, Qt::Key_Menu,
+ XKB_KEY_Hyper_L, Qt::Key_Hyper_L,
+ XKB_KEY_Hyper_R, Qt::Key_Hyper_R,
+ XKB_KEY_Help, Qt::Key_Help,
+
+ XKB_KEY_KP_Space, Qt::Key_Space,
+ XKB_KEY_KP_Tab, Qt::Key_Tab,
+ XKB_KEY_KP_Enter, Qt::Key_Enter,
+ XKB_KEY_KP_Home, Qt::Key_Home,
+ XKB_KEY_KP_Left, Qt::Key_Left,
+ XKB_KEY_KP_Up, Qt::Key_Up,
+ XKB_KEY_KP_Right, Qt::Key_Right,
+ XKB_KEY_KP_Down, Qt::Key_Down,
+ XKB_KEY_KP_Prior, Qt::Key_PageUp,
+ XKB_KEY_KP_Next, Qt::Key_PageDown,
+ XKB_KEY_KP_End, Qt::Key_End,
+ XKB_KEY_KP_Begin, Qt::Key_Clear,
+ XKB_KEY_KP_Insert, Qt::Key_Insert,
+ XKB_KEY_KP_Delete, Qt::Key_Delete,
+ XKB_KEY_KP_Equal, Qt::Key_Equal,
+ XKB_KEY_KP_Multiply, Qt::Key_Asterisk,
+ XKB_KEY_KP_Add, Qt::Key_Plus,
+ XKB_KEY_KP_Separator, Qt::Key_Comma,
+ XKB_KEY_KP_Subtract, Qt::Key_Minus,
+ XKB_KEY_KP_Decimal, Qt::Key_Period,
+ XKB_KEY_KP_Divide, Qt::Key_Slash,
+
+ XKB_KEY_ISO_Level3_Shift, Qt::Key_AltGr,
+ XKB_KEY_Multi_key, Qt::Key_Multi_key,
+ XKB_KEY_Codeinput, Qt::Key_Codeinput,
+ XKB_KEY_SingleCandidate, Qt::Key_SingleCandidate,
+ XKB_KEY_MultipleCandidate, Qt::Key_MultipleCandidate,
+ XKB_KEY_PreviousCandidate, Qt::Key_PreviousCandidate,
+
+ XKB_KEY_Mode_switch, Qt::Key_Mode_switch,
+ XKB_KEY_script_switch, Qt::Key_Mode_switch,
+ XKB_KEY_XF86AudioRaiseVolume, Qt::Key_VolumeUp,
+ XKB_KEY_XF86AudioLowerVolume, Qt::Key_VolumeDown,
+ XKB_KEY_XF86PowerOff, Qt::Key_PowerOff,
+ XKB_KEY_XF86PowerDown, Qt::Key_PowerDown,
+
+ 0, 0
+};
+
+class UbuntuEvent : public QEvent
+{
+public:
+ UbuntuEvent(UbuntuWindow* window, const MirEvent *event, QEvent::Type type)
+ : QEvent(type), window(window) {
+ nativeEvent = mir_event_ref(event);
+ }
+ ~UbuntuEvent()
+ {
+ mir_event_unref(nativeEvent);
+ }
+
+ QPointer<UbuntuWindow> window;
+ const MirEvent *nativeEvent;
+};
+
+UbuntuInput::UbuntuInput(UbuntuClientIntegration* integration)
+ : QObject(nullptr)
+ , mIntegration(integration)
+ , mEventFilterType(static_cast<UbuntuNativeInterface*>(
+ integration->nativeInterface())->genericEventFilterType())
+ , mEventType(static_cast<QEvent::Type>(QEvent::registerEventType()))
+{
+ // Initialize touch device.
+ mTouchDevice = new QTouchDevice;
+ mTouchDevice->setType(QTouchDevice::TouchScreen);
+ mTouchDevice->setCapabilities(
+ QTouchDevice::Position | QTouchDevice::Area | QTouchDevice::Pressure |
+ QTouchDevice::NormalizedPosition);
+ QWindowSystemInterface::registerTouchDevice(mTouchDevice);
+}
+
+UbuntuInput::~UbuntuInput()
+{
+ // 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";
+ case mir_event_type_motion:
+ return "mir_event_type_motion";
+ case mir_event_type_surface:
+ return "mir_event_type_surface";
+ case mir_event_type_resize:
+ return "mir_event_type_resize";
+ case mir_event_type_prompt_session_state_change:
+ return "mir_event_type_prompt_session_state_change";
+ case mir_event_type_orientation:
+ return "mir_event_type_orientation";
+ case mir_event_type_close_surface:
+ return "mir_event_type_close_surface";
+ case mir_event_type_input:
+ return "mir_event_type_input";
+ default:
+ DLOG("Invalid event type %d", t);
+ return "invalid";
+ }
+}
+#endif // LOG_EVENTS != 0
+
+void UbuntuInput::customEvent(QEvent* event)
+{
+ DASSERT(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.";
+ return;
+ }
+
+ // Event filtering.
+ long result;
+ if (QWindowSystemInterface::handleNativeEvent(
+ ubuntuEvent->window->window(), mEventFilterType,
+ const_cast<void *>(static_cast<const void *>(nativeEvent)), &result) == true) {
+ DLOG("event filtered out by native interface");
+ return;
+ }
+
+ #if (LOG_EVENTS != 0)
+ LOG("UbuntuInput::customEvent(type=%s)", nativeEventTypeToStr(mir_event_get_type(nativeEvent)));
+ #endif
+
+ // Event dispatching.
+ switch (mir_event_get_type(nativeEvent))
+ {
+ case mir_event_type_input:
+ dispatchInputEvent(ubuntuEvent->window->window(), mir_event_get_input_event(nativeEvent));
+ 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));
+
+ ubuntuEvent->window->handleSurfaceResize(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) {
+ ubuntuEvent->window->handleSurfaceFocusChange(mir_surface_event_get_attribute_value(surfaceEvent) ==
+ mir_surface_focused);
+ }
+ break;
+ }
+ case mir_event_type_orientation:
+ dispatchOrientationEvent(ubuntuEvent->window->window(), mir_event_get_orientation_event(nativeEvent));
+ break;
+ case mir_event_type_close_surface:
+ QWindowSystemInterface::handleCloseEvent(ubuntuEvent->window->window());
+ break;
+ default:
+ DLOG("unhandled event type: %d", static_cast<int>(mir_event_get_type(nativeEvent)));
+ }
+}
+
+void UbuntuInput::postEvent(UbuntuWindow *platformWindow, const MirEvent *event)
+{
+ QWindow *window = platformWindow->window();
+
+ QCoreApplication::postEvent(this, new UbuntuEvent(
+ platformWindow, event, mEventType));
+
+ if ((window->flags().testFlag(Qt::WindowTransparentForInput)) && window->parent()) {
+ QCoreApplication::postEvent(this, new UbuntuEvent(
+ static_cast<UbuntuWindow*>(platformWindow->QPlatformWindow::parent()),
+ event, mEventType));
+ }
+}
+
+void UbuntuInput::dispatchInputEvent(QWindow *window, const MirInputEvent *ev)
+{
+ switch (mir_input_event_get_type(ev))
+ {
+ case mir_input_event_type_key:
+ dispatchKeyEvent(window, ev);
+ break;
+ case mir_input_event_type_touch:
+ dispatchTouchEvent(window, ev);
+ break;
+ case mir_input_event_type_pointer:
+ dispatchPointerEvent(window, ev);
+ break;
+ default:
+ break;
+ }
+}
+
+void UbuntuInput::dispatchTouchEvent(QWindow *window, const MirInputEvent *ev)
+{
+ const MirTouchEvent *tev = mir_input_event_get_touch_event(ev);
+
+ // FIXME(loicm) Max pressure is device specific. That one is for the Samsung Galaxy Nexus. That
+ // needs to be fixed as soon as the compat input lib adds query support.
+ const float kMaxPressure = 1.28;
+ const QRect kWindowGeometry = window->geometry();
+ QList<QWindowSystemInterface::TouchPoint> touchPoints;
+
+
+ // TODO: Is it worth setting the Qt::TouchPointStationary ones? Currently they are left
+ // as Qt::TouchPointMoved
+ const unsigned int kPointerCount = mir_touch_event_point_count(tev);
+ for (unsigned int i = 0; i < kPointerCount; ++i) {
+ QWindowSystemInterface::TouchPoint touchPoint;
+
+ const float kX = mir_touch_event_axis_value(tev, i, mir_touch_axis_x) + kWindowGeometry.x();
+ const float kY = mir_touch_event_axis_value(tev, i, mir_touch_axis_y) + kWindowGeometry.y(); // see bug lp:1346633 workaround comments elsewhere
+ const float kW = mir_touch_event_axis_value(tev, i, mir_touch_axis_touch_major);
+ const float kH = mir_touch_event_axis_value(tev, i, mir_touch_axis_touch_minor);
+ const float kP = mir_touch_event_axis_value(tev, i, mir_touch_axis_pressure);
+ touchPoint.id = mir_touch_event_id(tev, i);
+ touchPoint.normalPosition = QPointF(kX / kWindowGeometry.width(), kY / kWindowGeometry.height());
+ touchPoint.area = QRectF(kX - (kW / 2.0), kY - (kH / 2.0), kW, kH);
+ touchPoint.pressure = kP / kMaxPressure;
+
+ MirTouchAction touch_action = mir_touch_event_action(tev, i);
+ switch (touch_action)
+ {
+ case mir_touch_action_down:
+ 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;
+ }
+
+ touchPoints.append(touchPoint);
+ }
+
+ ulong timestamp = mir_input_event_get_event_time(ev) / 1000000;
+ QWindowSystemInterface::handleTouchEvent(window, timestamp,
+ mTouchDevice, touchPoints);
+}
+
+static uint32_t translateKeysym(uint32_t sym, char *string, size_t size)
+{
+ Q_UNUSED(size);
+ string[0] = '\0';
+
+ 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];
+ }
+
+ string[0] = sym;
+ string[1] = '\0';
+ return toupper(sym);
+}
+
+namespace
+{
+Qt::KeyboardModifiers qt_modifiers_from_mir(MirInputEventModifiers modifiers)
+{
+ Qt::KeyboardModifiers q_modifiers = Qt::NoModifier;
+ if (modifiers & mir_input_event_modifier_shift) {
+ q_modifiers |= Qt::ShiftModifier;
+ }
+ if (modifiers & mir_input_event_modifier_ctrl) {
+ q_modifiers |= Qt::ControlModifier;
+ }
+ if (modifiers & mir_input_event_modifier_alt) {
+ q_modifiers |= Qt::AltModifier;
+ }
+ if (modifiers & mir_input_event_modifier_meta) {
+ q_modifiers |= Qt::MetaModifier;
+ }
+ return q_modifiers;
+}
+}
+
+void UbuntuInput::dispatchKeyEvent(QWindow *window, const MirInputEvent *event)
+{
+ const MirKeyboardEvent *key_event = mir_input_event_get_keyboard_event(event);
+
+ ulong timestamp = mir_input_event_get_event_time(event) / 1000000;
+ xkb_keysym_t xk_sym = mir_keyboard_event_key_code(key_event);
+
+ // Key modifier and unicode index mapping.
+ auto modifiers = qt_modifiers_from_mir(mir_keyboard_event_modifiers(key_event));
+
+ MirKeyboardAction action = mir_keyboard_event_action(key_event);
+ QEvent::Type keyType = action == mir_keyboard_action_up
+ ? QEvent::KeyRelease : QEvent::KeyPress;
+
+ char s[2];
+ int sym = translateKeysym(xk_sym, s, sizeof(s));
+ QString text = QString::fromLatin1(s);
+
+ 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.setTimestamp(timestamp);
+ if (context->filterEvent(&qKeyEvent)) {
+ DLOG("key event filtered out by input context");
+ return;
+ }
+ }
+
+ QWindowSystemInterface::handleKeyEvent(window, timestamp, keyType, sym, modifiers, text, is_auto_rep);
+}
+
+namespace
+{
+Qt::MouseButtons extract_buttons(const MirPointerEvent *pev)
+{
+ Qt::MouseButtons buttons = Qt::NoButton;
+ if (mir_pointer_event_button_state(pev, mir_pointer_button_primary))
+ buttons |= Qt::LeftButton;
+ if (mir_pointer_event_button_state(pev, mir_pointer_button_secondary))
+ buttons |= Qt::RightButton;
+ if (mir_pointer_event_button_state(pev, mir_pointer_button_tertiary))
+ buttons |= Qt::MidButton;
+
+ // TODO: Should mir back and forward buttons exist?
+ // should they be Qt::X button 1 and 2?
+ return buttons;
+}
+}
+
+void UbuntuInput::dispatchPointerEvent(QWindow *window, const MirInputEvent *ev)
+{
+ auto timestamp = mir_input_event_get_event_time(ev) / 1000000;
+
+ auto pev = mir_input_event_get_pointer_event(ev);
+ auto modifiers = qt_modifiers_from_mir(mir_pointer_event_modifiers(pev));
+ auto buttons = extract_buttons(pev);
+
+ auto local_point = QPointF(mir_pointer_event_axis_value(pev, mir_pointer_axis_x),
+ mir_pointer_event_axis_value(pev, mir_pointer_axis_y));
+
+ QWindowSystemInterface::handleMouseEvent(window, timestamp, local_point, local_point /* Should we omit global point instead? */,
+ buttons, modifiers);
+}
+
+#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!";
+ }
+}
+#endif
+
+void UbuntuInput::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
+
+ if (!window->screen()) {
+ DLOG("Window has no associated screen, dropping orientation event");
+ return;
+ }
+
+ QOrientationReading::Orientation orientation;
+ switch (mir_orientation) {
+ case mir_orientation_normal:
+ orientation = QOrientationReading::TopUp;
+ break;
+ case mir_orientation_left:
+ orientation = QOrientationReading::LeftUp;
+ break;
+ case mir_orientation_inverted:
+ orientation = QOrientationReading::TopDown;
+ break;
+ case mir_orientation_right:
+ orientation = QOrientationReading::RightUp;
+ break;
+ default:
+ DLOG("No such orientation %d", mir_orientation);
+ return;
+ }
+
+ // Dispatch orientation event to [Platform]Screen, as that is where Qt reads it. Screen will handle
+ // notifying Qt of the actual orientation change - done to prevent multiple Windows each creating
+ // an identical orientation change event and passing it directly to Qt.
+ // [Platform]Screen can also factor in the native orientation.
+ QCoreApplication::postEvent(static_cast<UbuntuScreen*>(window->screen()->handle()),
+ new OrientationChangeEvent(OrientationChangeEvent::mType, orientation));
+}
+
diff --git a/src/plugins/platforms/mirclient/input.h b/src/plugins/platforms/mirclient/input.h
new file mode 100644
index 0000000000..6c7aa97fe2
--- /dev/null
+++ b/src/plugins/platforms/mirclient/input.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2014-2015 Canonical, Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License version 3, as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef UBUNTU_INPUT_H
+#define UBUNTU_INPUT_H
+
+// Qt
+#include <qpa/qwindowsysteminterface.h>
+
+#include <mir_toolkit/mir_client_library.h>
+
+class UbuntuClientIntegration;
+class UbuntuWindow;
+
+class UbuntuInput : public QObject
+{
+ Q_OBJECT
+
+public:
+ UbuntuInput(UbuntuClientIntegration* integration);
+ virtual ~UbuntuInput();
+
+ // QObject methods.
+ void customEvent(QEvent* event) override;
+
+ void postEvent(UbuntuWindow* window, const MirEvent *event);
+ UbuntuClientIntegration* integration() const { return mIntegration; }
+
+protected:
+ void dispatchKeyEvent(QWindow *window, const MirInputEvent *event);
+ void dispatchPointerEvent(QWindow *window, const MirInputEvent *event);
+ void dispatchTouchEvent(QWindow *window, const MirInputEvent *event);
+ void dispatchInputEvent(QWindow *window, const MirInputEvent *event);
+
+ void dispatchOrientationEvent(QWindow* window, const MirOrientationEvent *event);
+
+private:
+ UbuntuClientIntegration* mIntegration;
+ QTouchDevice* mTouchDevice;
+ const QByteArray mEventFilterType;
+ const QEvent::Type mEventType;
+};
+
+#endif // UBUNTU_INPUT_H
diff --git a/src/plugins/platforms/mirclient/integration.cpp b/src/plugins/platforms/mirclient/integration.cpp
new file mode 100644
index 0000000000..6dc04c4507
--- /dev/null
+++ b/src/plugins/platforms/mirclient/integration.cpp
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2014-2015 Canonical, Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License version 3, as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+// Qt
+#include <QGuiApplication>
+#include <private/qguiapplication_p.h>
+#include <qpa/qplatformnativeinterface.h>
+#include <qpa/qplatforminputcontextfactory_p.h>
+#include <qpa/qplatforminputcontext.h>
+#include <QtPlatformSupport/private/qgenericunixfontdatabase_p.h>
+#include <QtPlatformSupport/private/qgenericunixeventdispatcher_p.h>
+#include <QOpenGLContext>
+
+// Local
+#include "backingstore.h"
+#include "clipboard.h"
+#include "glcontext.h"
+#include "input.h"
+#include "integration.h"
+#include "logging.h"
+#include "nativeinterface.h"
+#include "screen.h"
+#include "theme.h"
+#include "window.h"
+
+// 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)
+{
+ Q_UNUSED(options)
+ Q_UNUSED(context)
+ DASSERT(context != NULL);
+ QCoreApplication::postEvent(QCoreApplication::instance(),
+ new QEvent(QEvent::ApplicationActivate));
+}
+
+static void aboutToStopCallback(UApplicationArchive *archive, void* context)
+{
+ Q_UNUSED(archive)
+ DASSERT(context != NULL);
+ UbuntuClientIntegration* integration = static_cast<UbuntuClientIntegration*>(context);
+ integration->inputContext()->hideInputPanel();
+ QCoreApplication::postEvent(QCoreApplication::instance(),
+ new QEvent(QEvent::ApplicationDeactivate));
+}
+
+UbuntuClientIntegration::UbuntuClientIntegration()
+ : QPlatformIntegration()
+ , mNativeInterface(new UbuntuNativeInterface)
+ , mFontDb(new QGenericUnixFontDatabase)
+ , mServices(new UbuntuPlatformServices)
+ , mClipboard(new UbuntuClipboard)
+ , mScaleFactor(1.0)
+{
+ setupOptions();
+ setupDescription();
+
+ // Create new application instance
+ mInstance = u_application_instance_new_from_description_with_options(mDesc, mOptions);
+
+ if (mInstance == nullptr)
+ qFatal("UbuntuClientIntegration: connection to Mir server failed. Check that a Mir server is\n"
+ "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");
+
+ // Create default screen.
+ mScreen = new UbuntuScreen(u_application_instance_get_mir_connection(mInstance));
+ screenAdded(mScreen);
+
+ // Initialize input.
+ if (qEnvironmentVariableIsEmpty("QTUBUNTU_NO_INPUT")) {
+ mInput = new UbuntuInput(this);
+ mInputContext = QPlatformInputContextFactory::create();
+ } else {
+ mInput = nullptr;
+ mInputContext = nullptr;
+ }
+
+ // compute the scale factor
+ 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;
+ }
+ }
+ mScaleFactor = static_cast<qreal>(gridUnit) / defaultGridUnit;
+}
+
+UbuntuClientIntegration::~UbuntuClientIntegration()
+{
+ delete mInput;
+ delete mInputContext;
+ delete mScreen;
+ delete mServices;
+}
+
+QPlatformServices *UbuntuClientIntegration::services() const
+{
+ return mServices;
+}
+
+void UbuntuClientIntegration::setupOptions()
+{
+ QStringList args = QCoreApplication::arguments();
+ int argc = args.size() + 1;
+ char **argv = new char*[argc];
+ for (int i = 0; i < argc - 1; i++)
+ argv[i] = qstrdup(args.at(i).toLocal8Bit());
+ argv[argc - 1] = nullptr;
+
+ mOptions = u_application_options_new_from_cmd_line(argc - 1, argv);
+
+ for (int i = 0; i < argc; i++)
+ delete [] argv[i];
+ delete [] argv;
+}
+
+void UbuntuClientIntegration::setupDescription()
+{
+ mDesc = u_application_description_new();
+ UApplicationId* id = u_application_id_new_from_stringn("QtUbuntu", 8);
+ u_application_description_set_application_id(mDesc, id);
+
+ UApplicationLifecycleDelegate* delegate = u_application_lifecycle_delegate_new();
+ u_application_lifecycle_delegate_set_application_resumed_cb(delegate, &resumedCallback);
+ u_application_lifecycle_delegate_set_application_about_to_stop_cb(delegate, &aboutToStopCallback);
+ u_application_lifecycle_delegate_set_context(delegate, this);
+ u_application_description_set_application_lifecycle_delegate(mDesc, delegate);
+}
+
+QPlatformWindow* UbuntuClientIntegration::createPlatformWindow(QWindow* window) const
+{
+ return const_cast<UbuntuClientIntegration*>(this)->createPlatformWindow(window);
+}
+
+QPlatformWindow* UbuntuClientIntegration::createPlatformWindow(QWindow* window)
+{
+ QPlatformWindow* platformWindow = new UbuntuWindow(
+ window, mClipboard, static_cast<UbuntuScreen*>(mScreen), mInput, u_application_instance_get_mir_connection(mInstance));
+ platformWindow->requestActivateWindow();
+ return platformWindow;
+}
+
+bool UbuntuClientIntegration::hasCapability(QPlatformIntegration::Capability cap) const
+{
+ switch (cap) {
+ case ThreadedPixmaps:
+ return true;
+ break;
+
+ case OpenGL:
+ return true;
+ break;
+
+ case ThreadedOpenGL:
+ if (qEnvironmentVariableIsEmpty("QTUBUNTU_NO_THREADED_OPENGL")) {
+ return true;
+ } else {
+ DLOG("ubuntumirclient: disabled threaded OpenGL");
+ return false;
+ }
+ break;
+
+ default:
+ return QPlatformIntegration::hasCapability(cap);
+ }
+}
+
+QAbstractEventDispatcher *UbuntuClientIntegration::createEventDispatcher() const
+{
+ return createUnixEventDispatcher();
+}
+
+QPlatformBackingStore* UbuntuClientIntegration::createPlatformBackingStore(QWindow* window) const
+{
+ return new UbuntuBackingStore(window);
+}
+
+QPlatformOpenGLContext* UbuntuClientIntegration::createPlatformOpenGLContext(
+ QOpenGLContext* context) const
+{
+ return const_cast<UbuntuClientIntegration*>(this)->createPlatformOpenGLContext(context);
+}
+
+QPlatformOpenGLContext* UbuntuClientIntegration::createPlatformOpenGLContext(
+ QOpenGLContext* context)
+{
+ return new UbuntuOpenGLContext(static_cast<UbuntuScreen*>(context->screen()->handle()),
+ static_cast<UbuntuOpenGLContext*>(context->shareHandle()));
+}
+
+QStringList UbuntuClientIntegration::themeNames() const
+{
+ return QStringList(UbuntuTheme::name);
+}
+
+QPlatformTheme* UbuntuClientIntegration::createPlatformTheme(const QString& name) const
+{
+ Q_UNUSED(name);
+ return new UbuntuTheme;
+}
+
+QVariant UbuntuClientIntegration::styleHint(StyleHint hint) const
+{
+ switch (hint) {
+ case QPlatformIntegration::StartDragDistance: {
+ // default is 10 pixels (see QPlatformTheme::defaultThemeHint)
+ return 10.0 * mScaleFactor;
+ }
+ case QPlatformIntegration::PasswordMaskDelay: {
+ // return time in milliseconds - 1 second
+ return QVariant(1000);
+ }
+ default:
+ break;
+ }
+ return QPlatformIntegration::styleHint(hint);
+}
+
+QPlatformClipboard* UbuntuClientIntegration::clipboard() const
+{
+ return mClipboard.data();
+}
diff --git a/src/plugins/platforms/mirclient/integration.h b/src/plugins/platforms/mirclient/integration.h
new file mode 100644
index 0000000000..45c10ba432
--- /dev/null
+++ b/src/plugins/platforms/mirclient/integration.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2014 Canonical, Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License version 3, as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef UBUNTU_CLIENT_INTEGRATION_H
+#define UBUNTU_CLIENT_INTEGRATION_H
+
+#include <qpa/qplatformintegration.h>
+#include <QSharedPointer>
+
+#include "platformservices.h"
+
+// platform-api
+#include <ubuntu/application/description.h>
+#include <ubuntu/application/instance.h>
+
+class UbuntuClipboard;
+class UbuntuInput;
+class UbuntuScreen;
+
+class UbuntuClientIntegration : public QPlatformIntegration {
+public:
+ UbuntuClientIntegration();
+ virtual ~UbuntuClientIntegration();
+
+ // QPlatformIntegration methods.
+ bool hasCapability(QPlatformIntegration::Capability cap) const override;
+ QAbstractEventDispatcher *createEventDispatcher() const override;
+ QPlatformNativeInterface* nativeInterface() const override { return mNativeInterface; }
+ QPlatformBackingStore* createPlatformBackingStore(QWindow* window) const override;
+ QPlatformOpenGLContext* createPlatformOpenGLContext(QOpenGLContext* context) const override;
+ QPlatformFontDatabase* fontDatabase() const override { return mFontDb; }
+ QStringList themeNames() const override;
+ QPlatformTheme* createPlatformTheme(const QString& name) const override;
+ QVariant styleHint(StyleHint hint) const override;
+ QPlatformServices *services() const override;
+ QPlatformWindow* createPlatformWindow(QWindow* window) const override;
+ QPlatformInputContext* inputContext() const override { return mInputContext; }
+ QPlatformClipboard* clipboard() const override;
+
+ QPlatformOpenGLContext* createPlatformOpenGLContext(QOpenGLContext* context);
+ QPlatformWindow* createPlatformWindow(QWindow* window);
+ UbuntuScreen* screen() const { return mScreen; }
+
+private:
+ void setupOptions();
+ void setupDescription();
+
+ QPlatformNativeInterface* mNativeInterface;
+ QPlatformFontDatabase* mFontDb;
+
+ UbuntuPlatformServices* mServices;
+
+ UbuntuScreen* mScreen;
+ UbuntuInput* mInput;
+ QPlatformInputContext* mInputContext;
+ QSharedPointer<UbuntuClipboard> mClipboard;
+ qreal mScaleFactor;
+
+ // Platform API stuff
+ UApplicationOptions* mOptions;
+ UApplicationDescription* mDesc;
+ UApplicationInstance* mInstance;
+};
+
+#endif // UBUNTU_CLIENT_INTEGRATION_H
diff --git a/src/plugins/platforms/mirclient/logging.h b/src/plugins/platforms/mirclient/logging.h
new file mode 100644
index 0000000000..07f57245d7
--- /dev/null
+++ b/src/plugins/platforms/mirclient/logging.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2014 Canonical, Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License version 3, as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef QUBUNTULOGGING_H
+#define QUBUNTULOGGING_H
+
+// Logging and assertion macros.
+#define LOG(...) qDebug(__VA_ARGS__)
+#define LOG_IF(cond,...) do { if (cond) qDebug(__VA_ARGS__); } while(0)
+#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
+
+#endif // QUBUNTULOGGING_H
diff --git a/src/plugins/platforms/mirclient/nativeinterface.cpp b/src/plugins/platforms/mirclient/nativeinterface.cpp
new file mode 100644
index 0000000000..cad1871cea
--- /dev/null
+++ b/src/plugins/platforms/mirclient/nativeinterface.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2014 Canonical, Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License version 3, as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+// Qt
+#include <private/qguiapplication_p.h>
+#include <QtGui/qopenglcontext.h>
+#include <QtGui/qscreen.h>
+#include <QtCore/QMap>
+
+// Local
+#include "nativeinterface.h"
+#include "screen.h"
+#include "glcontext.h"
+
+class UbuntuResourceMap : public QMap<QByteArray, UbuntuNativeInterface::ResourceType>
+{
+public:
+ UbuntuResourceMap()
+ : QMap<QByteArray, UbuntuNativeInterface::ResourceType>() {
+ insert("egldisplay", UbuntuNativeInterface::EglDisplay);
+ insert("eglcontext", UbuntuNativeInterface::EglContext);
+ insert("nativeorientation", UbuntuNativeInterface::NativeOrientation);
+ insert("display", UbuntuNativeInterface::Display);
+ }
+};
+
+Q_GLOBAL_STATIC(UbuntuResourceMap, ubuntuResourceMap)
+
+UbuntuNativeInterface::UbuntuNativeInterface()
+ : mGenericEventFilterType(QByteArrayLiteral("Event"))
+ , mNativeOrientation(nullptr)
+{
+}
+
+UbuntuNativeInterface::~UbuntuNativeInterface()
+{
+ delete mNativeOrientation;
+ mNativeOrientation = nullptr;
+}
+
+void* UbuntuNativeInterface::nativeResourceForContext(
+ const QByteArray& resourceString, QOpenGLContext* context)
+{
+ if (!context)
+ return nullptr;
+
+ const QByteArray kLowerCaseResource = resourceString.toLower();
+
+ if (!ubuntuResourceMap()->contains(kLowerCaseResource))
+ return nullptr;
+
+ const ResourceType kResourceType = ubuntuResourceMap()->value(kLowerCaseResource);
+
+ if (kResourceType == UbuntuNativeInterface::EglContext)
+ return static_cast<UbuntuOpenGLContext*>(context->handle())->eglContext();
+ else
+ return nullptr;
+}
+
+void* UbuntuNativeInterface::nativeResourceForWindow(const QByteArray& resourceString, QWindow* window)
+{
+ const QByteArray kLowerCaseResource = resourceString.toLower();
+ if (!ubuntuResourceMap()->contains(kLowerCaseResource))
+ return NULL;
+ const ResourceType kResourceType = ubuntuResourceMap()->value(kLowerCaseResource);
+ if (kResourceType == UbuntuNativeInterface::EglDisplay) {
+ if (window) {
+ return static_cast<UbuntuScreen*>(window->screen()->handle())->eglDisplay();
+ } else {
+ return static_cast<UbuntuScreen*>(
+ QGuiApplication::primaryScreen()->handle())->eglDisplay();
+ }
+ } else if (kResourceType == UbuntuNativeInterface::NativeOrientation) {
+ // Return the device's native screen orientation.
+ if (window) {
+ UbuntuScreen *ubuntuScreen = static_cast<UbuntuScreen*>(window->screen()->handle());
+ mNativeOrientation = new Qt::ScreenOrientation(ubuntuScreen->nativeOrientation());
+ } else {
+ QPlatformScreen *platformScreen = QGuiApplication::primaryScreen()->handle();
+ mNativeOrientation = new Qt::ScreenOrientation(platformScreen->nativeOrientation());
+ }
+ return mNativeOrientation;
+ } else {
+ return NULL;
+ }
+}
+
+void* UbuntuNativeInterface::nativeResourceForScreen(const QByteArray& resourceString, QScreen* screen)
+{
+ const QByteArray kLowerCaseResource = resourceString.toLower();
+ if (!ubuntuResourceMap()->contains(kLowerCaseResource))
+ return NULL;
+ const ResourceType kResourceType = ubuntuResourceMap()->value(kLowerCaseResource);
+ if (kResourceType == UbuntuNativeInterface::Display) {
+ if (!screen)
+ screen = QGuiApplication::primaryScreen();
+ return static_cast<UbuntuScreen*>(screen->handle())->eglNativeDisplay();
+ } else
+ return NULL;
+}
diff --git a/src/plugins/platforms/mirclient/nativeinterface.h b/src/plugins/platforms/mirclient/nativeinterface.h
new file mode 100644
index 0000000000..129afb1083
--- /dev/null
+++ b/src/plugins/platforms/mirclient/nativeinterface.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2014 Canonical, Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License version 3, as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef UBUNTU_NATIVE_INTERFACE_H
+#define UBUNTU_NATIVE_INTERFACE_H
+
+#include <qpa/qplatformnativeinterface.h>
+
+class UbuntuNativeInterface : public QPlatformNativeInterface {
+public:
+ enum ResourceType { EglDisplay, EglContext, NativeOrientation, Display };
+
+ UbuntuNativeInterface();
+ ~UbuntuNativeInterface();
+
+ // QPlatformNativeInterface methods.
+ void* nativeResourceForContext(const QByteArray& resourceString,
+ QOpenGLContext* context) override;
+ void* nativeResourceForWindow(const QByteArray& resourceString,
+ QWindow* window) override;
+ void* nativeResourceForScreen(const QByteArray& resourceString,
+ QScreen* screen) override;
+
+ // New methods.
+ const QByteArray& genericEventFilterType() const { return mGenericEventFilterType; }
+
+private:
+ const QByteArray mGenericEventFilterType;
+ Qt::ScreenOrientation* mNativeOrientation;
+};
+
+#endif // UBUNTU_NATIVE_INTERFACE_H
diff --git a/src/plugins/platforms/mirclient/orientationchangeevent_p.h b/src/plugins/platforms/mirclient/orientationchangeevent_p.h
new file mode 100644
index 0000000000..35a65f136f
--- /dev/null
+++ b/src/plugins/platforms/mirclient/orientationchangeevent_p.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2014 Canonical, Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License version 3, as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef ORIENTATIONCHANGEEVENT_P_H
+#define ORIENTATIONCHANGEEVENT_P_H
+
+#include <QEvent>
+#include <QOrientationReading>
+#include "logging.h"
+
+class OrientationChangeEvent : public QEvent {
+public:
+ OrientationChangeEvent(QEvent::Type type, QOrientationReading::Orientation orientation)
+ : QEvent(type)
+ , mOrientation(orientation)
+ {
+ }
+
+ static const QEvent::Type mType;
+ QOrientationReading::Orientation mOrientation;
+};
+
+#endif // ORIENTATIONCHANGEEVENT_P_H
diff --git a/src/plugins/platforms/mirclient/platformservices.cpp b/src/plugins/platforms/mirclient/platformservices.cpp
new file mode 100644
index 0000000000..c9a974b711
--- /dev/null
+++ b/src/plugins/platforms/mirclient/platformservices.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2014 Canonical, Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License version 3, as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "platformservices.h"
+
+#include <QUrl>
+
+#include <ubuntu/application/url_dispatcher/service.h>
+#include <ubuntu/application/url_dispatcher/session.h>
+
+bool UbuntuPlatformServices::openUrl(const QUrl &url)
+{
+ return callDispatcher(url);
+}
+
+bool UbuntuPlatformServices::openDocument(const QUrl &url)
+{
+ return callDispatcher(url);
+}
+
+bool UbuntuPlatformServices::callDispatcher(const QUrl &url)
+{
+ UAUrlDispatcherSession* session = ua_url_dispatcher_session();
+ if (!session)
+ return false;
+
+ ua_url_dispatcher_session_open(session, url.toEncoded().constData(), NULL, NULL);
+
+ free(session);
+
+ // We are returning true here because the other option
+ // is spawning a nested event loop and wait for the
+ // callback. But there is no guarantee on how fast
+ // the callback is going to be so we prefer to avoid the
+ // nested event loop. Long term plan is improve Qt API
+ // to support an async openUrl
+ return true;
+}
diff --git a/src/plugins/platforms/mirclient/platformservices.h b/src/plugins/platforms/mirclient/platformservices.h
new file mode 100644
index 0000000000..1c3e68bf8a
--- /dev/null
+++ b/src/plugins/platforms/mirclient/platformservices.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2014 Canonical, Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License version 3, as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef UBUNTU_PLATFORM_SERVICES_H
+#define UBUNTU_PLATFORM_SERVICES_H
+
+#include <qpa/qplatformservices.h>
+#include <QtPlatformSupport/private/qgenericunixfontdatabase_p.h>
+#include <QtPlatformSupport/private/qgenericunixeventdispatcher_p.h>
+
+class UbuntuPlatformServices : public QPlatformServices {
+public:
+ bool openUrl(const QUrl &url) override;
+ bool openDocument(const QUrl &url) override;
+
+private:
+ bool callDispatcher(const QUrl &url);
+};
+
+#endif // UBUNTU_PLATFORM_SERVICES_H
diff --git a/src/plugins/platforms/mirclient/plugin.cpp b/src/plugins/platforms/mirclient/plugin.cpp
new file mode 100644
index 0000000000..6b245ea525
--- /dev/null
+++ b/src/plugins/platforms/mirclient/plugin.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2014 Canonical, Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License version 3, as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "plugin.h"
+#include "integration.h"
+
+QStringList UbuntuMirClientIntegrationPlugin::keys() const
+{
+ QStringList list;
+ list << "ubuntumirclient";
+ return list;
+}
+
+QPlatformIntegration* UbuntuMirClientIntegrationPlugin::create(const QString &system,
+ const QStringList &)
+{
+ if (system.toLower() == "ubuntumirclient") {
+#ifdef PLATFORM_API_TOUCH
+ setenv("UBUNTU_PLATFORM_API_BACKEND", "touch_mirclient", 1);
+#else
+ setenv("UBUNTU_PLATFORM_API_BACKEND", "desktop_mirclient", 1);
+#endif
+ return new UbuntuClientIntegration;
+ } else {
+ return 0;
+ }
+}
diff --git a/src/plugins/platforms/mirclient/plugin.h b/src/plugins/platforms/mirclient/plugin.h
new file mode 100644
index 0000000000..ac27f1aead
--- /dev/null
+++ b/src/plugins/platforms/mirclient/plugin.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2014 Canonical, Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License version 3, as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef UBUNTU_CLIENT_PLUGIN_H
+#define UBUNTU_CLIENT_PLUGIN_H
+
+#include <qpa/qplatformintegrationplugin.h>
+
+class UbuntuMirClientIntegrationPlugin : public QPlatformIntegrationPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QPlatformIntegrationFactoryInterface_iid FILE "ubuntumirclient.json")
+
+public:
+ QStringList keys() const;
+ QPlatformIntegration* create(const QString&, const QStringList&);
+};
+
+#endif // UBUNTU_CLIENT_PLUGIN_H
diff --git a/src/plugins/platforms/mirclient/screen.cpp b/src/plugins/platforms/mirclient/screen.cpp
new file mode 100644
index 0000000000..52aa8f501e
--- /dev/null
+++ b/src/plugins/platforms/mirclient/screen.cpp
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2014 Canonical, Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License version 3, as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <mir_toolkit/mir_client_library.h>
+
+// Qt
+#include <QCoreApplication>
+#include <QtCore/qmath.h>
+#include <QScreen>
+#include <QThread>
+#include <qpa/qwindowsysteminterface.h>
+#include <QtPlatformSupport/private/qeglconvenience_p.h>
+
+// local
+#include "screen.h"
+#include "logging.h"
+#include "orientationchangeevent_p.h"
+
+#include "memory"
+
+static const int kSwapInterval = 1;
+
+#if !defined(QT_NO_DEBUG)
+
+static const char *orientationToStr(Qt::ScreenOrientation orientation) {
+ switch (orientation) {
+ case Qt::PrimaryOrientation:
+ return "primary";
+ case Qt::PortraitOrientation:
+ return "portrait";
+ case Qt::LandscapeOrientation:
+ return "landscape";
+ case Qt::InvertedPortraitOrientation:
+ return "inverted portrait";
+ case Qt::InvertedLandscapeOrientation:
+ return "inverted landscape";
+ default:
+ return "INVALID!";
+ }
+}
+
+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;
+}
+
+UbuntuScreen::UbuntuScreen(MirConnection *connection)
+ : mFormat(QImage::Format_RGB32)
+ , mDepth(32)
+ , mSurfaceFormat()
+ , mEglDisplay(EGL_NO_DISPLAY)
+ , mEglConfig(nullptr)
+{
+ // 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);
+
+ 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("QUbuntuScreen::QUbuntuScreen (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;
+}
+
+UbuntuScreen::~UbuntuScreen()
+{
+ eglTerminate(mEglDisplay);
+}
+
+void UbuntuScreen::customEvent(QEvent* event) {
+ DASSERT(QThread::currentThread() == thread());
+
+ OrientationChangeEvent* oReadingEvent = static_cast<OrientationChangeEvent*>(event);
+ switch (oReadingEvent->mOrientation) {
+ case QOrientationReading::LeftUp: {
+ mCurrentOrientation = (screen()->primaryOrientation() == Qt::LandscapeOrientation) ?
+ Qt::InvertedPortraitOrientation : Qt::LandscapeOrientation;
+ break;
+ }
+ case QOrientationReading::TopUp: {
+ mCurrentOrientation = (screen()->primaryOrientation() == Qt::LandscapeOrientation) ?
+ Qt::LandscapeOrientation : Qt::PortraitOrientation;
+ break;
+ }
+ case QOrientationReading::RightUp: {
+ mCurrentOrientation = (screen()->primaryOrientation() == Qt::LandscapeOrientation) ?
+ Qt::PortraitOrientation : Qt::InvertedLandscapeOrientation;
+ break;
+ }
+ case QOrientationReading::TopDown: {
+ mCurrentOrientation = (screen()->primaryOrientation() == Qt::LandscapeOrientation) ?
+ Qt::InvertedLandscapeOrientation : Qt::InvertedPortraitOrientation;
+ break;
+ }
+ default: {
+ DLOG("UbuntuScreen::customEvent - Unknown orientation.");
+ return;
+ }
+ }
+
+ // Raise the event signal so that client apps know the orientation changed
+ DLOG("UbuntuScreen::customEvent - handling orientation change to %s", orientationToStr(mCurrentOrientation));
+ QWindowSystemInterface::handleScreenOrientationChange(screen(), mCurrentOrientation);
+}
+
+void UbuntuScreen::handleWindowSurfaceResize(int windowWidth, int windowHeight)
+{
+ if ((windowWidth > windowHeight && mGeometry.width() < mGeometry.height())
+ || (windowWidth < windowHeight && mGeometry.width() > mGeometry.height())) {
+
+ // The window aspect ratio differ's from the screen one. This means that
+ // unity8 has rotated the window in its scene.
+ // As there's no way to express window rotation in Qt's API, we have
+ // Flip QScreen's dimensions so that orientation properties match
+ // (primaryOrientation particularly).
+ // FIXME: This assumes a phone scenario. Won't work, or make sense,
+ // on the desktop
+
+ QRect currGeometry = mGeometry;
+ mGeometry.setWidth(currGeometry.height());
+ mGeometry.setHeight(currGeometry.width());
+
+ DLOG("UbuntuScreen::handleWindowSurfaceResize - new screen geometry (w=%d, h=%d)",
+ mGeometry.width(), mGeometry.height());
+ QWindowSystemInterface::handleScreenGeometryChange(screen(),
+ mGeometry /* newGeometry */,
+ mGeometry /* newAvailableGeometry */);
+
+ if (mGeometry.width() < mGeometry.height()) {
+ mCurrentOrientation = Qt::PortraitOrientation;
+ } else {
+ mCurrentOrientation = Qt::LandscapeOrientation;
+ }
+ DLOG("UbuntuScreen::handleWindowSurfaceResize - new orientation %s",orientationToStr(mCurrentOrientation));
+ QWindowSystemInterface::handleScreenOrientationChange(screen(), mCurrentOrientation);
+ }
+}
diff --git a/src/plugins/platforms/mirclient/screen.h b/src/plugins/platforms/mirclient/screen.h
new file mode 100644
index 0000000000..fa44f4cee9
--- /dev/null
+++ b/src/plugins/platforms/mirclient/screen.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2014 Canonical, Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License version 3, as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef UBUNTU_SCREEN_H
+#define UBUNTU_SCREEN_H
+
+#include <qpa/qplatformscreen.h>
+#include <QSurfaceFormat>
+#include <EGL/egl.h>
+
+struct MirConnection;
+
+class UbuntuScreen : public QObject, public QPlatformScreen
+{
+ Q_OBJECT
+public:
+ UbuntuScreen(MirConnection *connection);
+ virtual ~UbuntuScreen();
+
+ // QPlatformScreen methods.
+ QImage::Format format() const override { return mFormat; }
+ int depth() const override { return mDepth; }
+ QRect geometry() const override { return mGeometry; }
+ QRect availableGeometry() const override { return mGeometry; }
+ Qt::ScreenOrientation nativeOrientation() const override { return mNativeOrientation; }
+ Qt::ScreenOrientation orientation() const override { return mNativeOrientation; }
+
+ // New methods.
+ QSurfaceFormat surfaceFormat() const { return mSurfaceFormat; }
+ EGLDisplay eglDisplay() const { return mEglDisplay; }
+ EGLConfig eglConfig() const { return mEglConfig; }
+ EGLNativeDisplayType eglNativeDisplay() const { return mEglNativeDisplay; }
+ void handleWindowSurfaceResize(int width, int height);
+
+ // QObject methods.
+ void customEvent(QEvent* event);
+
+private:
+ QRect mGeometry;
+ Qt::ScreenOrientation mNativeOrientation;
+ Qt::ScreenOrientation mCurrentOrientation;
+ QImage::Format mFormat;
+ int mDepth;
+ QSurfaceFormat mSurfaceFormat;
+ EGLDisplay mEglDisplay;
+ EGLConfig mEglConfig;
+ EGLNativeDisplayType mEglNativeDisplay;
+};
+
+#endif // UBUNTU_SCREEN_H
diff --git a/src/plugins/platforms/mirclient/theme.cpp b/src/plugins/platforms/mirclient/theme.cpp
new file mode 100644
index 0000000000..31624fc4e8
--- /dev/null
+++ b/src/plugins/platforms/mirclient/theme.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2014 Canonical, Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License version 3, as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "theme.h"
+
+#include <QtCore/QVariant>
+
+const char *UbuntuTheme::name = "ubuntu";
+
+UbuntuTheme::UbuntuTheme()
+{
+}
+
+UbuntuTheme::~UbuntuTheme()
+{
+}
+
+QVariant UbuntuTheme::themeHint(ThemeHint hint) const
+{
+ if (hint == QPlatformTheme::SystemIconThemeName) {
+ QByteArray iconTheme = qgetenv("QTUBUNTU_ICON_THEME");
+ if (iconTheme.isEmpty()) {
+ return QVariant(QStringLiteral("ubuntu-mobile"));
+ } else {
+ return QVariant(QString(iconTheme));
+ }
+ } else {
+ return QGenericUnixTheme::themeHint(hint);
+ }
+}
diff --git a/src/plugins/platforms/mirclient/theme.h b/src/plugins/platforms/mirclient/theme.h
new file mode 100644
index 0000000000..5924f73c2f
--- /dev/null
+++ b/src/plugins/platforms/mirclient/theme.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2014 Canonical, Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License version 3, as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef UBUNTU_THEME_H
+#define UBUNTU_THEME_H
+
+#include <QtPlatformSupport/private/qgenericunixthemes_p.h>
+
+class UbuntuTheme : public QGenericUnixTheme
+{
+public:
+ static const char* name;
+ UbuntuTheme();
+ virtual ~UbuntuTheme();
+
+ // From QPlatformTheme
+ QVariant themeHint(ThemeHint hint) const override;
+};
+
+#endif // UBUNTU_THEME_H
diff --git a/src/plugins/platforms/mirclient/ubuntumirclient.json b/src/plugins/platforms/mirclient/ubuntumirclient.json
new file mode 100644
index 0000000000..c02432352f
--- /dev/null
+++ b/src/plugins/platforms/mirclient/ubuntumirclient.json
@@ -0,0 +1,3 @@
+{
+ "Keys": [ "ubuntumirclient" ]
+}
diff --git a/src/plugins/platforms/mirclient/ubuntumirclient.pro b/src/plugins/platforms/mirclient/ubuntumirclient.pro
new file mode 100644
index 0000000000..0230abab3f
--- /dev/null
+++ b/src/plugins/platforms/mirclient/ubuntumirclient.pro
@@ -0,0 +1,48 @@
+TARGET = qpa-ubuntumirclient
+TEMPLATE = lib
+
+QT -= gui
+QT += core-private gui-private platformsupport-private sensors dbus
+
+CONFIG += plugin no_keywords qpa/genericunixfontdatabase
+
+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
+QMAKE_LFLAGS += -std=c++11 -Wl,-no-undefined
+
+CONFIG += link_pkgconfig
+PKGCONFIG += egl mirclient ubuntu-platform-api
+
+SOURCES = \
+ backingstore.cpp \
+ clipboard.cpp \
+ glcontext.cpp \
+ input.cpp \
+ integration.cpp \
+ nativeinterface.cpp \
+ platformservices.cpp \
+ plugin.cpp \
+ screen.cpp \
+ theme.cpp \
+ window.cpp
+
+HEADERS = \
+ backingstore.h \
+ clipboard.h \
+ glcontext.h \
+ input.h \
+ integration.h \
+ logging.h \
+ nativeinterface.h \
+ orientationchangeevent_p.h \
+ platformservices.h \
+ plugin.h \
+ screen.h \
+ theme.h \
+ window.h
+
+# Installation path
+target.path += $$[QT_INSTALL_PLUGINS]/platforms
+
+INSTALLS += target
diff --git a/src/plugins/platforms/mirclient/window.cpp b/src/plugins/platforms/mirclient/window.cpp
new file mode 100644
index 0000000000..812b71e692
--- /dev/null
+++ b/src/plugins/platforms/mirclient/window.cpp
@@ -0,0 +1,449 @@
+/*
+ * Copyright (C) 2014-2015 Canonical, Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License version 3, as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+// Local
+#include "clipboard.h"
+#include "input.h"
+#include "window.h"
+#include "screen.h"
+#include "logging.h"
+
+// Qt
+#include <qpa/qwindowsysteminterface.h>
+#include <qpa/qwindowsysteminterface.h>
+#include <QMutex>
+#include <QMutexLocker>
+#include <QSize>
+#include <QtMath>
+
+// Platform API
+#include <ubuntu/application/instance.h>
+
+#include <EGL/egl.h>
+
+#define IS_OPAQUE_FLAG 1
+
+namespace
+{
+MirSurfaceState qtWindowStateToMirSurfaceState(Qt::WindowState state)
+{
+ switch (state) {
+ case Qt::WindowNoState:
+ return mir_surface_state_restored;
+
+ case Qt::WindowFullScreen:
+ return mir_surface_state_fullscreen;
+
+ case Qt::WindowMaximized:
+ return mir_surface_state_maximized;
+
+ case Qt::WindowMinimized:
+ return mir_surface_state_minimized;
+
+ default:
+ LOG("Unexpected Qt::WindowState: %d", state);
+ return mir_surface_state_restored;
+ }
+}
+
+#if !defined(QT_NO_DEBUG)
+const char *qtWindowStateToStr(Qt::WindowState state)
+{
+ switch (state) {
+ case Qt::WindowNoState:
+ return "NoState";
+
+ case Qt::WindowFullScreen:
+ return "FullScreen";
+
+ case Qt::WindowMaximized:
+ return "Maximized";
+
+ case Qt::WindowMinimized:
+ return "Minimized";
+
+ default:
+ return "!?";
+ }
+}
+#endif
+
+} // anonymous namespace
+
+class UbuntuWindowPrivate
+{
+public:
+ void createEGLSurface(EGLNativeWindowType nativeWindow);
+ void destroyEGLSurface();
+ int panelHeight();
+
+ UbuntuScreen* screen;
+ EGLSurface eglSurface;
+ WId id;
+ UbuntuInput* input;
+ Qt::WindowState state;
+ MirConnection *connection;
+ MirSurface* surface;
+ QSize bufferSize;
+ QSize targetBufferSize;
+ QMutex mutex;
+ QSharedPointer<UbuntuClipboard> clipboard;
+};
+
+static void eventCallback(MirSurface* surface, const MirEvent *event, void* context)
+{
+ (void) surface;
+ DASSERT(context != NULL);
+ UbuntuWindow* platformWindow = static_cast<UbuntuWindow*>(context);
+ platformWindow->priv()->input->postEvent(platformWindow, event);
+}
+
+static void surfaceCreateCallback(MirSurface* surface, void* context)
+{
+ DASSERT(context != NULL);
+ UbuntuWindow* platformWindow = static_cast<UbuntuWindow*>(context);
+ platformWindow->priv()->surface = surface;
+
+ mir_surface_set_event_handler(surface, eventCallback, context);
+}
+
+UbuntuWindow::UbuntuWindow(QWindow* w, QSharedPointer<UbuntuClipboard> clipboard, UbuntuScreen* screen,
+ UbuntuInput* input, MirConnection* connection)
+ : QObject(nullptr), QPlatformWindow(w)
+{
+ DASSERT(screen != NULL);
+
+ d = new UbuntuWindowPrivate;
+ d->screen = screen;
+ d->eglSurface = EGL_NO_SURFACE;
+ d->input = input;
+ d->state = window()->windowState();
+ d->connection = connection;
+ d->clipboard = clipboard;
+
+ static int id = 1;
+ d->id = id++;
+
+ // Use client geometry if set explicitly, use available screen geometry otherwise.
+ QPlatformWindow::setGeometry(window()->geometry() != screen->geometry() ?
+ window()->geometry() : screen->availableGeometry());
+ createWindow();
+ DLOG("UbuntuWindow::UbuntuWindow (this=%p, w=%p, screen=%p, input=%p)", this, w, screen, input);
+}
+
+UbuntuWindow::~UbuntuWindow()
+{
+ DLOG("UbuntuWindow::~UbuntuWindow");
+ d->destroyEGLSurface();
+
+ mir_surface_release_sync(d->surface);
+
+ delete d;
+}
+
+void UbuntuWindowPrivate::createEGLSurface(EGLNativeWindowType nativeWindow)
+{
+ DLOG("UbuntuWindowPrivate::createEGLSurface (this=%p, nativeWindow=%p)",
+ this, reinterpret_cast<void*>(nativeWindow));
+
+ eglSurface = eglCreateWindowSurface(screen->eglDisplay(), screen->eglConfig(),
+ nativeWindow, nullptr);
+
+ DASSERT(eglSurface != EGL_NO_SURFACE);
+}
+
+void UbuntuWindowPrivate::destroyEGLSurface()
+{
+ DLOG("UbuntuWindowPrivate::destroyEGLSurface (this=%p)", this);
+ if (eglSurface != EGL_NO_SURFACE) {
+ eglDestroySurface(screen->eglDisplay(), eglSurface);
+ eglSurface = EGL_NO_SURFACE;
+ }
+}
+
+// FIXME - in order to work around https://bugs.launchpad.net/mir/+bug/1346633
+// we need to guess the panel height (3GU + 2DP)
+int UbuntuWindowPrivate::panelHeight()
+{
+ 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;
+ }
+ }
+ qreal densityPixelRatio = static_cast<qreal>(gridUnit) / defaultGridUnit;
+ return gridUnit * 3 + qFloor(densityPixelRatio) * 2;
+}
+
+namespace
+{
+static MirPixelFormat
+mir_choose_default_pixel_format(MirConnection *connection)
+{
+ MirPixelFormat format[mir_pixel_formats];
+ unsigned int nformats;
+
+ mir_connection_get_available_surface_formats(connection,
+ format, mir_pixel_formats, &nformats);
+
+ return format[0];
+}
+}
+
+void UbuntuWindow::createWindow()
+{
+ DLOG("UbuntuWindow::createWindow (this=%p)", this);
+
+ // FIXME: remove this remnant of an old platform-api enum - needs ubuntu-keyboard update
+ const int SCREEN_KEYBOARD_ROLE = 7;
+ // Get surface role and flags.
+ QVariant roleVariant = window()->property("role");
+ int role = roleVariant.isValid() ? roleVariant.toUInt() : 1; // 1 is the default role for apps.
+ QVariant opaqueVariant = window()->property("opaque");
+ uint flags = opaqueVariant.isValid() ?
+ opaqueVariant.toUInt() ? static_cast<uint>(IS_OPAQUE_FLAG) : 0 : 0;
+
+ // FIXME(loicm) Opaque flag is forced for now for non-system sessions (applications) for
+ // performance reasons.
+ flags |= static_cast<uint>(IS_OPAQUE_FLAG);
+
+ const QByteArray title = (!window()->title().isNull()) ? window()->title().toUtf8() : "Window 1"; // legacy title
+ const int panelHeight = d->panelHeight();
+
+#if !defined(QT_NO_DEBUG)
+ LOG("panelHeight: '%d'", panelHeight);
+ LOG("role: '%d'", role);
+ LOG("flags: '%s'", (flags & static_cast<uint>(1)) ? "Opaque" : "NotOpaque");
+ LOG("title: '%s'", title.constData());
+#endif
+
+ // Get surface geometry.
+ QRect geometry;
+ if (d->state == Qt::WindowFullScreen) {
+ printf("UbuntuWindow - fullscreen geometry\n");
+ geometry = screen()->geometry();
+ } else if (d->state == Qt::WindowMaximized) {
+ printf("UbuntuWindow - maximized geometry\n");
+ geometry = screen()->availableGeometry();
+ /*
+ * FIXME: Autopilot relies on being able to convert coordinates relative of the window
+ * into absolute screen coordinates. Mir does not allow this, see bug lp:1346633
+ * Until there's a correct way to perform this transformation agreed, this horrible hack
+ * guesses the transformation heuristically.
+ *
+ * Assumption: this method only used on phone devices!
+ */
+ geometry.setY(panelHeight);
+ } else {
+ printf("UbuntuWindow - regular geometry\n");
+ geometry = this->geometry();
+ geometry.setY(panelHeight);
+ }
+
+ DLOG("[ubuntumirclient QPA] creating surface at (%d, %d) with size (%d, %d) with title '%s'\n",
+ geometry.x(), geometry.y(), geometry.width(), geometry.height(), title.data());
+
+ MirSurfaceSpec *spec;
+ if (role == SCREEN_KEYBOARD_ROLE)
+ {
+ spec = mir_connection_create_spec_for_input_method(d->connection, geometry.width(),
+ geometry.height(), mir_choose_default_pixel_format(d->connection));
+ }
+ else
+ {
+ spec = mir_connection_create_spec_for_normal_surface(d->connection, geometry.width(),
+ geometry.height(), mir_choose_default_pixel_format(d->connection));
+ }
+ mir_surface_spec_set_name(spec, title.data());
+
+ // Create platform window
+ mir_wait_for(mir_surface_create(spec, surfaceCreateCallback, this));
+ mir_surface_spec_release(spec);
+
+ DASSERT(d->surface != NULL);
+ d->createEGLSurface((EGLNativeWindowType)mir_buffer_stream_get_egl_native_window(mir_surface_get_buffer_stream(d->surface)));
+
+ if (d->state == Qt::WindowFullScreen) {
+ // TODO: We could set this on creation once surface spec supports it (mps already up)
+ mir_wait_for(mir_surface_set_state(d->surface, mir_surface_state_fullscreen));
+ }
+
+ // 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(d->surface, &parameters);
+
+ geometry.setWidth(parameters.width);
+ geometry.setHeight(parameters.height);
+ }
+
+ DLOG("[ubuntumirclient QPA] created surface has size (%d, %d)",
+ geometry.width(), geometry.height());
+
+ // Assume that the buffer size matches the surface size at creation time
+ d->bufferSize = geometry.size();
+
+ // Tell Qt about the geometry.
+ QWindowSystemInterface::handleGeometryChange(window(), geometry);
+ QPlatformWindow::setGeometry(geometry);
+}
+
+void UbuntuWindow::moveResize(const QRect& rect)
+{
+ (void) rect;
+ // TODO: Not yet supported by mir.
+}
+
+void UbuntuWindow::handleSurfaceResize(int width, int height)
+{
+ QMutexLocker(&d->mutex);
+ LOG("UbuntuWindow::handleSurfaceResize(width=%d, height=%d)", width, height);
+
+ // The current buffer size hasn't actually changed. so just render on it and swap
+ // buffers until we render on a buffer with the target size.
+
+ d->targetBufferSize.rwidth() = width;
+ d->targetBufferSize.rheight() = height;
+
+ if (d->bufferSize != d->targetBufferSize) {
+ QWindowSystemInterface::handleExposeEvent(window(), geometry());
+ } else {
+ qWarning("[ubuntumirclient QPA] UbuntuWindow::handleSurfaceResize"
+ " current buffer already has the target size");
+ d->targetBufferSize = QSize();
+ }
+}
+
+void UbuntuWindow::handleSurfaceFocusChange(bool focused)
+{
+ LOG("UbuntuWindow::handleSurfaceFocusChange(focused=%s)", focused ? "true" : "false");
+ QWindow *activatedWindow = focused ? window() : nullptr;
+
+ // System clipboard contents might have changed while this window was unfocused and wihtout
+ // 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.
+ if (focused) {
+ d->clipboard->requestDBusClipboardContents();
+ }
+
+ QWindowSystemInterface::handleWindowActivated(activatedWindow, Qt::ActiveWindowFocusReason);
+}
+
+void UbuntuWindow::setWindowState(Qt::WindowState state)
+{
+ QMutexLocker(&d->mutex);
+ DLOG("UbuntuWindow::setWindowState (this=%p, %s)", this, qtWindowStateToStr(state));
+
+ if (state == d->state)
+ return;
+
+ // TODO: Perhaps we should check if the states are applied?
+ mir_wait_for(mir_surface_set_state(d->surface, qtWindowStateToMirSurfaceState(state)));
+ d->state = state;
+}
+
+void UbuntuWindow::setGeometry(const QRect& rect)
+{
+ DLOG("UbuntuWindow::setGeometry (this=%p)", this);
+
+ bool doMoveResize;
+
+ {
+ QMutexLocker(&d->mutex);
+ QPlatformWindow::setGeometry(rect);
+ doMoveResize = d->state != Qt::WindowFullScreen && d->state != Qt::WindowMaximized;
+ }
+
+ if (doMoveResize) {
+ moveResize(rect);
+ }
+}
+
+void UbuntuWindow::setVisible(bool visible)
+{
+ QMutexLocker(&d->mutex);
+ DLOG("UbuntuWindow::setVisible (this=%p, visible=%s)", this, visible ? "true" : "false");
+
+ if (visible) {
+ mir_wait_for(mir_surface_set_state(d->surface, qtWindowStateToMirSurfaceState(d->state)));
+
+ QWindowSystemInterface::handleExposeEvent(window(), QRect());
+ QWindowSystemInterface::flushWindowSystemEvents();
+ } else {
+ // TODO: Use the new mir_surface_state_hidden state instead of mir_surface_state_minimized.
+ // Will have to change qtmir and unity8 for that.
+ mir_wait_for(mir_surface_set_state(d->surface, mir_surface_state_minimized));
+ }
+}
+
+void* UbuntuWindow::eglSurface() const
+{
+ return d->eglSurface;
+}
+
+WId UbuntuWindow::winId() const
+{
+ return d->id;
+}
+
+void UbuntuWindow::onBuffersSwapped_threadSafe(int newBufferWidth, int newBufferHeight)
+{
+ QMutexLocker(&d->mutex);
+
+ bool sizeKnown = newBufferWidth > 0 && newBufferHeight > 0;
+
+ if (sizeKnown && (d->bufferSize.width() != newBufferWidth ||
+ d->bufferSize.height() != newBufferHeight)) {
+
+ DLOG("UbuntuWindow::onBuffersSwapped_threadSafe - buffer size changed from (%d,%d) to (%d,%d)",
+ d->bufferSize.width(), d->bufferSize.height(), newBufferWidth, newBufferHeight);
+
+ d->bufferSize.rwidth() = newBufferWidth;
+ d->bufferSize.rheight() = newBufferHeight;
+
+ QRect newGeometry;
+
+ newGeometry = geometry();
+ newGeometry.setWidth(d->bufferSize.width());
+ newGeometry.setHeight(d->bufferSize.height());
+
+ QPlatformWindow::setGeometry(newGeometry);
+ QWindowSystemInterface::handleGeometryChange(window(), newGeometry, QRect());
+ QWindowSystemInterface::handleExposeEvent(window(), newGeometry);
+
+ } else {
+ // buffer size hasn't changed
+ if (d->targetBufferSize.isValid()) {
+ if (d->bufferSize != d->targetBufferSize) {
+ // but we still didn't reach the promised buffer size from the mir resize event.
+ // thus keep swapping buffers
+ QWindowSystemInterface::handleExposeEvent(window(), geometry());
+ } else {
+ // target met. we have just provided a render with the target size and
+ // can therefore finally rest.
+ d->targetBufferSize = QSize();
+ }
+ }
+ }
+}
diff --git a/src/plugins/platforms/mirclient/window.h b/src/plugins/platforms/mirclient/window.h
new file mode 100644
index 0000000000..64365789e6
--- /dev/null
+++ b/src/plugins/platforms/mirclient/window.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2014-2015 Canonical, Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License version 3, as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef UBUNTU_WINDOW_H
+#define UBUNTU_WINDOW_H
+
+#include <qpa/qplatformwindow.h>
+#include <QSharedPointer>
+
+#include <mir_toolkit/mir_client_library.h>
+
+class UbuntuClipboard;
+class UbuntuInput;
+class UbuntuScreen;
+class UbuntuWindowPrivate;
+
+class UbuntuWindow : public QObject, public QPlatformWindow
+{
+ Q_OBJECT
+public:
+ UbuntuWindow(QWindow *w, QSharedPointer<UbuntuClipboard> clipboard, UbuntuScreen *screen,
+ UbuntuInput *input, MirConnection *mir_connection);
+ virtual ~UbuntuWindow();
+
+ // QPlatformWindow methods.
+ WId winId() const override;
+ void setGeometry(const QRect&) override;
+ void setWindowState(Qt::WindowState state) override;
+ void setVisible(bool visible) override;
+
+ // New methods.
+ void* eglSurface() const;
+ void handleSurfaceResize(int width, int height);
+ void handleSurfaceFocusChange(bool focused);
+ void onBuffersSwapped_threadSafe(int newBufferWidth, int newBufferHeight);
+
+ UbuntuWindowPrivate* priv() { return d; }
+
+private:
+ void createWindow();
+ void moveResize(const QRect& rect);
+
+ UbuntuWindowPrivate *d;
+};
+
+#endif // UBUNTU_WINDOW_H