From f08e1ecdc88e9373a31c1505d5db7f905431c644 Mon Sep 17 00:00:00 2001 From: Paul Olav Tvete Date: Fri, 7 Aug 2015 14:28:14 +0200 Subject: Add Mir client platform plugin Contributed by Canonical, Ltd. Change-Id: I77752a1fd56641342be6c84e01b013d3df36ad73 Reviewed-by: Paul Olav Tvete --- config.tests/qpa/mirclient/mirclient.cpp | 47 ++ config.tests/qpa/mirclient/mirclient.pro | 4 + configure | 34 +- src/plugins/platforms/mirclient/mirclient.json | 3 + src/plugins/platforms/mirclient/mirclient.pro | 47 ++ .../platforms/mirclient/qmirclientbackingstore.cpp | 146 ++++++ .../platforms/mirclient/qmirclientbackingstore.h | 70 +++ .../platforms/mirclient/qmirclientclipboard.cpp | 305 ++++++++++++ .../platforms/mirclient/qmirclientclipboard.h | 88 ++++ .../platforms/mirclient/qmirclientglcontext.cpp | 156 +++++++ .../platforms/mirclient/qmirclientglcontext.h | 66 +++ .../platforms/mirclient/qmirclientinput.cpp | 520 +++++++++++++++++++++ src/plugins/platforms/mirclient/qmirclientinput.h | 78 ++++ .../platforms/mirclient/qmirclientintegration.cpp | 264 +++++++++++ .../platforms/mirclient/qmirclientintegration.h | 99 ++++ .../platforms/mirclient/qmirclientlogging.h | 60 +++ .../mirclient/qmirclientnativeinterface.cpp | 134 ++++++ .../mirclient/qmirclientnativeinterface.h | 66 +++ .../mirclient/qmirclientorientationchangeevent_p.h | 66 +++ .../mirclient/qmirclientplatformservices.cpp | 72 +++ .../mirclient/qmirclientplatformservices.h | 54 +++ .../platforms/mirclient/qmirclientplugin.cpp | 61 +++ src/plugins/platforms/mirclient/qmirclientplugin.h | 53 +++ .../platforms/mirclient/qmirclientscreen.cpp | 296 ++++++++++++ src/plugins/platforms/mirclient/qmirclientscreen.h | 84 ++++ .../platforms/mirclient/qmirclienttheme.cpp | 64 +++ src/plugins/platforms/mirclient/qmirclienttheme.h | 54 +++ .../platforms/mirclient/qmirclientwindow.cpp | 452 ++++++++++++++++++ src/plugins/platforms/mirclient/qmirclientwindow.h | 80 ++++ src/plugins/platforms/platforms.pro | 2 + 30 files changed, 3523 insertions(+), 2 deletions(-) create mode 100644 config.tests/qpa/mirclient/mirclient.cpp create mode 100644 config.tests/qpa/mirclient/mirclient.pro create mode 100644 src/plugins/platforms/mirclient/mirclient.json create mode 100644 src/plugins/platforms/mirclient/mirclient.pro create mode 100644 src/plugins/platforms/mirclient/qmirclientbackingstore.cpp create mode 100644 src/plugins/platforms/mirclient/qmirclientbackingstore.h create mode 100644 src/plugins/platforms/mirclient/qmirclientclipboard.cpp create mode 100644 src/plugins/platforms/mirclient/qmirclientclipboard.h create mode 100644 src/plugins/platforms/mirclient/qmirclientglcontext.cpp create mode 100644 src/plugins/platforms/mirclient/qmirclientglcontext.h create mode 100644 src/plugins/platforms/mirclient/qmirclientinput.cpp create mode 100644 src/plugins/platforms/mirclient/qmirclientinput.h create mode 100644 src/plugins/platforms/mirclient/qmirclientintegration.cpp create mode 100644 src/plugins/platforms/mirclient/qmirclientintegration.h create mode 100644 src/plugins/platforms/mirclient/qmirclientlogging.h create mode 100644 src/plugins/platforms/mirclient/qmirclientnativeinterface.cpp create mode 100644 src/plugins/platforms/mirclient/qmirclientnativeinterface.h create mode 100644 src/plugins/platforms/mirclient/qmirclientorientationchangeevent_p.h create mode 100644 src/plugins/platforms/mirclient/qmirclientplatformservices.cpp create mode 100644 src/plugins/platforms/mirclient/qmirclientplatformservices.h create mode 100644 src/plugins/platforms/mirclient/qmirclientplugin.cpp create mode 100644 src/plugins/platforms/mirclient/qmirclientplugin.h create mode 100644 src/plugins/platforms/mirclient/qmirclientscreen.cpp create mode 100644 src/plugins/platforms/mirclient/qmirclientscreen.h create mode 100644 src/plugins/platforms/mirclient/qmirclienttheme.cpp create mode 100644 src/plugins/platforms/mirclient/qmirclienttheme.h create mode 100644 src/plugins/platforms/mirclient/qmirclientwindow.cpp create mode 100644 src/plugins/platforms/mirclient/qmirclientwindow.h diff --git a/config.tests/qpa/mirclient/mirclient.cpp b/config.tests/qpa/mirclient/mirclient.cpp new file mode 100644 index 0000000000..c7148e82f9 --- /dev/null +++ b/config.tests/qpa/mirclient/mirclient.cpp @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the config.tests of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include + +static void surfaceCreateCallback(MirSurface*, void*) +{ +} + + +int main(int, char **) +{ + u_application_lifecycle_delegate_new(); + mir_surface_create(0, surfaceCreateCallback, 0); +} diff --git a/config.tests/qpa/mirclient/mirclient.pro b/config.tests/qpa/mirclient/mirclient.pro new file mode 100644 index 0000000000..b397c2d08a --- /dev/null +++ b/config.tests/qpa/mirclient/mirclient.pro @@ -0,0 +1,4 @@ +SOURCES = mirclient.cpp +CONFIG += link_pkgconfig +PKGCONFIG += egl mirclient ubuntu-platform-api +CONFIG -= qt diff --git a/configure b/configure index ed352d4c95..9aac48e15c 100755 --- a/configure +++ b/configure @@ -689,6 +689,7 @@ CFG_EGLFS_VIV=no CFG_DIRECTFB=auto CFG_LINUXFB=auto CFG_KMS=auto +CFG_MIRCLIENT=auto CFG_LIBUDEV=auto CFG_LIBINPUT=auto CFG_OBSOLETE_WAYLAND=no @@ -1850,6 +1851,13 @@ while [ "$#" -gt 0 ]; do UNKNOWN_OPT=yes fi ;; + mirclient) + if [ "$VAL" = "yes" ] || [ "$VAL" = "no" ]; then + CFG_MIRCLIENT="$VAL" + else + UNKNOWN_OPT=yes + fi + ;; libudev) if [ "$VAL" = "yes" ] || [ "$VAL" = "no" ]; then CFG_LIBUDEV="$VAL" @@ -2650,6 +2658,9 @@ Additional options: -no-kms ............ Do not compile KMS support. * -kms ............... Compile KMS support (Requires EGL). + * -no-mirclient....... Do not compile Mir client support. + -mirclient.......... Compile Mir client support. + -qpa ......... Sets the default QPA platform (e.g xcb, cocoa, windows). -xplatform target ... The target platform when cross-compiling. @@ -5269,6 +5280,7 @@ ORIG_CFG_EGLFS="$CFG_EGLFS" ORIG_CFG_DIRECTFB="$CFG_DIRECTFB" ORIG_CFG_LINUXFB="$CFG_LINUXFB" ORIG_CFG_KMS="$CFG_KMS" +ORIG_CFG_MIRCLIENT="$CFG_MIRCLIENT" if [ "$CFG_LIBUDEV" != "no" ]; then if [ -n "$PKG_CONFIG" ] && $PKG_CONFIG --exists libudev 2>/dev/null; then @@ -5551,6 +5563,20 @@ if [ "$CFG_KMS" != "no" ]; then fi fi +if [ "$CFG_MIRCLIENT" != "no" ]; then + if compileTest qpa/mirclient "Mir client"; then + CFG_MIRCLIENT=yes + elif [ "$CFG_MIRCLIENT" = "yes" ]; then + echo " Mir client support cannot be enabled due to functionality tests!" + echo " Turn on verbose messaging (-v) to $0 to see the final report." + echo " If you believe this message is in error you may use the continue" + echo " switch (-continue) to $0 to continue." + exit 101 + else + CFG_MIRCLIENT=no + fi +fi + # Detect libxkbcommon MIN_REQ_XKBCOMMON="0.4.1" # currently only xcb platform plugin supports building xkbcommon @@ -5721,11 +5747,14 @@ fi if [ "$CFG_KMS" = "yes" ]; then QT_CONFIG="$QT_CONFIG kms" fi +if [ "$CFG_MIRCLIENT" = "yes" ]; then + QT_CONFIG="$QT_CONFIG mirclient" +fi if [ "$XPLATFORM_MAC" = "no" ] && [ "$XPLATFORM_MINGW" = "no" ] && [ "$XPLATFORM_QNX" = "no" ] && [ "$XPLATFORM_ANDROID" = "no" ] && [ "$XPLATFORM_HAIKU" = "no" ]; then - if [ "$CFG_XCB" = "no" ] && [ "$CFG_EGLFS" = "no" ] && [ "$CFG_DIRECTFB" = "no" ] && [ "$CFG_LINUXFB" = "no" ] && [ "$CFG_KMS" = "no" ]; then + if [ "$CFG_XCB" = "no" ] && [ "$CFG_EGLFS" = "no" ] && [ "$CFG_DIRECTFB" = "no" ] && [ "$CFG_LINUXFB" = "no" ] && [ "$CFG_KMS" = "no" ] && [ "$CFG_MIRCLIENT" = "no" ]; then if [ "$QPA_PLATFORM_GUARD" = "yes" ] && - ( [ "$ORIG_CFG_XCB" = "auto" ] || [ "$ORIG_CFG_EGLFS" = "auto" ] || [ "$ORIG_CFG_DIRECTFB" = "auto" ] || [ "$ORIG_CFG_LINUXFB" = "auto" ] || [ "$ORIG_CFG_KMS" = "auto" ] ); then + ( [ "$ORIG_CFG_XCB" = "auto" ] || [ "$ORIG_CFG_EGLFS" = "auto" ] || [ "$ORIG_CFG_DIRECTFB" = "auto" ] || [ "$ORIG_CFG_LINUXFB" = "auto" ] || [ "$ORIG_CFG_KMS" = "auto" ] || [ "$ORIG_CFG_MIRCLIENT" = "auto" ] ); then echo "No QPA platform plugin enabled!" echo " If you really want to build without a QPA platform plugin you must pass" echo " -no-qpa-platform-guard to configure. Doing this will" @@ -7142,6 +7171,7 @@ report_support " EGLFS Mali ........." "$CFG_EGLFS_MALI" report_support " EGLFS Raspberry Pi ." "$CFG_EGLFS_BRCM" report_support " EGLFS X11 .........." "$CFG_EGL_X" report_support " LinuxFB .............." "$CFG_LINUXFB" +report_support " Mir client............" "$CFG_MIRCLIENT" report_support " XCB .................." "$CFG_XCB" system "system library" qt "bundled copy" if [ "$CFG_XCB" != "no" ]; then report_support " EGL on X ..........." "$CFG_EGL_X" diff --git a/src/plugins/platforms/mirclient/mirclient.json b/src/plugins/platforms/mirclient/mirclient.json new file mode 100644 index 0000000000..c31558a2f1 --- /dev/null +++ b/src/plugins/platforms/mirclient/mirclient.json @@ -0,0 +1,3 @@ +{ + "Keys": [ "mirclient" ] +} diff --git a/src/plugins/platforms/mirclient/mirclient.pro b/src/plugins/platforms/mirclient/mirclient.pro new file mode 100644 index 0000000000..033ce579b9 --- /dev/null +++ b/src/plugins/platforms/mirclient/mirclient.pro @@ -0,0 +1,47 @@ +TARGET = mirclient +TEMPLATE = lib + +PLUGIN_TYPE = platforms +PLUGIN_CLASS_NAME = MirServerIntegrationPlugin +!equals(TARGET, $$QT_DEFAULT_QPA_PLUGIN): PLUGIN_EXTENDS = - +load(qt_plugin) + +QT += core-private gui-private platformsupport-private dbus + +CONFIG += 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 = \ + qmirclientbackingstore.cpp \ + qmirclientclipboard.cpp \ + qmirclientglcontext.cpp \ + qmirclientinput.cpp \ + qmirclientintegration.cpp \ + qmirclientnativeinterface.cpp \ + qmirclientplatformservices.cpp \ + qmirclientplugin.cpp \ + qmirclientscreen.cpp \ + qmirclienttheme.cpp \ + qmirclientwindow.cpp + +HEADERS = \ + qmirclientbackingstore.h \ + qmirclientclipboard.h \ + qmirclientglcontext.h \ + qmirclientinput.h \ + qmirclientintegration.h \ + qmirclientlogging.h \ + qmirclientnativeinterface.h \ + qmirclientorientationchangeevent_p.h \ + qmirclientplatformservices.h \ + qmirclientplugin.h \ + qmirclientscreen.h \ + qmirclienttheme.h \ + qmirclientwindow.h diff --git a/src/plugins/platforms/mirclient/qmirclientbackingstore.cpp b/src/plugins/platforms/mirclient/qmirclientbackingstore.cpp new file mode 100644 index 0000000000..daa0b229ec --- /dev/null +++ b/src/plugins/platforms/mirclient/qmirclientbackingstore.cpp @@ -0,0 +1,146 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Canonical, Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qmirclientbackingstore.h" +#include "qmirclientlogging.h" +#include +#include +#include +#include +#include + +QMirClientBackingStore::QMirClientBackingStore(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); +} + +QMirClientBackingStore::~QMirClientBackingStore() +{ +} + +void QMirClientBackingStore::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 QMirClientBackingStore::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(); + + 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 QMirClientBackingStore::beginPaint(const QRegion& region) +{ + mDirty |= region; +} + +void QMirClientBackingStore::resize(const QSize& size, const QRegion& /*staticContents*/) +{ + mImage = QImage(size, QImage::Format_RGB32); + + if (mTexture->isCreated()) + mTexture->destroy(); +} + +QPaintDevice* QMirClientBackingStore::paintDevice() +{ + return &mImage; +} diff --git a/src/plugins/platforms/mirclient/qmirclientbackingstore.h b/src/plugins/platforms/mirclient/qmirclientbackingstore.h new file mode 100644 index 0000000000..22b8bf9bc5 --- /dev/null +++ b/src/plugins/platforms/mirclient/qmirclientbackingstore.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Canonical, Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QMIRCLIENTBACKINGSTORE_H +#define QMIRCLIENTBACKINGSTORE_H + +#include + +class QOpenGLContext; +class QOpenGLTexture; +class QOpenGLTextureBlitter; + +class QMirClientBackingStore : public QPlatformBackingStore +{ +public: + QMirClientBackingStore(QWindow* window); + virtual ~QMirClientBackingStore(); + + // 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 mContext; + QScopedPointer mTexture; + QScopedPointer mBlitter; + QImage mImage; + QRegion mDirty; +}; + +#endif // QMIRCLIENTBACKINGSTORE_H diff --git a/src/plugins/platforms/mirclient/qmirclientclipboard.cpp b/src/plugins/platforms/mirclient/qmirclientclipboard.cpp new file mode 100644 index 0000000000..aa2ddf2103 --- /dev/null +++ b/src/plugins/platforms/mirclient/qmirclientclipboard.cpp @@ -0,0 +1,305 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Canonical, Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qmirclientclipboard.h" + +#include +#include +#include +#include +#include + +// 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 + +} + +QMirClientClipboard::QMirClientClipboard() + : mMimeData(new QMimeData) + , mIsOutdated(true) + , mUpdatesDisabled(false) + , mDBusSetupDone(false) +{ +} + +QMirClientClipboard::~QMirClientClipboard() +{ + delete mMimeData; +} + +void QMirClientClipboard::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 QMirClientClipboard::onDBusClipboardGetContentsFinished(QDBusPendingCallWatcher* call) +{ + Q_ASSERT(call == mPendingGetContentsCall.data()); + + QDBusPendingReply reply = *call; + if (reply.isError()) { + qCritical("QMirClientClipboard - Failed to get system clipboard contents via D-Bus. %s, %s", + qPrintable(reply.error().name()), qPrintable(reply.error().message())); + // TODO: Might try again later a number of times... + } else { + QByteArray serializedMimeData = reply.argumentAt<0>(); + updateMimeData(serializedMimeData); + } + call->deleteLater(); +} + +void QMirClientClipboard::onDBusClipboardSetContentsFinished(QDBusPendingCallWatcher *call) +{ + QDBusPendingReply reply = *call; + if (reply.isError()) { + qCritical("QMirClientClipboard - Failed to set the system clipboard contents via D-Bus. %s, %s", + qPrintable(reply.error().name()), qPrintable(reply.error().message())); + // TODO: Might try again later a number of times... + } + call->deleteLater(); +} + +void QMirClientClipboard::updateMimeData(const QByteArray &serializedMimeData) +{ + if (mUpdatesDisabled) + return; + + QMimeData *newMimeData = deserializeMimeData(serializedMimeData); + if (newMimeData) { + delete mMimeData; + mMimeData = newMimeData; + mIsOutdated = false; + emitChanged(QClipboard::Clipboard); + } else { + qWarning("QMirClientClipboard - Got invalid serialized mime data. Ignoring it."); + } +} + +void QMirClientClipboard::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("QMirClientClipboard - 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 QMirClientClipboard::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(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("QMirClientClipboard: Not sending contents (%d bytes) to the global clipboard as it's" + " bigger than the maximum allowed size of %d bytes", bufferSize, maxBufferSize); + } + + return serializedMimeData; +} + +QMimeData *QMirClientClipboard::deserializeMimeData(const QByteArray &serializedMimeData) const +{ + if (static_cast(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(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* QMirClientClipboard::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 QMirClientClipboard::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 QMirClientClipboard::supportsMode(QClipboard::Mode mode) const +{ + return mode == QClipboard::Clipboard; +} + +bool QMirClientClipboard::ownsMode(QClipboard::Mode mode) const +{ + Q_UNUSED(mode); + return false; +} + +void QMirClientClipboard::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/qmirclientclipboard.h b/src/plugins/platforms/mirclient/qmirclientclipboard.h new file mode 100644 index 0000000000..d3d3d400d2 --- /dev/null +++ b/src/plugins/platforms/mirclient/qmirclientclipboard.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Canonical, Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QMIRCLIENTCLIPBOARD_H +#define QMIRCLIENTCLIPBOARD_H + +#include + +#include +#include +class QDBusInterface; +class QDBusPendingCallWatcher; + +class QMirClientClipboard : public QObject, public QPlatformClipboard +{ + Q_OBJECT +public: + QMirClientClipboard(); + virtual ~QMirClientClipboard(); + + // 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 mDBusClipboard; + + QPointer mPendingGetContentsCall; + QPointer mPendingSetContentsCall; + + bool mUpdatesDisabled; + bool mDBusSetupDone; +}; + +#endif // QMIRCLIENTCLIPBOARD_H diff --git a/src/plugins/platforms/mirclient/qmirclientglcontext.cpp b/src/plugins/platforms/mirclient/qmirclientglcontext.cpp new file mode 100644 index 0000000000..bfba5051e5 --- /dev/null +++ b/src/plugins/platforms/mirclient/qmirclientglcontext.cpp @@ -0,0 +1,156 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Canonical, Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qmirclientglcontext.h" +#include "qmirclientwindow.h" +#include "qmirclientlogging.h" +#include + +#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 +} + +QMirClientOpenGLContext::QMirClientOpenGLContext(QMirClientScreen* screen, QMirClientOpenGLContext* share) +{ + ASSERT(screen != NULL); + mEglDisplay = screen->eglDisplay(); + mScreen = screen; + + // Create an OpenGL ES 2 context. + QVector 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); +} + +QMirClientOpenGLContext::~QMirClientOpenGLContext() +{ + ASSERT(eglDestroyContext(mEglDisplay, mEglContext) == EGL_TRUE); +} + +bool QMirClientOpenGLContext::makeCurrent(QPlatformSurface* surface) +{ + DASSERT(surface->surface()->surfaceType() == QSurface::OpenGLSurface); + EGLSurface eglSurface = static_cast(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 QMirClientOpenGLContext::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 QMirClientOpenGLContext::swapBuffers(QPlatformSurface* surface) +{ + QMirClientWindow *ubuntuWindow = static_cast(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 (*QMirClientOpenGLContext::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/qmirclientglcontext.h b/src/plugins/platforms/mirclient/qmirclientglcontext.h new file mode 100644 index 0000000000..cc40298259 --- /dev/null +++ b/src/plugins/platforms/mirclient/qmirclientglcontext.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Canonical, Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QMIRCLIENTGLCONTEXT_H +#define QMIRCLIENTGLCONTEXT_H + +#include +#include "qmirclientscreen.h" + +class QMirClientOpenGLContext : public QPlatformOpenGLContext +{ +public: + QMirClientOpenGLContext(QMirClientScreen* screen, QMirClientOpenGLContext* share); + virtual ~QMirClientOpenGLContext(); + + // QPlatformOpenGLContext methods. + QSurfaceFormat format() const override { return mScreen->surfaceFormat(); } + void swapBuffers(QPlatformSurface* surface) override; + bool makeCurrent(QPlatformSurface* surface) override; + void doneCurrent() override; + bool isValid() const override { return mEglContext != EGL_NO_CONTEXT; } + void (*getProcAddress(const QByteArray& procName)) (); + + EGLContext eglContext() const { return mEglContext; } + +private: + QMirClientScreen* mScreen; + EGLContext mEglContext; + EGLDisplay mEglDisplay; +}; + +#endif // QMIRCLIENTGLCONTEXT_H diff --git a/src/plugins/platforms/mirclient/qmirclientinput.cpp b/src/plugins/platforms/mirclient/qmirclientinput.cpp new file mode 100644 index 0000000000..56bc21f420 --- /dev/null +++ b/src/plugins/platforms/mirclient/qmirclientinput.cpp @@ -0,0 +1,520 @@ +/**************************************************************************** +** +** Copyright (C) 2014-2015 Canonical, Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +// Local +#include "qmirclientinput.h" +#include "qmirclientintegration.h" +#include "qmirclientnativeinterface.h" +#include "qmirclientscreen.h" +#include "qmirclientwindow.h" +#include "qmirclientlogging.h" +#include "qmirclientorientationchangeevent_p.h" + +// Qt +#if !defined(QT_NO_DEBUG) +#include +#endif +#include +#include +#include +#include +#include + +#include +#include + +#include + +#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 QMirClientEvent : public QEvent +{ +public: + QMirClientEvent(QMirClientWindow* window, const MirEvent *event, QEvent::Type type) + : QEvent(type), window(window) { + nativeEvent = mir_event_ref(event); + } + ~QMirClientEvent() + { + mir_event_unref(nativeEvent); + } + + QPointer window; + const MirEvent *nativeEvent; +}; + +QMirClientInput::QMirClientInput(QMirClientClientIntegration* integration) + : QObject(nullptr) + , mIntegration(integration) + , mEventFilterType(static_cast( + integration->nativeInterface())->genericEventFilterType()) + , mEventType(static_cast(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); +} + +QMirClientInput::~QMirClientInput() +{ + // Qt will take care of deleting mTouchDevice. +} + +#if (LOG_EVENTS != 0) +static const char* nativeEventTypeToStr(MirEventType t) +{ + switch (t) + { + case mir_event_type_key: + return "mir_event_type_key"; + 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 QMirClientInput::customEvent(QEvent* event) +{ + DASSERT(QThread::currentThread() == thread()); + QMirClientEvent* ubuntuEvent = static_cast(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(static_cast(nativeEvent)), &result) == true) { + DLOG("event filtered out by native interface"); + return; + } + + #if (LOG_EVENTS != 0) + LOG("QMirClientInput::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(mir_event_get_type(nativeEvent))); + } +} + +void QMirClientInput::postEvent(QMirClientWindow *platformWindow, const MirEvent *event) +{ + QWindow *window = platformWindow->window(); + + QCoreApplication::postEvent(this, new QMirClientEvent( + platformWindow, event, mEventType)); + + if ((window->flags().testFlag(Qt::WindowTransparentForInput)) && window->parent()) { + QCoreApplication::postEvent(this, new QMirClientEvent( + static_cast(platformWindow->QPlatformWindow::parent()), + event, mEventType)); + } +} + +void QMirClientInput::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 QMirClientInput::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 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 QMirClientInput::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 QMirClientInput::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 QMirClientInput::dispatchOrientationEvent(QWindow *window, const MirOrientationEvent *event) +{ + MirOrientation mir_orientation = mir_orientation_event_get_direction(event); + #if (LOG_EVENTS != 0) + // Orientation event logging. + LOG("ORIENTATION direction: %s", nativeOrientationDirectionToStr(mir_orientation)); + #endif + + if (!window->screen()) { + DLOG("Window has no associated screen, dropping orientation event"); + return; + } + + OrientationChangeEvent::Orientation orientation; + switch (mir_orientation) { + case mir_orientation_normal: + orientation = OrientationChangeEvent::TopUp; + break; + case mir_orientation_left: + orientation = OrientationChangeEvent::LeftUp; + break; + case mir_orientation_inverted: + orientation = OrientationChangeEvent::TopDown; + break; + case mir_orientation_right: + orientation = OrientationChangeEvent::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(window->screen()->handle()), + new OrientationChangeEvent(OrientationChangeEvent::mType, orientation)); +} + diff --git a/src/plugins/platforms/mirclient/qmirclientinput.h b/src/plugins/platforms/mirclient/qmirclientinput.h new file mode 100644 index 0000000000..c987d18c12 --- /dev/null +++ b/src/plugins/platforms/mirclient/qmirclientinput.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2014-2015 Canonical, Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QMIRCLIENTINPUT_H +#define QMIRCLIENTINPUT_H + +// Qt +#include + +#include + +class QMirClientClientIntegration; +class QMirClientWindow; + +class QMirClientInput : public QObject +{ + Q_OBJECT + +public: + QMirClientInput(QMirClientClientIntegration* integration); + virtual ~QMirClientInput(); + + // QObject methods. + void customEvent(QEvent* event) override; + + void postEvent(QMirClientWindow* window, const MirEvent *event); + QMirClientClientIntegration* 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: + QMirClientClientIntegration* mIntegration; + QTouchDevice* mTouchDevice; + const QByteArray mEventFilterType; + const QEvent::Type mEventType; +}; + +#endif // QMIRCLIENTINPUT_H diff --git a/src/plugins/platforms/mirclient/qmirclientintegration.cpp b/src/plugins/platforms/mirclient/qmirclientintegration.cpp new file mode 100644 index 0000000000..a234f4eac6 --- /dev/null +++ b/src/plugins/platforms/mirclient/qmirclientintegration.cpp @@ -0,0 +1,264 @@ +/**************************************************************************** +** +** Copyright (C) 2014-2015 Canonical, Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +// Qt +#include +#include +#include +#include +#include +#include +#include +#include + +// Local +#include "qmirclientbackingstore.h" +#include "qmirclientclipboard.h" +#include "qmirclientglcontext.h" +#include "qmirclientinput.h" +#include "qmirclientintegration.h" +#include "qmirclientlogging.h" +#include "qmirclientnativeinterface.h" +#include "qmirclientscreen.h" +#include "qmirclienttheme.h" +#include "qmirclientwindow.h" + +// platform-api +#include +#include +#include + +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); + QMirClientClientIntegration* integration = static_cast(context); + integration->inputContext()->hideInputPanel(); + QCoreApplication::postEvent(QCoreApplication::instance(), + new QEvent(QEvent::ApplicationDeactivate)); +} + +QMirClientClientIntegration::QMirClientClientIntegration() + : QPlatformIntegration() + , mNativeInterface(new QMirClientNativeInterface) + , mFontDb(new QGenericUnixFontDatabase) + , mServices(new QMirClientPlatformServices) + , mClipboard(new QMirClientClipboard) + , mScaleFactor(1.0) +{ + setupOptions(); + setupDescription(); + + // Create new application instance + mInstance = u_application_instance_new_from_description_with_options(mDesc, mOptions); + + if (mInstance == nullptr) + qFatal("QMirClientClientIntegration: 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 QMirClientScreen(u_application_instance_get_mir_connection(mInstance)); + screenAdded(mScreen); + + // Initialize input. + if (qEnvironmentVariableIsEmpty("QTUBUNTU_NO_INPUT")) { + mInput = new QMirClientInput(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(gridUnit) / defaultGridUnit; +} + +QMirClientClientIntegration::~QMirClientClientIntegration() +{ + delete mInput; + delete mInputContext; + delete mScreen; + delete mServices; +} + +QPlatformServices *QMirClientClientIntegration::services() const +{ + return mServices; +} + +void QMirClientClientIntegration::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 QMirClientClientIntegration::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* QMirClientClientIntegration::createPlatformWindow(QWindow* window) const +{ + return const_cast(this)->createPlatformWindow(window); +} + +QPlatformWindow* QMirClientClientIntegration::createPlatformWindow(QWindow* window) +{ + QPlatformWindow* platformWindow = new QMirClientWindow( + window, mClipboard, static_cast(mScreen), mInput, u_application_instance_get_mir_connection(mInstance)); + platformWindow->requestActivateWindow(); + return platformWindow; +} + +bool QMirClientClientIntegration::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 *QMirClientClientIntegration::createEventDispatcher() const +{ + return createUnixEventDispatcher(); +} + +QPlatformBackingStore* QMirClientClientIntegration::createPlatformBackingStore(QWindow* window) const +{ + return new QMirClientBackingStore(window); +} + +QPlatformOpenGLContext* QMirClientClientIntegration::createPlatformOpenGLContext( + QOpenGLContext* context) const +{ + return const_cast(this)->createPlatformOpenGLContext(context); +} + +QPlatformOpenGLContext* QMirClientClientIntegration::createPlatformOpenGLContext( + QOpenGLContext* context) +{ + return new QMirClientOpenGLContext(static_cast(context->screen()->handle()), + static_cast(context->shareHandle())); +} + +QStringList QMirClientClientIntegration::themeNames() const +{ + return QStringList(QMirClientTheme::name); +} + +QPlatformTheme* QMirClientClientIntegration::createPlatformTheme(const QString& name) const +{ + Q_UNUSED(name); + return new QMirClientTheme; +} + +QVariant QMirClientClientIntegration::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* QMirClientClientIntegration::clipboard() const +{ + return mClipboard.data(); +} diff --git a/src/plugins/platforms/mirclient/qmirclientintegration.h b/src/plugins/platforms/mirclient/qmirclientintegration.h new file mode 100644 index 0000000000..2960209691 --- /dev/null +++ b/src/plugins/platforms/mirclient/qmirclientintegration.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Canonical, Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QMIRCLIENTINTEGRATION_H +#define QMIRCLIENTINTEGRATION_H + +#include +#include + +#include "qmirclientplatformservices.h" + +// platform-api +#include +#include + +class QMirClientClipboard; +class QMirClientInput; +class QMirClientScreen; + +class QMirClientClientIntegration : public QPlatformIntegration { +public: + QMirClientClientIntegration(); + virtual ~QMirClientClientIntegration(); + + // 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); + QMirClientScreen* screen() const { return mScreen; } + +private: + void setupOptions(); + void setupDescription(); + + QPlatformNativeInterface* mNativeInterface; + QPlatformFontDatabase* mFontDb; + + QMirClientPlatformServices* mServices; + + QMirClientScreen* mScreen; + QMirClientInput* mInput; + QPlatformInputContext* mInputContext; + QSharedPointer mClipboard; + qreal mScaleFactor; + + // Platform API stuff + UApplicationOptions* mOptions; + UApplicationDescription* mDesc; + UApplicationInstance* mInstance; +}; + +#endif // QMIRCLIENTINTEGRATION_H diff --git a/src/plugins/platforms/mirclient/qmirclientlogging.h b/src/plugins/platforms/mirclient/qmirclientlogging.h new file mode 100644 index 0000000000..80914d28b9 --- /dev/null +++ b/src/plugins/platforms/mirclient/qmirclientlogging.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Canonical, Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QMIRCLIENTLOGGING_H +#define QMIRCLIENTLOGGING_H + +// Logging and assertion macros. +#define LOG(...) qDebug(__VA_ARGS__) +#define LOG_IF(cond,...) do { if (cond) qDebug(__VA_ARGS__); } while (0) +#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 // QMIRCLIENTLOGGING_H diff --git a/src/plugins/platforms/mirclient/qmirclientnativeinterface.cpp b/src/plugins/platforms/mirclient/qmirclientnativeinterface.cpp new file mode 100644 index 0000000000..a0bb932df3 --- /dev/null +++ b/src/plugins/platforms/mirclient/qmirclientnativeinterface.cpp @@ -0,0 +1,134 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Canonical, Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +// Qt +#include +#include +#include +#include + +// Local +#include "qmirclientnativeinterface.h" +#include "qmirclientscreen.h" +#include "qmirclientglcontext.h" + +class QMirClientResourceMap : public QMap +{ +public: + QMirClientResourceMap() + : QMap() { + insert("egldisplay", QMirClientNativeInterface::EglDisplay); + insert("eglcontext", QMirClientNativeInterface::EglContext); + insert("nativeorientation", QMirClientNativeInterface::NativeOrientation); + insert("display", QMirClientNativeInterface::Display); + } +}; + +Q_GLOBAL_STATIC(QMirClientResourceMap, ubuntuResourceMap) + +QMirClientNativeInterface::QMirClientNativeInterface() + : mGenericEventFilterType(QByteArrayLiteral("Event")) + , mNativeOrientation(nullptr) +{ +} + +QMirClientNativeInterface::~QMirClientNativeInterface() +{ + delete mNativeOrientation; + mNativeOrientation = nullptr; +} + +void* QMirClientNativeInterface::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 == QMirClientNativeInterface::EglContext) + return static_cast(context->handle())->eglContext(); + else + return nullptr; +} + +void* QMirClientNativeInterface::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 == QMirClientNativeInterface::EglDisplay) { + if (window) { + return static_cast(window->screen()->handle())->eglDisplay(); + } else { + return static_cast( + QGuiApplication::primaryScreen()->handle())->eglDisplay(); + } + } else if (kResourceType == QMirClientNativeInterface::NativeOrientation) { + // Return the device's native screen orientation. + if (window) { + QMirClientScreen *ubuntuScreen = static_cast(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* QMirClientNativeInterface::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 == QMirClientNativeInterface::Display) { + if (!screen) + screen = QGuiApplication::primaryScreen(); + return static_cast(screen->handle())->eglNativeDisplay(); + } else + return NULL; +} diff --git a/src/plugins/platforms/mirclient/qmirclientnativeinterface.h b/src/plugins/platforms/mirclient/qmirclientnativeinterface.h new file mode 100644 index 0000000000..84f03bb915 --- /dev/null +++ b/src/plugins/platforms/mirclient/qmirclientnativeinterface.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Canonical, Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QMIRCLIENTNATIVEINTERFACE_H +#define QMIRCLIENTNATIVEINTERFACE_H + +#include + +class QMirClientNativeInterface : public QPlatformNativeInterface { +public: + enum ResourceType { EglDisplay, EglContext, NativeOrientation, Display }; + + QMirClientNativeInterface(); + ~QMirClientNativeInterface(); + + // 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 // QMIRCLIENTNATIVEINTERFACE_H diff --git a/src/plugins/platforms/mirclient/qmirclientorientationchangeevent_p.h b/src/plugins/platforms/mirclient/qmirclientorientationchangeevent_p.h new file mode 100644 index 0000000000..24d7307faa --- /dev/null +++ b/src/plugins/platforms/mirclient/qmirclientorientationchangeevent_p.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Canonical, Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QMIRCLIENTORIENTATIONCHANGEEVENT_P_H +#define QMIRCLIENTORIENTATIONCHANGEEVENT_P_H + +#include +#include "qmirclientlogging.h" + +class OrientationChangeEvent : public QEvent { +public: + enum Orientation { + Undefined = 0, + TopUp, + TopDown, + LeftUp, + RightUp, + FaceUp, + FaceDown + }; + + OrientationChangeEvent(QEvent::Type type, Orientation orientation) + : QEvent(type) + , mOrientation(orientation) + { + } + + static const QEvent::Type mType; + Orientation mOrientation; +}; + +#endif // QMIRCLIENTORIENTATIONCHANGEEVENT_P_H diff --git a/src/plugins/platforms/mirclient/qmirclientplatformservices.cpp b/src/plugins/platforms/mirclient/qmirclientplatformservices.cpp new file mode 100644 index 0000000000..d0260c79d3 --- /dev/null +++ b/src/plugins/platforms/mirclient/qmirclientplatformservices.cpp @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Canonical, Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qmirclientplatformservices.h" + +#include + +#include +#include + +bool QMirClientPlatformServices::openUrl(const QUrl &url) +{ + return callDispatcher(url); +} + +bool QMirClientPlatformServices::openDocument(const QUrl &url) +{ + return callDispatcher(url); +} + +bool QMirClientPlatformServices::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/qmirclientplatformservices.h b/src/plugins/platforms/mirclient/qmirclientplatformservices.h new file mode 100644 index 0000000000..64a0432d06 --- /dev/null +++ b/src/plugins/platforms/mirclient/qmirclientplatformservices.h @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Canonical, Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QMIRCLIENTPLATFORMSERVICES_H +#define QMIRCLIENTPLATFORMSERVICES_H + +#include +#include +#include + +class QMirClientPlatformServices : public QPlatformServices { +public: + bool openUrl(const QUrl &url) override; + bool openDocument(const QUrl &url) override; + +private: + bool callDispatcher(const QUrl &url); +}; + +#endif // QMIRCLIENTPLATFORMSERVICES_H diff --git a/src/plugins/platforms/mirclient/qmirclientplugin.cpp b/src/plugins/platforms/mirclient/qmirclientplugin.cpp new file mode 100644 index 0000000000..43d913f8d2 --- /dev/null +++ b/src/plugins/platforms/mirclient/qmirclientplugin.cpp @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Canonical, Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qmirclientplugin.h" +#include "qmirclientintegration.h" + +QStringList QMirClientIntegrationPlugin::keys() const +{ + QStringList list; + list << "mirclient"; + return list; +} + +QPlatformIntegration* QMirClientIntegrationPlugin::create(const QString &system, + const QStringList &) +{ + if (system.toLower() == "mirclient") { +#ifdef PLATFORM_API_TOUCH + setenv("UBUNTU_PLATFORM_API_BACKEND", "touch_mirclient", 1); +#else + setenv("UBUNTU_PLATFORM_API_BACKEND", "desktop_mirclient", 1); +#endif + return new QMirClientClientIntegration; + } else { + return 0; + } +} diff --git a/src/plugins/platforms/mirclient/qmirclientplugin.h b/src/plugins/platforms/mirclient/qmirclientplugin.h new file mode 100644 index 0000000000..a6f1a1081a --- /dev/null +++ b/src/plugins/platforms/mirclient/qmirclientplugin.h @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Canonical, Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QMIRCLIENTPLUGIN_H +#define QMIRCLIENTPLUGIN_H + +#include + +class QMirClientIntegrationPlugin : public QPlatformIntegrationPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QPlatformIntegrationFactoryInterface_iid FILE "mirclient.json") + +public: + QStringList keys() const; + QPlatformIntegration* create(const QString&, const QStringList&); +}; + +#endif // QMIRCLIENTPLUGIN_H diff --git a/src/plugins/platforms/mirclient/qmirclientscreen.cpp b/src/plugins/platforms/mirclient/qmirclientscreen.cpp new file mode 100644 index 0000000000..5c4b1cd0d6 --- /dev/null +++ b/src/plugins/platforms/mirclient/qmirclientscreen.cpp @@ -0,0 +1,296 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Canonical, Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include + +// Qt +#include +#include +#include +#include +#include +#include + +// local +#include "qmirclientscreen.h" +#include "qmirclientlogging.h" +#include "qmirclientorientationchangeevent_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(value)); + } +} +#endif + + +const QEvent::Type OrientationChangeEvent::mType = + static_cast(QEvent::registerEventType()); + +static const MirDisplayOutput *find_active_output( + const MirDisplayConfiguration *conf) +{ + const MirDisplayOutput *output = NULL; + for (uint32_t d = 0; d < conf->num_outputs; d++) + { + const MirDisplayOutput *out = conf->outputs + d; + + if (out->used && + out->connected && + out->num_modes && + out->current_mode < out->num_modes) + { + output = out; + break; + } + } + + return output; +} + +QMirClientScreen::QMirClientScreen(MirConnection *connection) + : mFormat(QImage::Format_RGB32) + , 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; + 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("QQMirClientScreen::QQMirClientScreen (this=%p)", this); + + // Set the default orientation based on the initial screen dimmensions. + mNativeOrientation = (mGeometry.width() >= mGeometry.height()) ? Qt::LandscapeOrientation : Qt::PortraitOrientation; + + // If it's a landscape device (i.e. some tablets), start in landscape, otherwise portrait + mCurrentOrientation = (mNativeOrientation == Qt::LandscapeOrientation) ? Qt::LandscapeOrientation : Qt::PortraitOrientation; +} + +QMirClientScreen::~QMirClientScreen() +{ + eglTerminate(mEglDisplay); +} + +void QMirClientScreen::customEvent(QEvent* event) { + DASSERT(QThread::currentThread() == thread()); + + OrientationChangeEvent* oReadingEvent = static_cast(event); + switch (oReadingEvent->mOrientation) { + case OrientationChangeEvent::LeftUp: { + mCurrentOrientation = (screen()->primaryOrientation() == Qt::LandscapeOrientation) ? + Qt::InvertedPortraitOrientation : Qt::LandscapeOrientation; + break; + } + case OrientationChangeEvent::TopUp: { + mCurrentOrientation = (screen()->primaryOrientation() == Qt::LandscapeOrientation) ? + Qt::LandscapeOrientation : Qt::PortraitOrientation; + break; + } + case OrientationChangeEvent::RightUp: { + mCurrentOrientation = (screen()->primaryOrientation() == Qt::LandscapeOrientation) ? + Qt::PortraitOrientation : Qt::InvertedLandscapeOrientation; + break; + } + case OrientationChangeEvent::TopDown: { + mCurrentOrientation = (screen()->primaryOrientation() == Qt::LandscapeOrientation) ? + Qt::InvertedLandscapeOrientation : Qt::InvertedPortraitOrientation; + break; + } + default: { + DLOG("QMirClientScreen::customEvent - Unknown orientation."); + return; + } + } + + // Raise the event signal so that client apps know the orientation changed + DLOG("QMirClientScreen::customEvent - handling orientation change to %s", orientationToStr(mCurrentOrientation)); + QWindowSystemInterface::handleScreenOrientationChange(screen(), mCurrentOrientation); +} + +void QMirClientScreen::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("QMirClientScreen::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("QMirClientScreen::handleWindowSurfaceResize - new orientation %s",orientationToStr(mCurrentOrientation)); + QWindowSystemInterface::handleScreenOrientationChange(screen(), mCurrentOrientation); + } +} diff --git a/src/plugins/platforms/mirclient/qmirclientscreen.h b/src/plugins/platforms/mirclient/qmirclientscreen.h new file mode 100644 index 0000000000..5d9325354f --- /dev/null +++ b/src/plugins/platforms/mirclient/qmirclientscreen.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Canonical, Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QMIRCLIENTSCREEN_H +#define QMIRCLIENTSCREEN_H + +#include +#include +#include + +struct MirConnection; + +class QMirClientScreen : public QObject, public QPlatformScreen +{ + Q_OBJECT +public: + QMirClientScreen(MirConnection *connection); + virtual ~QMirClientScreen(); + + // 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 // QMIRCLIENTSCREEN_H diff --git a/src/plugins/platforms/mirclient/qmirclienttheme.cpp b/src/plugins/platforms/mirclient/qmirclienttheme.cpp new file mode 100644 index 0000000000..c15da23945 --- /dev/null +++ b/src/plugins/platforms/mirclient/qmirclienttheme.cpp @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Canonical, Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qmirclienttheme.h" + +#include + +const char *QMirClientTheme::name = "ubuntu"; + +QMirClientTheme::QMirClientTheme() +{ +} + +QMirClientTheme::~QMirClientTheme() +{ +} + +QVariant QMirClientTheme::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/qmirclienttheme.h b/src/plugins/platforms/mirclient/qmirclienttheme.h new file mode 100644 index 0000000000..8f330395a0 --- /dev/null +++ b/src/plugins/platforms/mirclient/qmirclienttheme.h @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Canonical, Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QMIRCLIENTTHEME_H +#define QMIRCLIENTTHEME_H + +#include + +class QMirClientTheme : public QGenericUnixTheme +{ +public: + static const char* name; + QMirClientTheme(); + virtual ~QMirClientTheme(); + + // From QPlatformTheme + QVariant themeHint(ThemeHint hint) const override; +}; + +#endif // QMIRCLIENTTHEME_H diff --git a/src/plugins/platforms/mirclient/qmirclientwindow.cpp b/src/plugins/platforms/mirclient/qmirclientwindow.cpp new file mode 100644 index 0000000000..f3fd1e756d --- /dev/null +++ b/src/plugins/platforms/mirclient/qmirclientwindow.cpp @@ -0,0 +1,452 @@ +/**************************************************************************** +** +** Copyright (C) 2014-2015 Canonical, Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +// Local +#include "qmirclientclipboard.h" +#include "qmirclientinput.h" +#include "qmirclientwindow.h" +#include "qmirclientscreen.h" +#include "qmirclientlogging.h" + +// Qt +#include +#include +#include +#include +#include +#include + +// Platform API +#include + +#include + +#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 QMirClientWindowPrivate +{ +public: + void createEGLSurface(EGLNativeWindowType nativeWindow); + void destroyEGLSurface(); + int panelHeight(); + + QMirClientScreen* screen; + EGLSurface eglSurface; + WId id; + QMirClientInput* input; + Qt::WindowState state; + MirConnection *connection; + MirSurface* surface; + QSize bufferSize; + QMutex mutex; + QSharedPointer clipboard; +}; + +static void eventCallback(MirSurface* surface, const MirEvent *event, void* context) +{ + (void) surface; + DASSERT(context != NULL); + QMirClientWindow* platformWindow = static_cast(context); + platformWindow->priv()->input->postEvent(platformWindow, event); +} + +static void surfaceCreateCallback(MirSurface* surface, void* context) +{ + DASSERT(context != NULL); + QMirClientWindow* platformWindow = static_cast(context); + platformWindow->priv()->surface = surface; + + mir_surface_set_event_handler(surface, eventCallback, context); +} + +QMirClientWindow::QMirClientWindow(QWindow* w, QSharedPointer clipboard, QMirClientScreen* screen, + QMirClientInput* input, MirConnection* connection) + : QObject(nullptr), QPlatformWindow(w) +{ + DASSERT(screen != NULL); + + d = new QMirClientWindowPrivate; + 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("QMirClientWindow::QMirClientWindow (this=%p, w=%p, screen=%p, input=%p)", this, w, screen, input); +} + +QMirClientWindow::~QMirClientWindow() +{ + DLOG("QMirClientWindow::~QMirClientWindow"); + d->destroyEGLSurface(); + + mir_surface_release_sync(d->surface); + + delete d; +} + +void QMirClientWindowPrivate::createEGLSurface(EGLNativeWindowType nativeWindow) +{ + DLOG("QMirClientWindowPrivate::createEGLSurface (this=%p, nativeWindow=%p)", + this, reinterpret_cast(nativeWindow)); + + eglSurface = eglCreateWindowSurface(screen->eglDisplay(), screen->eglConfig(), + nativeWindow, nullptr); + + DASSERT(eglSurface != EGL_NO_SURFACE); +} + +void QMirClientWindowPrivate::destroyEGLSurface() +{ + DLOG("QMirClientWindowPrivate::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 QMirClientWindowPrivate::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(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 QMirClientWindow::createWindow() +{ + DLOG("QMirClientWindow::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(IS_OPAQUE_FLAG) : 0 : 0; + + // FIXME(loicm) Opaque flag is forced for now for non-system sessions (applications) for + // performance reasons. + flags |= static_cast(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(1)) ? "Opaque" : "NotOpaque"); + LOG("title: '%s'", title.constData()); +#endif + + // Get surface geometry. + QRect geometry; + if (d->state == Qt::WindowFullScreen) { + printf("QMirClientWindow - fullscreen geometry\n"); + geometry = screen()->geometry(); + } else if (d->state == Qt::WindowMaximized) { + printf("QMirClientWindow - 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("QMirClientWindow - 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, ¶meters); + + 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 QMirClientWindow::moveResize(const QRect& rect) +{ + (void) rect; + // TODO: Not yet supported by mir. +} + +void QMirClientWindow::handleSurfaceResize(int width, int height) +{ + QMutexLocker(&d->mutex); + LOG("QMirClientWindow::handleSurfaceResize(width=%d, height=%d)", width, height); + + // The current buffer size hasn't actually changed. so just render on it and swap + // buffers in the hope that the next buffer will match the surface size advertised + // in this event. + // But since this event is processed by a thread different from the one that swaps + // buffers, you can never know if this information is already outdated as there's + // no synchronicity whatsoever between the processing of resize events and the + // consumption of buffers. + if (d->bufferSize.width() != width || d->bufferSize.height() != height) { + QWindowSystemInterface::handleExposeEvent(window(), geometry()); + QWindowSystemInterface::flushWindowSystemEvents(); + } +} + +void QMirClientWindow::handleSurfaceFocusChange(bool focused) +{ + LOG("QMirClientWindow::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 QMirClientWindow::setWindowState(Qt::WindowState state) +{ + QMutexLocker(&d->mutex); + DLOG("QMirClientWindow::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 QMirClientWindow::setGeometry(const QRect& rect) +{ + DLOG("QMirClientWindow::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 QMirClientWindow::setVisible(bool visible) +{ + QMutexLocker(&d->mutex); + DLOG("QMirClientWindow::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* QMirClientWindow::eglSurface() const +{ + return d->eglSurface; +} + +WId QMirClientWindow::winId() const +{ + return d->id; +} + +void QMirClientWindow::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("QMirClientWindow::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()); + } +} diff --git a/src/plugins/platforms/mirclient/qmirclientwindow.h b/src/plugins/platforms/mirclient/qmirclientwindow.h new file mode 100644 index 0000000000..f342669544 --- /dev/null +++ b/src/plugins/platforms/mirclient/qmirclientwindow.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2014-2015 Canonical, Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QMIRCLIENTWINDOW_H +#define QMIRCLIENTWINDOW_H + +#include +#include + +#include + +class QMirClientClipboard; +class QMirClientInput; +class QMirClientScreen; +class QMirClientWindowPrivate; + +class QMirClientWindow : public QObject, public QPlatformWindow +{ + Q_OBJECT +public: + QMirClientWindow(QWindow *w, QSharedPointer clipboard, QMirClientScreen *screen, + QMirClientInput *input, MirConnection *mir_connection); + virtual ~QMirClientWindow(); + + // 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); + + QMirClientWindowPrivate* priv() { return d; } + +private: + void createWindow(); + void moveResize(const QRect& rect); + + QMirClientWindowPrivate *d; +}; + +#endif // QMIRCLIENTWINDOW_H diff --git a/src/plugins/platforms/platforms.pro b/src/plugins/platforms/platforms.pro index 22d443733e..43bb04e318 100644 --- a/src/plugins/platforms/platforms.pro +++ b/src/plugins/platforms/platforms.pro @@ -40,3 +40,5 @@ contains(QT_CONFIG, linuxfb): SUBDIRS += linuxfb haiku { SUBDIRS += haiku } + +contains(QT_CONFIG, mirclient): SUBDIRS += mirclient -- cgit v1.2.3