Mirclient: update based on upstream development in lp:qtubuntu

This is based on revision 360 of lp:qtubuntu.

Main features/bugs fixed:
- fix QQuickWidget-based app rendering
- wire up Qt window types to Mir to enable desktop-based applications
  to function with a window manager
- use QEGLPlatformContext and QEGLPBuffer instead of custom code
- correctly populate and update list of QScreens
- support for switching keyboard layouts
- improve window resizing to fix visual glitching

Change-Id: If816a858eb10b6356275d4b80c89a72562b3c29f
Reviewed-by: Eirik Aavitsland <eirik.aavitsland@qt.io>
Reviewed-by: Matti Paaso <matti.paaso@qt.io>
This commit is contained in:
Gerry Boland 2016-11-02 16:46:53 +00:00 committed by Matti Paaso
parent 4b507e8257
commit c28fde3fda
31 changed files with 2199 additions and 1084 deletions

View File

@ -168,7 +168,7 @@
"label": "Mir client libraries", "label": "Mir client libraries",
"test": "qpa/mirclient", "test": "qpa/mirclient",
"sources": [ "sources": [
{ "type": "pkgConfig", "args": "egl mirclient ubuntu-platform-api" } { "type": "pkgConfig", "args": "egl mirclient ubuntu-platform-api libcontent-hub" }
] ]
}, },
"mtdev": { "mtdev": {

View File

@ -5,6 +5,9 @@ QT += \
theme_support-private eventdispatcher_support-private \ theme_support-private eventdispatcher_support-private \
fontdatabase_support-private egl_support-private fontdatabase_support-private egl_support-private
qtHaveModule(linuxaccessibility_support-private): \
QT += linuxaccessibility_support-private
DEFINES += MESA_EGL_NO_X11_HEADERS DEFINES += MESA_EGL_NO_X11_HEADERS
# CONFIG += c++11 # only enables C++0x # CONFIG += c++11 # only enables C++0x
QMAKE_CXXFLAGS += -fvisibility=hidden -fvisibility-inlines-hidden -std=c++11 -Werror -Wall QMAKE_CXXFLAGS += -fvisibility=hidden -fvisibility-inlines-hidden -std=c++11 -Werror -Wall
@ -13,9 +16,12 @@ QMAKE_LFLAGS += -std=c++11 -Wl,-no-undefined
QMAKE_USE_PRIVATE += mirclient QMAKE_USE_PRIVATE += mirclient
SOURCES = \ SOURCES = \
qmirclientappstatecontroller.cpp \
qmirclientbackingstore.cpp \ qmirclientbackingstore.cpp \
qmirclientclipboard.cpp \ qmirclientclipboard.cpp \
qmirclientcursor.cpp \ qmirclientcursor.cpp \
qmirclientdebugextension.cpp \
qmirclientdesktopwindow.cpp \
qmirclientglcontext.cpp \ qmirclientglcontext.cpp \
qmirclientinput.cpp \ qmirclientinput.cpp \
qmirclientintegration.cpp \ qmirclientintegration.cpp \
@ -23,13 +29,17 @@ SOURCES = \
qmirclientplatformservices.cpp \ qmirclientplatformservices.cpp \
qmirclientplugin.cpp \ qmirclientplugin.cpp \
qmirclientscreen.cpp \ qmirclientscreen.cpp \
qmirclientscreenobserver.cpp \
qmirclienttheme.cpp \ qmirclienttheme.cpp \
qmirclientwindow.cpp qmirclientwindow.cpp
HEADERS = \ HEADERS = \
qmirclientappstatecontroller.h \
qmirclientbackingstore.h \ qmirclientbackingstore.h \
qmirclientclipboard.h \ qmirclientclipboard.h \
qmirclientcursor.h \ qmirclientcursor.h \
qmirclientdebugextension.h \
qmirclientdesktopwindow.h \
qmirclientglcontext.h \ qmirclientglcontext.h \
qmirclientinput.h \ qmirclientinput.h \
qmirclientintegration.h \ qmirclientintegration.h \
@ -39,9 +49,17 @@ HEADERS = \
qmirclientplatformservices.h \ qmirclientplatformservices.h \
qmirclientplugin.h \ qmirclientplugin.h \
qmirclientscreen.h \ qmirclientscreen.h \
qmirclientscreenobserver.h \
qmirclienttheme.h \ qmirclienttheme.h \
qmirclientwindow.h qmirclientwindow.h
# libxkbcommon
!qtConfig(xkbcommon-system) {
include(../../../3rdparty/xkbcommon.pri)
} else {
QMAKE_USE += xkbcommon
}
PLUGIN_TYPE = platforms PLUGIN_TYPE = platforms
PLUGIN_CLASS_NAME = MirServerIntegrationPlugin PLUGIN_CLASS_NAME = MirServerIntegrationPlugin
!equals(TARGET, $$QT_DEFAULT_QPA_PLUGIN): PLUGIN_EXTENDS = - !equals(TARGET, $$QT_DEFAULT_QPA_PLUGIN): PLUGIN_EXTENDS = -

View File

@ -0,0 +1,102 @@
/****************************************************************************
**
** Copyright (C) 2016 Canonical, Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qmirclientappstatecontroller.h"
#include <qpa/qwindowsysteminterface.h>
/*
* QMirClientAppStateController - updates Qt's QApplication::applicationState property.
*
* Tries to avoid active-inactive-active invocations using a timer. The rapid state
* change can confuse some applications.
*/
QMirClientAppStateController::QMirClientAppStateController()
: m_suspended(false)
, m_lastActive(true)
{
m_inactiveTimer.setSingleShot(true);
m_inactiveTimer.setInterval(10);
QObject::connect(&m_inactiveTimer, &QTimer::timeout, []()
{
QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationInactive);
});
}
void QMirClientAppStateController::setSuspended()
{
m_inactiveTimer.stop();
if (!m_suspended) {
m_suspended = true;
QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationSuspended);
}
}
void QMirClientAppStateController::setResumed()
{
m_inactiveTimer.stop();
if (m_suspended) {
m_suspended = false;
if (m_lastActive) {
QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationActive);
} else {
QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationInactive);
}
}
}
void QMirClientAppStateController::setWindowFocused(bool focused)
{
if (m_suspended) {
return;
}
if (focused) {
m_inactiveTimer.stop();
QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationActive);
} else {
m_inactiveTimer.start();
}
m_lastActive = focused;
}

View File

@ -0,0 +1,62 @@
/****************************************************************************
**
** Copyright (C) 2016 Canonical, Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QMIRCLIENTAPPSTATECONTROLLER_H
#define QMIRCLIENTAPPSTATECONTROLLER_H
#include <QTimer>
class QMirClientAppStateController
{
public:
QMirClientAppStateController();
void setSuspended();
void setResumed();
void setWindowFocused(bool focused);
private:
bool m_suspended;
bool m_lastActive;
QTimer m_inactiveTimer;
};
#endif // QMIRCLIENTAPPSTATECONTROLLER_H

View File

@ -61,6 +61,7 @@ QMirClientBackingStore::QMirClientBackingStore(QWindow* window)
QMirClientBackingStore::~QMirClientBackingStore() QMirClientBackingStore::~QMirClientBackingStore()
{ {
mContext->makeCurrent(window()); // needed as QOpenGLTexture destructor assumes current context
} }
void QMirClientBackingStore::flush(QWindow* window, const QRegion& region, const QPoint& offset) void QMirClientBackingStore::flush(QWindow* window, const QRegion& region, const QPoint& offset)
@ -76,7 +77,6 @@ void QMirClientBackingStore::flush(QWindow* window, const QRegion& region, const
mBlitter->create(); mBlitter->create();
mBlitter->bind(); mBlitter->bind();
mBlitter->setRedBlueSwizzle(true);
mBlitter->blit(mTexture->textureId(), QMatrix4x4(), QOpenGLTextureBlitter::OriginTopLeft); mBlitter->blit(mTexture->textureId(), QMatrix4x4(), QOpenGLTextureBlitter::OriginTopLeft);
mBlitter->release(); mBlitter->release();
@ -137,7 +137,9 @@ void QMirClientBackingStore::beginPaint(const QRegion& region)
void QMirClientBackingStore::resize(const QSize& size, const QRegion& /*staticContents*/) void QMirClientBackingStore::resize(const QSize& size, const QRegion& /*staticContents*/)
{ {
mImage = QImage(size, QImage::Format_RGB32); mImage = QImage(size, QImage::Format_RGBA8888);
mContext->makeCurrent(window());
if (mTexture->isCreated()) if (mTexture->isCreated())
mTexture->destroy(); mTexture->destroy();
@ -147,3 +149,9 @@ QPaintDevice* QMirClientBackingStore::paintDevice()
{ {
return &mImage; return &mImage;
} }
QImage QMirClientBackingStore::toImage() const
{
// used by QPlatformBackingStore::composeAndFlush
return mImage;
}

View File

@ -58,6 +58,7 @@ public:
void flush(QWindow* window, const QRegion& region, const QPoint& offset) override; void flush(QWindow* window, const QRegion& region, const QPoint& offset) override;
void resize(const QSize& size, const QRegion& staticContents) override; void resize(const QSize& size, const QRegion& staticContents) override;
QPaintDevice* paintDevice() override; QPaintDevice* paintDevice() override;
QImage toImage() const override;
protected: protected:
void updateTexture(); void updateTexture();

View File

@ -39,41 +39,36 @@
#include "qmirclientclipboard.h" #include "qmirclientclipboard.h"
#include "qmirclientlogging.h"
#include "qmirclientwindow.h"
#include <QDBusPendingCallWatcher>
#include <QGuiApplication>
#include <QSignalBlocker>
#include <QtCore/QMimeData> #include <QtCore/QMimeData>
#include <QtCore/QStringList> #include <QtCore/QStringList>
#include <QDBusInterface>
#include <QDBusPendingCallWatcher>
#include <QDBusPendingReply>
// FIXME(loicm) The clipboard data format is not defined by Ubuntu Platform API // content-hub
// which makes it impossible to have non-Qt applications communicate with Qt #include <com/ubuntu/content/hub.h>
// 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: // get this cumbersome nested namespace out of the way
// number of mime types (sizeof(int)) using namespace com::ubuntu::content;
// 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() QMirClientClipboard::QMirClientClipboard()
: mMimeData(new QMimeData) : mMimeData(new QMimeData)
, mIsOutdated(true) , mContentHub(Hub::Client::instance())
, mUpdatesDisabled(false)
, mDBusSetupDone(false)
{ {
connect(mContentHub, &Hub::pasteboardChanged, this, [this]() {
if (mClipboardState == QMirClientClipboard::SyncedClipboard) {
mClipboardState = QMirClientClipboard::OutdatedClipboard;
emitChanged(QClipboard::Clipboard);
}
});
connect(qGuiApp, &QGuiApplication::applicationStateChanged,
this, &QMirClientClipboard::onApplicationStateChanged);
requestMimeData();
} }
QMirClientClipboard::~QMirClientClipboard() QMirClientClipboard::~QMirClientClipboard()
@ -81,202 +76,39 @@ QMirClientClipboard::~QMirClientClipboard()
delete mMimeData; delete mMimeData;
} }
void QMirClientClipboard::requestDBusClipboardContents()
{
if (!mDBusSetupDone) {
setupDBus();
}
if (!mPendingGetContentsCall.isNull())
return;
QDBusPendingCall pendingCall = mDBusClipboard->asyncCall(QStringLiteral("GetContents"));
mPendingGetContentsCall = new QDBusPendingCallWatcher(pendingCall, this);
QObject::connect(mPendingGetContentsCall.data(), &QDBusPendingCallWatcher::finished,
this, &QMirClientClipboard::onDBusClipboardGetContentsFinished);
}
void QMirClientClipboard::onDBusClipboardGetContentsFinished(QDBusPendingCallWatcher* call)
{
Q_ASSERT(call == mPendingGetContentsCall.data());
QDBusPendingReply<QByteArray> reply = *call;
if (Q_UNLIKELY(reply.isError())) {
qCritical("QMirClientClipboard - Failed to get system clipboard contents via D-Bus. %s, %s",
qPrintable(reply.error().name()), qPrintable(reply.error().message()));
// TODO: Might try again later a number of times...
} else {
QByteArray serializedMimeData = reply.argumentAt<0>();
updateMimeData(serializedMimeData);
}
call->deleteLater();
}
void QMirClientClipboard::onDBusClipboardSetContentsFinished(QDBusPendingCallWatcher *call)
{
QDBusPendingReply<void> reply = *call;
if (Q_UNLIKELY(reply.isError())) {
qCritical("QMirClientClipboard - Failed to set the system clipboard contents via D-Bus. %s, %s",
qPrintable(reply.error().name()), qPrintable(reply.error().message()));
// TODO: Might try again later a number of times...
}
call->deleteLater();
}
void QMirClientClipboard::updateMimeData(const QByteArray &serializedMimeData)
{
if (mUpdatesDisabled)
return;
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(
QStringLiteral("com.canonical.QtMir"),
QStringLiteral("/com/canonical/QtMir/Clipboard"),
QStringLiteral("com.canonical.QtMir.Clipboard"),
QStringLiteral("ContentsChanged"),
this, SLOT(updateMimeData(QByteArray)));
if (Q_UNLIKELY(!ok))
qCritical("QMirClientClipboard - Failed to connect to ContentsChanged signal form the D-Bus system clipboard.");
mDBusClipboard = new QDBusInterface(QStringLiteral("com.canonical.QtMir"),
QStringLiteral("/com/canonical/QtMir/Clipboard"),
QStringLiteral("com.canonical.QtMir.Clipboard"),
dbusConnection);
mDBusSetupDone = true;
}
QByteArray QMirClientClipboard::serializeMimeData(QMimeData *mimeData) const
{
Q_ASSERT(mimeData != nullptr);
const QStringList formats = mimeData->formats();
const int formatCount = qMin(formats.size(), maxFormatsCount);
const int headerSize = sizeof(int) + (formatCount * 4 * sizeof(int));
int bufferSize = headerSize;
for (int i = 0; i < formatCount; i++)
bufferSize += formats[i].size() + mimeData->data(formats[i]).size();
QByteArray serializedMimeData;
if (bufferSize <= maxBufferSize) {
// Serialize data.
serializedMimeData.resize(bufferSize);
{
char *buffer = serializedMimeData.data();
int* header = reinterpret_cast<int*>(serializedMimeData.data());
int offset = headerSize;
header[0] = formatCount;
for (int i = 0; i < formatCount; i++) {
const QByteArray data = mimeData->data(formats[i]);
const int formatOffset = offset;
const int formatSize = formats[i].size();
const int dataOffset = offset + formatSize;
const int dataSize = data.size();
memcpy(&buffer[formatOffset], formats[i].toLatin1().data(), formatSize);
memcpy(&buffer[dataOffset], data.data(), dataSize);
header[i*4+1] = formatOffset;
header[i*4+2] = formatSize;
header[i*4+3] = dataOffset;
header[i*4+4] = dataSize;
offset += formatSize + dataSize;
}
}
} else {
qWarning("QMirClientClipboard: Not sending contents (%d bytes) to the global clipboard as it's"
" bigger than the maximum allowed size of %d bytes", bufferSize, maxBufferSize);
}
return serializedMimeData;
}
QMimeData *QMirClientClipboard::deserializeMimeData(const QByteArray &serializedMimeData) const
{
if (static_cast<std::size_t>(serializedMimeData.size()) < sizeof(int)) {
// Data is invalid
return nullptr;
}
QMimeData *mimeData = new QMimeData;
const char* const buffer = serializedMimeData.constData();
const int* const header = reinterpret_cast<const int*>(serializedMimeData.constData());
const int count = qMin(header[0], maxFormatsCount);
for (int i = 0; i < count; i++) {
const int formatOffset = header[i*4+1];
const int formatSize = header[i*4+2];
const int dataOffset = header[i*4+3];
const int dataSize = header[i*4+4];
if (formatOffset + formatSize <= serializedMimeData.size()
&& dataOffset + dataSize <= serializedMimeData.size()) {
QString mimeType = QString::fromLatin1(&buffer[formatOffset], formatSize);
QByteArray mimeDataBytes(&buffer[dataOffset], dataSize);
mimeData->setData(mimeType, mimeDataBytes);
}
}
return mimeData;
}
QMimeData* QMirClientClipboard::mimeData(QClipboard::Mode mode) QMimeData* QMirClientClipboard::mimeData(QClipboard::Mode mode)
{ {
if (mode != QClipboard::Clipboard) if (mode != QClipboard::Clipboard)
return nullptr; return nullptr;
if (mIsOutdated && mPendingGetContentsCall.isNull()) { // Blocks dataChanged() signal from being emitted. Makes no sense to emit it from
requestDBusClipboardContents(); // inside the data getter.
const QSignalBlocker blocker(this);
if (mClipboardState == OutdatedClipboard) {
updateMimeData();
} else if (mClipboardState == SyncingClipboard) {
mPasteReply->waitForFinished();
} }
// 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; return mMimeData;
} }
void QMirClientClipboard::setMimeData(QMimeData* mimeData, QClipboard::Mode mode) void QMirClientClipboard::setMimeData(QMimeData* mimeData, QClipboard::Mode mode)
{ {
if (mode != QClipboard::Clipboard) QWindow *focusWindow = QGuiApplication::focusWindow();
return; if (focusWindow && mode == QClipboard::Clipboard && mimeData != nullptr) {
QString surfaceId = static_cast<QMirClientWindow*>(focusWindow->handle())->persistentSurfaceId();
if (!mPendingGetContentsCall.isNull()) { QDBusPendingCall reply = mContentHub->createPaste(surfaceId, *mimeData);
// Ignore whatever comes from the system clipboard as we are going to change it anyway
QObject::disconnect(mPendingGetContentsCall.data(), 0, this, 0);
mUpdatesDisabled = true;
mPendingGetContentsCall->waitForFinished();
mUpdatesDisabled = false;
delete mPendingGetContentsCall.data();
}
if (mimeData != nullptr) { // Don't care whether it succeeded
QByteArray serializedMimeData = serializeMimeData(mimeData); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply, this);
if (!serializedMimeData.isEmpty()) { connect(watcher, &QDBusPendingCallWatcher::finished,
setDBusClipboardContents(serializedMimeData); watcher, &QObject::deleteLater);
}
mMimeData = mimeData; mMimeData = mimeData;
mClipboardState = SyncedClipboard;
emitChanged(QClipboard::Clipboard); emitChanged(QClipboard::Clipboard);
} }
} }
@ -292,25 +124,58 @@ bool QMirClientClipboard::ownsMode(QClipboard::Mode mode) const
return false; return false;
} }
void QMirClientClipboard::setDBusClipboardContents(const QByteArray &clipboardContents) void QMirClientClipboard::onApplicationStateChanged(Qt::ApplicationState state)
{ {
if (!mDBusSetupDone) { if (state == Qt::ApplicationActive) {
setupDBus(); // Only focused or active applications might be allowed to paste, so we probably
// missed changes in the clipboard while we were hidden, inactive or, more importantly,
// suspended.
requestMimeData();
} }
}
if (!mPendingSetContentsCall.isNull()) {
// Ignore any previous set call as we are going to overwrite it anyway void QMirClientClipboard::updateMimeData()
QObject::disconnect(mPendingSetContentsCall.data(), 0, this, 0); {
mUpdatesDisabled = true; if (qGuiApp->applicationState() != Qt::ApplicationActive) {
mPendingSetContentsCall->waitForFinished(); // Don't even bother asking as content-hub would probably ignore our request (and should).
mUpdatesDisabled = false; return;
delete mPendingSetContentsCall.data(); }
}
delete mMimeData;
QDBusPendingCall pendingCall = mDBusClipboard->asyncCall(QStringLiteral("SetContents"), clipboardContents);
QWindow *focusWindow = QGuiApplication::focusWindow();
mPendingSetContentsCall = new QDBusPendingCallWatcher(pendingCall, this); if (focusWindow) {
QString surfaceId = static_cast<QMirClientWindow*>(focusWindow->handle())->persistentSurfaceId();
QObject::connect(mPendingSetContentsCall.data(), &QDBusPendingCallWatcher::finished, mMimeData = mContentHub->latestPaste(surfaceId);
this, &QMirClientClipboard::onDBusClipboardSetContentsFinished); mClipboardState = SyncedClipboard;
emitChanged(QClipboard::Clipboard);
}
}
void QMirClientClipboard::requestMimeData()
{
if (qGuiApp->applicationState() != Qt::ApplicationActive) {
// Don't even bother asking as content-hub would probably ignore our request (and should).
return;
}
QWindow *focusWindow = QGuiApplication::focusWindow();
if (!focusWindow) {
return;
}
QString surfaceId = static_cast<QMirClientWindow*>(focusWindow->handle())->persistentSurfaceId();
QDBusPendingCall reply = mContentHub->requestLatestPaste(surfaceId);
mClipboardState = SyncingClipboard;
mPasteReply = new QDBusPendingCallWatcher(reply, this);
connect(mPasteReply, &QDBusPendingCallWatcher::finished,
this, [this]() {
delete mMimeData;
mMimeData = mContentHub->paste(*mPasteReply);
mClipboardState = SyncedClipboard;
mPasteReply->deleteLater();
mPasteReply = nullptr;
emitChanged(QClipboard::Clipboard);
});
} }

View File

@ -45,7 +45,15 @@
#include <QMimeData> #include <QMimeData>
#include <QPointer> #include <QPointer>
class QDBusInterface;
namespace com {
namespace ubuntu {
namespace content {
class Hub;
}
}
}
class QDBusPendingCallWatcher; class QDBusPendingCallWatcher;
class QMirClientClipboard : public QObject, public QPlatformClipboard class QMirClientClipboard : public QObject, public QPlatformClipboard
@ -61,31 +69,24 @@ public:
bool supportsMode(QClipboard::Mode mode) const override; bool supportsMode(QClipboard::Mode mode) const override;
bool ownsMode(QClipboard::Mode mode) const override; bool ownsMode(QClipboard::Mode mode) const override;
void requestDBusClipboardContents();
private Q_SLOTS: private Q_SLOTS:
void onDBusClipboardGetContentsFinished(QDBusPendingCallWatcher*); void onApplicationStateChanged(Qt::ApplicationState state);
void onDBusClipboardSetContentsFinished(QDBusPendingCallWatcher*);
void updateMimeData(const QByteArray &serializedMimeData);
private: private:
void setupDBus(); void updateMimeData();
void requestMimeData();
QByteArray serializeMimeData(QMimeData *mimeData) const;
QMimeData *deserializeMimeData(const QByteArray &serializedMimeData) const;
void setDBusClipboardContents(const QByteArray &clipboardContents);
QMimeData *mMimeData; QMimeData *mMimeData;
bool mIsOutdated;
QPointer<QDBusInterface> mDBusClipboard; enum {
OutdatedClipboard, // Our mimeData is outdated, need to fetch latest from ContentHub
SyncingClipboard, // Our mimeData is outdated and we are waiting for ContentHub to reply with the latest paste
SyncedClipboard // Our mimeData is in sync with what ContentHub has
} mClipboardState{OutdatedClipboard};
QPointer<QDBusPendingCallWatcher> mPendingGetContentsCall; com::ubuntu::content::Hub *mContentHub;
QPointer<QDBusPendingCallWatcher> mPendingSetContentsCall;
bool mUpdatesDisabled; QDBusPendingCallWatcher *mPasteReply{nullptr};
bool mDBusSetupDone;
}; };
#endif // QMIRCLIENTCLIPBOARD_H #endif // QMIRCLIENTCLIPBOARD_H

View File

@ -1,6 +1,6 @@
/**************************************************************************** /****************************************************************************
** **
** Copyright (C) 2015 Canonical, Ltd. ** Copyright (C) 2015-2016 Canonical, Ltd.
** Contact: https://www.qt.io/licensing/ ** Contact: https://www.qt.io/licensing/
** **
** This file is part of the plugins of the Qt Toolkit. ** This file is part of the plugins of the Qt Toolkit.
@ -45,35 +45,41 @@
#include <mir_toolkit/mir_client_library.h> #include <mir_toolkit/mir_client_library.h>
Q_LOGGING_CATEGORY(mirclientCursor, "qt.qpa.mirclient.cursor", QtWarningMsg)
QMirClientCursor::QMirClientCursor(MirConnection *connection) QMirClientCursor::QMirClientCursor(MirConnection *connection)
: mConnection(connection) : mConnection(connection)
{ {
mShapeToCursorName[Qt::ArrowCursor] = "left_ptr"; /*
* TODO: Add the missing cursors to Mir (LP: #1388987)
* Those are the ones without a mir_ prefix, which are X11 cursors
* and won't be understood by any shell other than Unity8.
*/
mShapeToCursorName[Qt::ArrowCursor] = mir_arrow_cursor_name;
mShapeToCursorName[Qt::UpArrowCursor] = "up_arrow"; mShapeToCursorName[Qt::UpArrowCursor] = "up_arrow";
mShapeToCursorName[Qt::CrossCursor] = "cross"; mShapeToCursorName[Qt::CrossCursor] = mir_crosshair_cursor_name;
mShapeToCursorName[Qt::WaitCursor] = "watch"; mShapeToCursorName[Qt::WaitCursor] = mir_busy_cursor_name;
mShapeToCursorName[Qt::IBeamCursor] = "xterm"; mShapeToCursorName[Qt::IBeamCursor] = mir_caret_cursor_name;
mShapeToCursorName[Qt::SizeVerCursor] = "size_ver"; mShapeToCursorName[Qt::SizeVerCursor] = mir_vertical_resize_cursor_name;
mShapeToCursorName[Qt::SizeHorCursor] = "size_hor"; mShapeToCursorName[Qt::SizeHorCursor] = mir_horizontal_resize_cursor_name;
mShapeToCursorName[Qt::SizeBDiagCursor] = "size_bdiag"; mShapeToCursorName[Qt::SizeBDiagCursor] = mir_diagonal_resize_bottom_to_top_cursor_name;
mShapeToCursorName[Qt::SizeFDiagCursor] = "size_fdiag"; mShapeToCursorName[Qt::SizeFDiagCursor] = mir_diagonal_resize_top_to_bottom_cursor_name;
mShapeToCursorName[Qt::SizeAllCursor] = "size_all"; mShapeToCursorName[Qt::SizeAllCursor] = mir_omnidirectional_resize_cursor_name;
mShapeToCursorName[Qt::BlankCursor] = "blank"; mShapeToCursorName[Qt::BlankCursor] = mir_disabled_cursor_name;
mShapeToCursorName[Qt::SplitVCursor] = "split_v"; mShapeToCursorName[Qt::SplitVCursor] = mir_vsplit_resize_cursor_name;
mShapeToCursorName[Qt::SplitHCursor] = "split_h"; mShapeToCursorName[Qt::SplitHCursor] = mir_hsplit_resize_cursor_name;
mShapeToCursorName[Qt::PointingHandCursor] = "hand"; mShapeToCursorName[Qt::PointingHandCursor] = mir_pointing_hand_cursor_name;
mShapeToCursorName[Qt::ForbiddenCursor] = "forbidden"; mShapeToCursorName[Qt::ForbiddenCursor] = "forbidden";
mShapeToCursorName[Qt::WhatsThisCursor] = "whats_this"; mShapeToCursorName[Qt::WhatsThisCursor] = "whats_this";
mShapeToCursorName[Qt::BusyCursor] = "left_ptr_watch"; mShapeToCursorName[Qt::BusyCursor] = "left_ptr_watch";
mShapeToCursorName[Qt::OpenHandCursor] = "openhand"; mShapeToCursorName[Qt::OpenHandCursor] = mir_open_hand_cursor_name;
mShapeToCursorName[Qt::ClosedHandCursor] = "closedhand"; mShapeToCursorName[Qt::ClosedHandCursor] = mir_closed_hand_cursor_name;
mShapeToCursorName[Qt::DragCopyCursor] = "dnd-copy"; mShapeToCursorName[Qt::DragCopyCursor] = "dnd-copy";
mShapeToCursorName[Qt::DragMoveCursor] = "dnd-move"; mShapeToCursorName[Qt::DragMoveCursor] = "dnd-move";
mShapeToCursorName[Qt::DragLinkCursor] = "dnd-link"; mShapeToCursorName[Qt::DragLinkCursor] = "dnd-link";
} }
namespace { namespace {
#if !defined(QT_NO_DEBUG)
const char *qtCursorShapeToStr(Qt::CursorShape shape) const char *qtCursorShapeToStr(Qt::CursorShape shape)
{ {
switch (shape) { switch (shape) {
@ -127,7 +133,6 @@ const char *qtCursorShapeToStr(Qt::CursorShape shape)
return "???"; return "???";
} }
} }
#endif // !defined(QT_NO_DEBUG)
} // anonymous namespace } // anonymous namespace
void QMirClientCursor::changeCursor(QCursor *windowCursor, QWindow *window) void QMirClientCursor::changeCursor(QCursor *windowCursor, QWindow *window)
@ -144,7 +149,7 @@ void QMirClientCursor::changeCursor(QCursor *windowCursor, QWindow *window)
if (windowCursor) { if (windowCursor) {
DLOG("[ubuntumirclient QPA] changeCursor shape=%s, window=%p\n", qtCursorShapeToStr(windowCursor->shape()), window); qCDebug(mirclientCursor, "changeCursor shape=%s, window=%p", qtCursorShapeToStr(windowCursor->shape()), window);
if (!windowCursor->pixmap().isNull()) { if (!windowCursor->pixmap().isNull()) {
configureMirCursorWithPixmapQCursor(surface, *windowCursor); configureMirCursorWithPixmapQCursor(surface, *windowCursor);
} else if (windowCursor->shape() == Qt::BitmapCursor) { } else if (windowCursor->shape() == Qt::BitmapCursor) {

View File

@ -1,6 +1,6 @@
/**************************************************************************** /****************************************************************************
** **
** Copyright (C) 2015 Canonical, Ltd. ** Copyright (C) 2015-2016 Canonical, Ltd.
** Contact: https://www.qt.io/licensing/ ** Contact: https://www.qt.io/licensing/
** **
** This file is part of the plugins of the Qt Toolkit. ** This file is part of the plugins of the Qt Toolkit.

View File

@ -0,0 +1,79 @@
/****************************************************************************
**
** Copyright (C) 2016 Canonical, Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qmirclientdebugextension.h"
#include "qmirclientlogging.h"
// mir client debug
#include <mir_toolkit/debug/surface.h>
Q_LOGGING_CATEGORY(mirclientDebug, "qt.qpa.mirclient.debug")
QMirClientDebugExtension::QMirClientDebugExtension()
: m_mirclientDebug(QStringLiteral("mirclient-debug-extension"), 1)
, m_mapper(nullptr)
{
qCDebug(mirclientDebug) << "NOTICE: Loading mirclient-debug-extension";
m_mapper = (MapperPrototype) m_mirclientDebug.resolve("mir_debug_surface_coords_to_screen");
if (!m_mirclientDebug.isLoaded()) {
qCWarning(mirclientDebug) << "ERROR: mirclient-debug-extension failed to load:"
<< m_mirclientDebug.errorString();
} else if (!m_mapper) {
qCWarning(mirclientDebug) << "ERROR: unable to find required symbols in mirclient-debug-extension:"
<< m_mirclientDebug.errorString();
}
}
QPoint QMirClientDebugExtension::mapSurfacePointToScreen(MirSurface *surface, const QPoint &point)
{
if (!m_mapper) {
return point;
}
QPoint mappedPoint;
bool status = m_mapper(surface, point.x(), point.y(), &mappedPoint.rx(), &mappedPoint.ry());
if (status) {
return mappedPoint;
} else {
return point;
}
}

View File

@ -0,0 +1,63 @@
/****************************************************************************
**
** Copyright (C) 2016 Canonical, Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QMIRCLIENTDEBUGEXTENSION_H
#define QMIRCLIENTDEBUGEXTENSION_H
#include <QPoint>
#include <QLibrary>
struct MirSurface;
typedef bool (*MapperPrototype)(MirSurface* surface, int x, int y, int* screenX, int* screenY);
class QMirClientDebugExtension
{
public:
QMirClientDebugExtension();
QPoint mapSurfacePointToScreen(MirSurface *, const QPoint &point);
private:
QLibrary m_mirclientDebug;
MapperPrototype m_mapper;
};
#endif // QMIRCLIENTDEBUGEXTENSION_H

View File

@ -0,0 +1,50 @@
/****************************************************************************
**
** Copyright (C) 2016 Canonical, Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qmirclientdesktopwindow.h"
// local
#include "qmirclientlogging.h"
QMirClientDesktopWindow::QMirClientDesktopWindow(QWindow *window)
: QPlatformWindow(window)
{
qCDebug(mirclient, "QMirClientDesktopWindow(window=%p)", window);
}

View File

@ -0,0 +1,53 @@
/****************************************************************************
**
** Copyright (C) 2016 Canonical, Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QMIRCLIENTDESKTOPWINDOW_H
#define QMIRCLIENTDESKTOPWINDOW_H
#include <qpa/qplatformwindow.h>
// TODO Implement it. For now it's just an empty, dummy class.
class QMirClientDesktopWindow : public QPlatformWindow
{
public:
QMirClientDesktopWindow(QWindow*);
};
#endif // QMIRCLIENTDESKTOPWINDOW_H

View File

@ -39,123 +39,94 @@
#include "qmirclientglcontext.h" #include "qmirclientglcontext.h"
#include "qmirclientwindow.h"
#include "qmirclientlogging.h" #include "qmirclientlogging.h"
#include <QtEglSupport/private/qeglconvenience_p.h> #include "qmirclientwindow.h"
#include <QtGui/private/qopenglcontext_p.h>
#include <dlfcn.h>
#if !defined(QT_NO_DEBUG) #include <QOpenGLFramebufferObject>
static void printOpenGLESConfig() { #include <QtEglSupport/private/qeglconvenience_p.h>
static bool once = true; #include <QtEglSupport/private/qeglpbuffer_p.h>
if (once) { #include <QtGui/private/qopenglcontext_p.h>
const char* string = (const char*) glGetString(GL_VENDOR);
LOG("OpenGL ES vendor: %s", string); Q_LOGGING_CATEGORY(mirclientGraphics, "qt.qpa.mirclient.graphics", QtWarningMsg)
string = (const char*) glGetString(GL_RENDERER);
LOG("OpenGL ES renderer: %s", string); namespace {
string = (const char*) glGetString(GL_VERSION);
LOG("OpenGL ES version: %s", string); void printEglConfig(EGLDisplay display, EGLConfig config)
string = (const char*) glGetString(GL_SHADING_LANGUAGE_VERSION); {
LOG("OpenGL ES Shading Language version: %s", string); Q_ASSERT(display != EGL_NO_DISPLAY);
string = (const char*) glGetString(GL_EXTENSIONS); Q_ASSERT(config != nullptr);
LOG("OpenGL ES extensions: %s", string);
once = false; const char *string = eglQueryString(display, EGL_VENDOR);
qCDebug(mirclientGraphics, "EGL vendor: %s", string);
string = eglQueryString(display, EGL_VERSION);
qCDebug(mirclientGraphics, "EGL version: %s", string);
string = eglQueryString(display, EGL_EXTENSIONS);
qCDebug(mirclientGraphics, "EGL extensions: %s", string);
qCDebug(mirclientGraphics, "EGL configuration attributes:");
q_printEglConfig(display, config);
}
} // anonymous namespace
QMirClientOpenGLContext::QMirClientOpenGLContext(const QSurfaceFormat &format, QPlatformOpenGLContext *share,
EGLDisplay display)
: QEGLPlatformContext(format, share, display, 0)
{
if (mirclientGraphics().isDebugEnabled()) {
printEglConfig(display, eglConfig());
} }
} }
#endif
static EGLenum api_in_use() static bool needsFBOReadBackWorkaround()
{ {
#ifdef QTUBUNTU_USE_OPENGL static bool set = false;
return EGL_OPENGL_API; static bool needsWorkaround = false;
#else
return EGL_OPENGL_ES_API;
#endif
}
QMirClientOpenGLContext::QMirClientOpenGLContext(QMirClientScreen* screen, QMirClientOpenGLContext* share) if (Q_UNLIKELY(!set)) {
{ const char *rendererString = reinterpret_cast<const char *>(glGetString(GL_RENDERER));
ASSERT(screen != NULL); needsWorkaround = qstrncmp(rendererString, "Mali-400", 8) == 0
mEglDisplay = screen->eglDisplay(); || qstrncmp(rendererString, "Mali-T7", 7) == 0
mScreen = screen; || qstrncmp(rendererString, "PowerVR Rogue G6200", 19) == 0;
set = true;
}
// Create an OpenGL ES 2 context. return needsWorkaround;
QVector<EGLint> attribs;
attribs.append(EGL_CONTEXT_CLIENT_VERSION);
attribs.append(2);
attribs.append(EGL_NONE);
ASSERT(eglBindAPI(api_in_use()) == EGL_TRUE);
mEglContext = eglCreateContext(mEglDisplay, screen->eglConfig(), share ? share->eglContext() : EGL_NO_CONTEXT,
attribs.constData());
DASSERT(mEglContext != EGL_NO_CONTEXT);
}
QMirClientOpenGLContext::~QMirClientOpenGLContext()
{
ASSERT(eglDestroyContext(mEglDisplay, mEglContext) == EGL_TRUE);
} }
bool QMirClientOpenGLContext::makeCurrent(QPlatformSurface* surface) bool QMirClientOpenGLContext::makeCurrent(QPlatformSurface* surface)
{ {
DASSERT(surface->surface()->surfaceType() == QSurface::OpenGLSurface); const bool ret = QEGLPlatformContext::makeCurrent(surface);
EGLSurface eglSurface = static_cast<QMirClientWindow*>(surface)->eglSurface();
#if defined(QT_NO_DEBUG)
eglBindAPI(api_in_use());
eglMakeCurrent(mEglDisplay, eglSurface, eglSurface, mEglContext);
#else
ASSERT(eglBindAPI(api_in_use()) == EGL_TRUE);
ASSERT(eglMakeCurrent(mEglDisplay, eglSurface, eglSurface, mEglContext) == EGL_TRUE);
printOpenGLESConfig();
#endif
// When running on the emulator, shaders will be compiled using a thin wrapper around the desktop drivers. if (Q_LIKELY(ret)) {
// These wrappers might not support the precision qualifiers, so set the workaround flag to true.
const char *rendererString = reinterpret_cast<const char *>(glGetString(GL_RENDERER));
if (rendererString != 0 && qstrncmp(rendererString, "Android Emulator", 16) == 0) {
QOpenGLContextPrivate *ctx_d = QOpenGLContextPrivate::get(context()); QOpenGLContextPrivate *ctx_d = QOpenGLContextPrivate::get(context());
ctx_d->workaround_missingPrecisionQualifiers = true; if (!ctx_d->workaround_brokenFBOReadBack && needsFBOReadBackWorkaround()) {
ctx_d->workaround_brokenFBOReadBack = true;
} }
}
return true; return ret;
} }
void QMirClientOpenGLContext::doneCurrent() // Following method used internally in the base class QEGLPlatformContext to access
// the egl surface of a QPlatformSurface/QMirClientWindow
EGLSurface QMirClientOpenGLContext::eglSurfaceForPlatformSurface(QPlatformSurface *surface)
{ {
#if defined(QT_NO_DEBUG) if (surface->surface()->surfaceClass() == QSurface::Window) {
eglBindAPI(api_in_use()); return static_cast<QMirClientWindow *>(surface)->eglSurface();
eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); } else {
#else return static_cast<QEGLPbuffer *>(surface)->pbuffer();
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) void QMirClientOpenGLContext::swapBuffers(QPlatformSurface* surface)
{ {
QMirClientWindow *ubuntuWindow = static_cast<QMirClientWindow*>(surface); QEGLPlatformContext::swapBuffers(surface);
EGLSurface eglSurface = ubuntuWindow->eglSurface(); if (surface->surface()->surfaceClass() == QSurface::Window) {
#if defined(QT_NO_DEBUG) // notify window on swap completion
eglBindAPI(api_in_use()); auto platformWindow = static_cast<QMirClientWindow *>(surface);
eglSwapBuffers(mEglDisplay, eglSurface); platformWindow->onSwapBuffersDone();
#else }
ASSERT(eglBindAPI(api_in_use()) == EGL_TRUE);
ASSERT(eglSwapBuffers(mEglDisplay, eglSurface) == EGL_TRUE);
#endif
ubuntuWindow->onSwapBuffersDone();
}
QFunctionPointer QMirClientOpenGLContext::getProcAddress(const char *procName)
{
#if defined(QT_NO_DEBUG)
eglBindAPI(api_in_use());
#else
ASSERT(eglBindAPI(api_in_use()) == EGL_TRUE);
#endif
QFunctionPointer proc = (QFunctionPointer) eglGetProcAddress(procName);
if (!proc)
proc = (QFunctionPointer) dlsym(RTLD_DEFAULT, procName);
return proc;
} }

View File

@ -42,28 +42,22 @@
#define QMIRCLIENTGLCONTEXT_H #define QMIRCLIENTGLCONTEXT_H
#include <qpa/qplatformopenglcontext.h> #include <qpa/qplatformopenglcontext.h>
#include "qmirclientscreen.h" #include <QtEglSupport/private/qeglplatformcontext_p.h>
class QMirClientOpenGLContext : public QPlatformOpenGLContext #include <EGL/egl.h>
class QMirClientOpenGLContext : public QEGLPlatformContext
{ {
public: public:
QMirClientOpenGLContext(QMirClientScreen* screen, QMirClientOpenGLContext* share); QMirClientOpenGLContext(const QSurfaceFormat &format, QPlatformOpenGLContext *share,
virtual ~QMirClientOpenGLContext(); EGLDisplay display);
// QPlatformOpenGLContext methods. // QEGLPlatformContext methods.
QSurfaceFormat format() const override { return mScreen->surfaceFormat(); } void swapBuffers(QPlatformSurface *surface) final;
void swapBuffers(QPlatformSurface* surface) override; bool makeCurrent(QPlatformSurface *surface) final;
bool makeCurrent(QPlatformSurface* surface) override;
void doneCurrent() override;
bool isValid() const override { return mEglContext != EGL_NO_CONTEXT; }
QFunctionPointer getProcAddress(const char *procName) override;
EGLContext eglContext() const { return mEglContext; } protected:
EGLSurface eglSurfaceForPlatformSurface(QPlatformSurface *surface) final;
private:
QMirClientScreen* mScreen;
EGLContext mEglContext;
EGLDisplay mEglDisplay;
}; };
#endif // QMIRCLIENTGLCONTEXT_H #endif // QMIRCLIENTGLCONTEXT_H

View File

@ -1,6 +1,6 @@
/**************************************************************************** /****************************************************************************
** **
** Copyright (C) 2014-2015 Canonical, Ltd. ** Copyright (C) 2014-2016 Canonical, Ltd.
** Contact: https://www.qt.io/licensing/ ** Contact: https://www.qt.io/licensing/
** **
** This file is part of the plugins of the Qt Toolkit. ** This file is part of the plugins of the Qt Toolkit.
@ -48,21 +48,23 @@
#include "qmirclientorientationchangeevent_p.h" #include "qmirclientorientationchangeevent_p.h"
// Qt // Qt
#if !defined(QT_NO_DEBUG)
#include <QtCore/QThread> #include <QtCore/QThread>
#endif
#include <QtCore/qglobal.h> #include <QtCore/qglobal.h>
#include <QtCore/QCoreApplication> #include <QtCore/QCoreApplication>
#include <private/qguiapplication_p.h> #include <QtGui/private/qguiapplication_p.h>
#include <qpa/qplatforminputcontext.h> #include <qpa/qplatforminputcontext.h>
#include <qpa/qwindowsysteminterface.h> #include <qpa/qwindowsysteminterface.h>
#include <QTextCodec>
#include <xkbcommon/xkbcommon.h> #include <xkbcommon/xkbcommon.h>
#include <xkbcommon/xkbcommon-keysyms.h> #include <xkbcommon/xkbcommon-keysyms.h>
#include <mir_toolkit/mir_client_library.h> #include <mir_toolkit/mir_client_library.h>
#define LOG_EVENTS 0 Q_LOGGING_CATEGORY(mirclientInput, "qt.qpa.mirclient.input", QtWarningMsg)
namespace
{
// XKB Keysyms which do not map directly to Qt types (i.e. Unicode points) // XKB Keysyms which do not map directly to Qt types (i.e. Unicode points)
static const uint32_t KeyTable[] = { static const uint32_t KeyTable[] = {
@ -134,6 +136,27 @@ static const uint32_t KeyTable[] = {
XKB_KEY_MultipleCandidate, Qt::Key_MultipleCandidate, XKB_KEY_MultipleCandidate, Qt::Key_MultipleCandidate,
XKB_KEY_PreviousCandidate, Qt::Key_PreviousCandidate, XKB_KEY_PreviousCandidate, Qt::Key_PreviousCandidate,
// dead keys
XKB_KEY_dead_grave, Qt::Key_Dead_Grave,
XKB_KEY_dead_acute, Qt::Key_Dead_Acute,
XKB_KEY_dead_circumflex, Qt::Key_Dead_Circumflex,
XKB_KEY_dead_tilde, Qt::Key_Dead_Tilde,
XKB_KEY_dead_macron, Qt::Key_Dead_Macron,
XKB_KEY_dead_breve, Qt::Key_Dead_Breve,
XKB_KEY_dead_abovedot, Qt::Key_Dead_Abovedot,
XKB_KEY_dead_diaeresis, Qt::Key_Dead_Diaeresis,
XKB_KEY_dead_abovering, Qt::Key_Dead_Abovering,
XKB_KEY_dead_doubleacute, Qt::Key_Dead_Doubleacute,
XKB_KEY_dead_caron, Qt::Key_Dead_Caron,
XKB_KEY_dead_cedilla, Qt::Key_Dead_Cedilla,
XKB_KEY_dead_ogonek, Qt::Key_Dead_Ogonek,
XKB_KEY_dead_iota, Qt::Key_Dead_Iota,
XKB_KEY_dead_voiced_sound, Qt::Key_Dead_Voiced_Sound,
XKB_KEY_dead_semivoiced_sound, Qt::Key_Dead_Semivoiced_Sound,
XKB_KEY_dead_belowdot, Qt::Key_Dead_Belowdot,
XKB_KEY_dead_hook, Qt::Key_Dead_Hook,
XKB_KEY_dead_horn, Qt::Key_Dead_Horn,
XKB_KEY_Mode_switch, Qt::Key_Mode_switch, XKB_KEY_Mode_switch, Qt::Key_Mode_switch,
XKB_KEY_script_switch, Qt::Key_Mode_switch, XKB_KEY_script_switch, Qt::Key_Mode_switch,
XKB_KEY_XF86AudioRaiseVolume, Qt::Key_VolumeUp, XKB_KEY_XF86AudioRaiseVolume, Qt::Key_VolumeUp,
@ -144,14 +167,37 @@ static const uint32_t KeyTable[] = {
0, 0 0, 0
}; };
class QMirClientEvent : public QEvent Qt::WindowState mirSurfaceStateToWindowState(MirSurfaceState state)
{
switch (state) {
case mir_surface_state_fullscreen:
return Qt::WindowFullScreen;
case mir_surface_state_maximized:
case mir_surface_state_vertmaximized:
case mir_surface_state_horizmaximized:
return Qt::WindowMaximized;
case mir_surface_state_minimized:
return Qt::WindowMinimized;
case mir_surface_state_hidden:
// We should be handling this state separately.
Q_ASSERT(false);
case mir_surface_state_restored:
case mir_surface_state_unknown:
default:
return Qt::WindowNoState;
}
}
} // namespace
class UbuntuEvent : public QEvent
{ {
public: public:
QMirClientEvent(QMirClientWindow* window, const MirEvent *event, QEvent::Type type) UbuntuEvent(QMirClientWindow* window, const MirEvent *event, QEvent::Type type)
: QEvent(type), window(window) { : QEvent(type), window(window) {
nativeEvent = mir_event_ref(event); nativeEvent = mir_event_ref(event);
} }
~QMirClientEvent() ~UbuntuEvent()
{ {
mir_event_unref(nativeEvent); mir_event_unref(nativeEvent);
} }
@ -166,7 +212,7 @@ QMirClientInput::QMirClientInput(QMirClientClientIntegration* integration)
, mEventFilterType(static_cast<QMirClientNativeInterface*>( , mEventFilterType(static_cast<QMirClientNativeInterface*>(
integration->nativeInterface())->genericEventFilterType()) integration->nativeInterface())->genericEventFilterType())
, mEventType(static_cast<QEvent::Type>(QEvent::registerEventType())) , mEventType(static_cast<QEvent::Type>(QEvent::registerEventType()))
, mLastFocusedWindow(nullptr) , mLastInputWindow(nullptr)
{ {
// Initialize touch device. // Initialize touch device.
mTouchDevice = new QTouchDevice; mTouchDevice = new QTouchDevice;
@ -182,42 +228,47 @@ QMirClientInput::~QMirClientInput()
// Qt will take care of deleting mTouchDevice. // Qt will take care of deleting mTouchDevice.
} }
#if (LOG_EVENTS != 0)
static const char* nativeEventTypeToStr(MirEventType t) static const char* nativeEventTypeToStr(MirEventType t)
{ {
switch (t) switch (t)
{ {
case mir_event_type_key: case mir_event_type_key:
return "mir_event_type_key"; return "key";
case mir_event_type_motion: case mir_event_type_motion:
return "mir_event_type_motion"; return "motion";
case mir_event_type_surface: case mir_event_type_surface:
return "mir_event_type_surface"; return "surface";
case mir_event_type_resize: case mir_event_type_resize:
return "mir_event_type_resize"; return "resize";
case mir_event_type_prompt_session_state_change: case mir_event_type_prompt_session_state_change:
return "mir_event_type_prompt_session_state_change"; return "prompt_session_state_change";
case mir_event_type_orientation: case mir_event_type_orientation:
return "mir_event_type_orientation"; return "orientation";
case mir_event_type_close_surface: case mir_event_type_close_surface:
return "mir_event_type_close_surface"; return "close_surface";
case mir_event_type_input: case mir_event_type_input:
return "mir_event_type_input"; return "input";
case mir_event_type_keymap:
return "keymap";
case mir_event_type_input_configuration:
return "input_configuration";
case mir_event_type_surface_output:
return "surface_output";
case mir_event_type_input_device_state:
return "input_device_state";
default: default:
DLOG("Invalid event type %d", t); return "unknown";
return "invalid";
} }
} }
#endif // LOG_EVENTS != 0
void QMirClientInput::customEvent(QEvent* event) void QMirClientInput::customEvent(QEvent* event)
{ {
DASSERT(QThread::currentThread() == thread()); Q_ASSERT(QThread::currentThread() == thread());
QMirClientEvent* ubuntuEvent = static_cast<QMirClientEvent*>(event); UbuntuEvent* ubuntuEvent = static_cast<UbuntuEvent*>(event);
const MirEvent *nativeEvent = ubuntuEvent->nativeEvent; const MirEvent *nativeEvent = ubuntuEvent->nativeEvent;
if ((ubuntuEvent->window == nullptr) || (ubuntuEvent->window->window() == nullptr)) { if ((ubuntuEvent->window == nullptr) || (ubuntuEvent->window->window() == nullptr)) {
qWarning("Attempted to deliver an event to a non-existent window, ignoring."); qCWarning(mirclient) << "Attempted to deliver an event to a non-existent window, ignoring.";
return; return;
} }
@ -226,13 +277,11 @@ void QMirClientInput::customEvent(QEvent* event)
if (QWindowSystemInterface::handleNativeEvent( if (QWindowSystemInterface::handleNativeEvent(
ubuntuEvent->window->window(), mEventFilterType, ubuntuEvent->window->window(), mEventFilterType,
const_cast<void *>(static_cast<const void *>(nativeEvent)), &result) == true) { const_cast<void *>(static_cast<const void *>(nativeEvent)), &result) == true) {
DLOG("event filtered out by native interface"); qCDebug(mirclient, "event filtered out by native interface");
return; return;
} }
#if (LOG_EVENTS != 0) qCDebug(mirclientInput, "customEvent(type=%s)", nativeEventTypeToStr(mir_event_get_type(nativeEvent)));
LOG("QMirClientInput::customEvent(type=%s)", nativeEventTypeToStr(mir_event_get_type(nativeEvent)));
#endif
// Event dispatching. // Event dispatching.
switch (mir_event_get_type(nativeEvent)) switch (mir_event_get_type(nativeEvent))
@ -242,43 +291,30 @@ void QMirClientInput::customEvent(QEvent* event)
break; break;
case mir_event_type_resize: case mir_event_type_resize:
{ {
Q_ASSERT(ubuntuEvent->window->screen() == mIntegration->screen());
auto resizeEvent = mir_event_get_resize_event(nativeEvent); auto resizeEvent = mir_event_get_resize_event(nativeEvent);
mIntegration->screen()->handleWindowSurfaceResize( // Enable workaround for Screen rotation
auto const targetWindow = ubuntuEvent->window;
if (targetWindow) {
auto const screen = static_cast<QMirClientScreen*>(targetWindow->screen());
if (screen) {
screen->handleWindowSurfaceResize(
mir_resize_event_get_width(resizeEvent), mir_resize_event_get_width(resizeEvent),
mir_resize_event_get_height(resizeEvent)); mir_resize_event_get_height(resizeEvent));
}
ubuntuEvent->window->handleSurfaceResized(mir_resize_event_get_width(resizeEvent), targetWindow->handleSurfaceResized(
mir_resize_event_get_width(resizeEvent),
mir_resize_event_get_height(resizeEvent)); mir_resize_event_get_height(resizeEvent));
}
break; break;
} }
case mir_event_type_surface: case mir_event_type_surface:
{ handleSurfaceEvent(ubuntuEvent->window, mir_event_get_surface_event(nativeEvent));
auto surfaceEvent = mir_event_get_surface_event(nativeEvent); break;
if (mir_surface_event_get_attribute(surfaceEvent) == mir_surface_attrib_focus) { case mir_event_type_surface_output:
const bool focused = mir_surface_event_get_attribute_value(surfaceEvent) == mir_surface_focused; handleSurfaceOutputEvent(ubuntuEvent->window, mir_event_get_surface_output_event(nativeEvent));
// Mir may have sent a pair of focus lost/gained events, so we need to "peek" into the queue
// so that we don't deactivate windows prematurely.
if (focused) {
mPendingFocusGainedEvents--;
ubuntuEvent->window->handleSurfaceFocused();
QWindowSystemInterface::handleWindowActivated(ubuntuEvent->window->window(), Qt::ActiveWindowFocusReason);
// NB: Since processing of system events is queued, never check qGuiApp->applicationState()
// as it might be outdated. Always call handleApplicationStateChanged() with the latest
// state regardless.
QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationActive);
} else if (!mPendingFocusGainedEvents) {
DLOG("[ubuntumirclient QPA] No windows have focus");
QWindowSystemInterface::handleWindowActivated(nullptr, Qt::ActiveWindowFocusReason);
QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationInactive);
}
}
break; break;
}
case mir_event_type_orientation: case mir_event_type_orientation:
dispatchOrientationEvent(ubuntuEvent->window->window(), mir_event_get_orientation_event(nativeEvent)); dispatchOrientationEvent(ubuntuEvent->window->window(), mir_event_get_orientation_event(nativeEvent));
break; break;
@ -286,7 +322,7 @@ void QMirClientInput::customEvent(QEvent* event)
QWindowSystemInterface::handleCloseEvent(ubuntuEvent->window->window()); QWindowSystemInterface::handleCloseEvent(ubuntuEvent->window->window());
break; break;
default: default:
DLOG("unhandled event type: %d", static_cast<int>(mir_event_get_type(nativeEvent))); qCDebug(mirclient, "unhandled event type: %d", static_cast<int>(mir_event_get_type(nativeEvent)));
} }
} }
@ -294,22 +330,11 @@ void QMirClientInput::postEvent(QMirClientWindow *platformWindow, const MirEvent
{ {
QWindow *window = platformWindow->window(); QWindow *window = platformWindow->window();
const auto eventType = mir_event_get_type(event); QCoreApplication::postEvent(this, new UbuntuEvent(
if (mir_event_type_surface == eventType) {
auto surfaceEvent = mir_event_get_surface_event(event);
if (mir_surface_attrib_focus == mir_surface_event_get_attribute(surfaceEvent)) {
const bool focused = mir_surface_event_get_attribute_value(surfaceEvent) == mir_surface_focused;
if (focused) {
mPendingFocusGainedEvents++;
}
}
}
QCoreApplication::postEvent(this, new QMirClientEvent(
platformWindow, event, mEventType)); platformWindow, event, mEventType));
if ((window->flags().testFlag(Qt::WindowTransparentForInput)) && window->parent()) { if ((window->flags().testFlag(Qt::WindowTransparentForInput)) && window->parent()) {
QCoreApplication::postEvent(this, new QMirClientEvent( QCoreApplication::postEvent(this, new UbuntuEvent(
static_cast<QMirClientWindow*>(platformWindow->QPlatformWindow::parent()), static_cast<QMirClientWindow*>(platformWindow->QPlatformWindow::parent()),
event, mEventType)); event, mEventType));
} }
@ -365,15 +390,17 @@ void QMirClientInput::dispatchTouchEvent(QMirClientWindow *window, const MirInpu
switch (touch_action) switch (touch_action)
{ {
case mir_touch_action_down: case mir_touch_action_down:
mLastFocusedWindow = window; mLastInputWindow = window;
touchPoint.state = Qt::TouchPointPressed; touchPoint.state = Qt::TouchPointPressed;
break; break;
case mir_touch_action_up: case mir_touch_action_up:
touchPoint.state = Qt::TouchPointReleased; touchPoint.state = Qt::TouchPointReleased;
break; break;
case mir_touch_action_change: case mir_touch_action_change:
default:
touchPoint.state = Qt::TouchPointMoved; touchPoint.state = Qt::TouchPointMoved;
break;
default:
Q_UNREACHABLE();
} }
touchPoints.append(touchPoint); touchPoints.append(touchPoint);
@ -384,22 +411,26 @@ void QMirClientInput::dispatchTouchEvent(QMirClientWindow *window, const MirInpu
mTouchDevice, touchPoints); mTouchDevice, touchPoints);
} }
static uint32_t translateKeysym(uint32_t sym, char *string, size_t size) static uint32_t translateKeysym(uint32_t sym, const QString &text) {
{ int code = 0;
Q_UNUSED(size);
string[0] = '\0';
if (sym >= XKB_KEY_F1 && sym <= XKB_KEY_F35) QTextCodec *systemCodec = QTextCodec::codecForLocale();
if (sym < 128 || (sym < 256 && systemCodec->mibEnum() == 4)) {
// upper-case key, if known
code = isprint((int)sym) ? toupper((int)sym) : 0;
} else if (sym >= XKB_KEY_F1 && sym <= XKB_KEY_F35) {
return Qt::Key_F1 + (int(sym) - XKB_KEY_F1); return Qt::Key_F1 + (int(sym) - XKB_KEY_F1);
} else if (text.length() == 1 && text.unicode()->unicode() > 0x1f
for (int i = 0; KeyTable[i]; i += 2) { && text.unicode()->unicode() != 0x7f
&& !(sym >= XKB_KEY_dead_grave && sym <= XKB_KEY_dead_currency)) {
code = text.unicode()->toUpper().unicode();
} else {
for (int i = 0; KeyTable[i]; i += 2)
if (sym == KeyTable[i]) if (sym == KeyTable[i])
return KeyTable[i + 1]; code = KeyTable[i + 1];
} }
string[0] = sym; return code;
string[1] = '\0';
return toupper(sym);
} }
namespace namespace
@ -413,12 +444,15 @@ Qt::KeyboardModifiers qt_modifiers_from_mir(MirInputEventModifiers modifiers)
if (modifiers & mir_input_event_modifier_ctrl) { if (modifiers & mir_input_event_modifier_ctrl) {
q_modifiers |= Qt::ControlModifier; q_modifiers |= Qt::ControlModifier;
} }
if (modifiers & mir_input_event_modifier_alt) { if (modifiers & mir_input_event_modifier_alt_left) {
q_modifiers |= Qt::AltModifier; q_modifiers |= Qt::AltModifier;
} }
if (modifiers & mir_input_event_modifier_meta) { if (modifiers & mir_input_event_modifier_meta) {
q_modifiers |= Qt::MetaModifier; q_modifiers |= Qt::MetaModifier;
} }
if (modifiers & mir_input_event_modifier_alt_right) {
q_modifiers |= Qt::GroupSwitchModifier;
}
return q_modifiers; return q_modifiers;
} }
} }
@ -429,34 +463,43 @@ void QMirClientInput::dispatchKeyEvent(QMirClientWindow *window, const MirInputE
ulong timestamp = mir_input_event_get_event_time(event) / 1000000; ulong timestamp = mir_input_event_get_event_time(event) / 1000000;
xkb_keysym_t xk_sym = mir_keyboard_event_key_code(key_event); xkb_keysym_t xk_sym = mir_keyboard_event_key_code(key_event);
quint32 scan_code = mir_keyboard_event_scan_code(key_event);
quint32 native_modifiers = mir_keyboard_event_modifiers(key_event);
// Key modifier and unicode index mapping. // Key modifier and unicode index mapping.
auto modifiers = qt_modifiers_from_mir(mir_keyboard_event_modifiers(key_event)); auto modifiers = qt_modifiers_from_mir(native_modifiers);
MirKeyboardAction action = mir_keyboard_event_action(key_event); MirKeyboardAction action = mir_keyboard_event_action(key_event);
QEvent::Type keyType = action == mir_keyboard_action_up QEvent::Type keyType = action == mir_keyboard_action_up
? QEvent::KeyRelease : QEvent::KeyPress; ? QEvent::KeyRelease : QEvent::KeyPress;
if (action == mir_keyboard_action_down) if (action == mir_keyboard_action_down)
mLastFocusedWindow = window; mLastInputWindow = window;
char s[2]; QString text;
int sym = translateKeysym(xk_sym, s, sizeof(s)); QVarLengthArray<char, 32> chars(32);
QString text = QString::fromLatin1(s); {
int result = xkb_keysym_to_utf8(xk_sym, chars.data(), chars.size());
if (result > 0) {
text = QString::fromUtf8(chars.constData());
}
}
int sym = translateKeysym(xk_sym, text);
bool is_auto_rep = action == mir_keyboard_action_repeat; bool is_auto_rep = action == mir_keyboard_action_repeat;
QPlatformInputContext *context = QGuiApplicationPrivate::platformIntegration()->inputContext(); QPlatformInputContext *context = QGuiApplicationPrivate::platformIntegration()->inputContext();
if (context) { if (context) {
QKeyEvent qKeyEvent(keyType, sym, modifiers, text, is_auto_rep); QKeyEvent qKeyEvent(keyType, sym, modifiers, scan_code, xk_sym, native_modifiers, text, is_auto_rep);
qKeyEvent.setTimestamp(timestamp); qKeyEvent.setTimestamp(timestamp);
if (context->filterEvent(&qKeyEvent)) { if (context->filterEvent(&qKeyEvent)) {
DLOG("key event filtered out by input context"); qCDebug(mirclient, "key event filtered out by input context");
return; return;
} }
} }
QWindowSystemInterface::handleKeyEvent(window->window(), timestamp, keyType, sym, modifiers, text, is_auto_rep); QWindowSystemInterface::handleExtendedKeyEvent(window->window(), timestamp, keyType, sym, modifiers, scan_code, xk_sym, native_modifiers, text, is_auto_rep);
} }
namespace namespace
@ -481,14 +524,17 @@ Qt::MouseButtons extract_buttons(const MirPointerEvent *pev)
void QMirClientInput::dispatchPointerEvent(QMirClientWindow *platformWindow, const MirInputEvent *ev) void QMirClientInput::dispatchPointerEvent(QMirClientWindow *platformWindow, const MirInputEvent *ev)
{ {
auto window = platformWindow->window(); const auto window = platformWindow->window();
auto timestamp = mir_input_event_get_event_time(ev) / 1000000; const auto timestamp = mir_input_event_get_event_time(ev) / 1000000;
auto pev = mir_input_event_get_pointer_event(ev); const auto pev = mir_input_event_get_pointer_event(ev);
auto action = mir_pointer_event_action(pev); const auto action = mir_pointer_event_action(pev);
auto localPoint = QPointF(mir_pointer_event_axis_value(pev, mir_pointer_axis_x),
const auto modifiers = qt_modifiers_from_mir(mir_pointer_event_modifiers(pev));
const auto localPoint = QPointF(mir_pointer_event_axis_value(pev, mir_pointer_axis_x),
mir_pointer_event_axis_value(pev, mir_pointer_axis_y)); mir_pointer_event_axis_value(pev, mir_pointer_axis_y));
auto modifiers = qt_modifiers_from_mir(mir_pointer_event_modifiers(pev));
mLastInputWindow = platformWindow;
switch (action) { switch (action) {
case mir_pointer_action_button_up: case mir_pointer_action_button_up:
@ -499,7 +545,8 @@ void QMirClientInput::dispatchPointerEvent(QMirClientWindow *platformWindow, con
const float vDelta = mir_pointer_event_axis_value(pev, mir_pointer_axis_vscroll); const float vDelta = mir_pointer_event_axis_value(pev, mir_pointer_axis_vscroll);
if (hDelta != 0 || vDelta != 0) { if (hDelta != 0 || vDelta != 0) {
const QPoint angleDelta = QPoint(hDelta * 15, vDelta * 15); // QWheelEvent::DefaultDeltasPerStep = 120 but doesn't exist on vivid
const QPoint angleDelta(120 * hDelta, 120 * vDelta);
QWindowSystemInterface::handleWheelEvent(window, timestamp, localPoint, window->position() + localPoint, QWindowSystemInterface::handleWheelEvent(window, timestamp, localPoint, window->position() + localPoint,
QPoint(), angleDelta, modifiers, Qt::ScrollUpdate); QPoint(), angleDelta, modifiers, Qt::ScrollUpdate);
} }
@ -515,42 +562,32 @@ void QMirClientInput::dispatchPointerEvent(QMirClientWindow *platformWindow, con
QWindowSystemInterface::handleLeaveEvent(window); QWindowSystemInterface::handleLeaveEvent(window);
break; break;
default: default:
DLOG("Unrecognized pointer event"); Q_UNREACHABLE();
} }
} }
#if (LOG_EVENTS != 0)
static const char* nativeOrientationDirectionToStr(MirOrientation orientation) static const char* nativeOrientationDirectionToStr(MirOrientation orientation)
{ {
switch (orientation) { switch (orientation) {
case mir_orientation_normal: case mir_orientation_normal:
return "Normal"; return "Normal";
break;
case mir_orientation_left: case mir_orientation_left:
return "Left"; return "Left";
break;
case mir_orientation_inverted: case mir_orientation_inverted:
return "Inverted"; return "Inverted";
break;
case mir_orientation_right: case mir_orientation_right:
return "Right"; return "Right";
break;
default:
return "INVALID!";
} }
Q_UNREACHABLE();
} }
#endif
void QMirClientInput::dispatchOrientationEvent(QWindow *window, const MirOrientationEvent *event) void QMirClientInput::dispatchOrientationEvent(QWindow *window, const MirOrientationEvent *event)
{ {
MirOrientation mir_orientation = mir_orientation_event_get_direction(event); MirOrientation mir_orientation = mir_orientation_event_get_direction(event);
#if (LOG_EVENTS != 0) qCDebug(mirclientInput, "orientation direction: %s", nativeOrientationDirectionToStr(mir_orientation));
// Orientation event logging.
LOG("ORIENTATION direction: %s", nativeOrientationDirectionToStr(mir_orientation));
#endif
if (!window->screen()) { if (!window->screen()) {
DLOG("Window has no associated screen, dropping orientation event"); qCDebug(mirclient, "Window has no associated screen, dropping orientation event");
return; return;
} }
@ -569,7 +606,7 @@ void QMirClientInput::dispatchOrientationEvent(QWindow *window, const MirOrienta
orientation = OrientationChangeEvent::RightUp; orientation = OrientationChangeEvent::RightUp;
break; break;
default: default:
DLOG("No such orientation %d", mir_orientation); qCDebug(mirclient, "No such orientation %d", mir_orientation);
return; return;
} }
@ -581,3 +618,61 @@ void QMirClientInput::dispatchOrientationEvent(QWindow *window, const MirOrienta
new OrientationChangeEvent(OrientationChangeEvent::mType, orientation)); new OrientationChangeEvent(OrientationChangeEvent::mType, orientation));
} }
void QMirClientInput::handleSurfaceEvent(const QPointer<QMirClientWindow> &window, const MirSurfaceEvent *event)
{
auto surfaceEventAttribute = mir_surface_event_get_attribute(event);
switch (surfaceEventAttribute) {
case mir_surface_attrib_focus: {
window->handleSurfaceFocusChanged(
mir_surface_event_get_attribute_value(event) == mir_surface_focused);
break;
}
case mir_surface_attrib_visibility: {
window->handleSurfaceExposeChange(
mir_surface_event_get_attribute_value(event) == mir_surface_visibility_exposed);
break;
}
// Remaining attributes are ones client sets for server, and server should not override them
case mir_surface_attrib_state: {
MirSurfaceState state = static_cast<MirSurfaceState>(mir_surface_event_get_attribute_value(event));
if (state == mir_surface_state_hidden) {
window->handleSurfaceVisibilityChanged(false);
} else {
// it's visible!
window->handleSurfaceVisibilityChanged(true);
window->handleSurfaceStateChanged(mirSurfaceStateToWindowState(state));
}
break;
}
case mir_surface_attrib_type:
case mir_surface_attrib_swapinterval:
case mir_surface_attrib_dpi:
case mir_surface_attrib_preferred_orientation:
case mir_surface_attribs:
break;
}
}
void QMirClientInput::handleSurfaceOutputEvent(const QPointer<QMirClientWindow> &window, const MirSurfaceOutputEvent *event)
{
const uint32_t outputId = mir_surface_output_event_get_output_id(event);
const int dpi = mir_surface_output_event_get_dpi(event);
const MirFormFactor formFactor = mir_surface_output_event_get_form_factor(event);
const float scale = mir_surface_output_event_get_scale(event);
const auto screenObserver = mIntegration->screenObserver();
QMirClientScreen *screen = screenObserver->findScreenWithId(outputId);
if (!screen) {
qCWarning(mirclient) << "Mir notified window" << window->window() << "on an unknown screen with id" << outputId;
return;
}
screenObserver->handleScreenPropertiesChange(screen, dpi, formFactor, scale);
window->handleScreenPropertiesChange(formFactor, scale);
if (window->screen() != screen) {
QWindowSystemInterface::handleWindowScreenChanged(window->window(), screen->screen());
}
}

View File

@ -1,6 +1,6 @@
/**************************************************************************** /****************************************************************************
** **
** Copyright (C) 2014-2015 Canonical, Ltd. ** Copyright (C) 2014-2016 Canonical, Ltd.
** Contact: https://www.qt.io/licensing/ ** Contact: https://www.qt.io/licensing/
** **
** This file is part of the plugins of the Qt Toolkit. ** This file is part of the plugins of the Qt Toolkit.
@ -43,7 +43,6 @@
// Qt // Qt
#include <qpa/qwindowsysteminterface.h> #include <qpa/qwindowsysteminterface.h>
#include <QAtomicInt>
#include <mir_toolkit/mir_client_library.h> #include <mir_toolkit/mir_client_library.h>
@ -63,7 +62,7 @@ public:
void postEvent(QMirClientWindow* window, const MirEvent *event); void postEvent(QMirClientWindow* window, const MirEvent *event);
QMirClientClientIntegration* integration() const { return mIntegration; } QMirClientClientIntegration* integration() const { return mIntegration; }
QMirClientWindow *lastFocusedWindow() const {return mLastFocusedWindow; } QMirClientWindow *lastInputWindow() const {return mLastInputWindow; }
protected: protected:
void dispatchKeyEvent(QMirClientWindow *window, const MirInputEvent *event); void dispatchKeyEvent(QMirClientWindow *window, const MirInputEvent *event);
@ -72,6 +71,8 @@ protected:
void dispatchInputEvent(QMirClientWindow *window, const MirInputEvent *event); void dispatchInputEvent(QMirClientWindow *window, const MirInputEvent *event);
void dispatchOrientationEvent(QWindow* window, const MirOrientationEvent *event); void dispatchOrientationEvent(QWindow* window, const MirOrientationEvent *event);
void handleSurfaceEvent(const QPointer<QMirClientWindow> &window, const MirSurfaceEvent *event);
void handleSurfaceOutputEvent(const QPointer<QMirClientWindow> &window, const MirSurfaceOutputEvent *event);
private: private:
QMirClientClientIntegration* mIntegration; QMirClientClientIntegration* mIntegration;
@ -79,8 +80,7 @@ private:
const QByteArray mEventFilterType; const QByteArray mEventFilterType;
const QEvent::Type mEventType; const QEvent::Type mEventType;
QMirClientWindow *mLastFocusedWindow; QMirClientWindow *mLastInputWindow;
QAtomicInt mPendingFocusGainedEvents;
}; };
#endif // QMIRCLIENTINPUT_H #endif // QMIRCLIENTINPUT_H

View File

@ -1,6 +1,6 @@
/**************************************************************************** /****************************************************************************
** **
** Copyright (C) 2014-2015 Canonical, Ltd. ** Copyright (C) 2014-2016 Canonical, Ltd.
** Contact: https://www.qt.io/licensing/ ** Contact: https://www.qt.io/licensing/
** **
** This file is part of the plugins of the Qt Toolkit. ** This file is part of the plugins of the Qt Toolkit.
@ -42,6 +42,8 @@
#include "qmirclientintegration.h" #include "qmirclientintegration.h"
#include "qmirclientbackingstore.h" #include "qmirclientbackingstore.h"
#include "qmirclientclipboard.h" #include "qmirclientclipboard.h"
#include "qmirclientdebugextension.h"
#include "qmirclientdesktopwindow.h"
#include "qmirclientglcontext.h" #include "qmirclientglcontext.h"
#include "qmirclientinput.h" #include "qmirclientinput.h"
#include "qmirclientlogging.h" #include "qmirclientlogging.h"
@ -51,56 +53,62 @@
#include "qmirclientwindow.h" #include "qmirclientwindow.h"
// Qt // Qt
#include <QFileInfo>
#include <QGuiApplication> #include <QGuiApplication>
#include <private/qguiapplication_p.h>
#include <qpa/qplatformnativeinterface.h> #include <qpa/qplatformnativeinterface.h>
#include <qpa/qplatforminputcontextfactory_p.h> #include <qpa/qplatforminputcontextfactory_p.h>
#include <qpa/qplatforminputcontext.h> #include <qpa/qplatforminputcontext.h>
#include <QtEglSupport/private/qeglconvenience_p.h>
#include <QtEglSupport/private/qeglpbuffer_p.h>
#include <QtFontDatabaseSupport/private/qgenericunixfontdatabase_p.h> #include <QtFontDatabaseSupport/private/qgenericunixfontdatabase_p.h>
#include <QtEventDispatcherSupport/private/qgenericunixeventdispatcher_p.h> #include <QtEventDispatcherSupport/private/qgenericunixeventdispatcher_p.h>
#ifndef QT_NO_ACCESSIBILITY
#include <qpa/qplatformaccessibility.h>
#ifndef QT_NO_ACCESSIBILITY_ATSPI_BRIDGE
#include <QtLinuxAccessibilitySupport/private/bridge_p.h>
#endif
#endif
#include <QOpenGLContext> #include <QOpenGLContext>
#include <QOffscreenSurface>
// platform-api // platform-api
#include <ubuntu/application/lifecycle_delegate.h> #include <ubuntu/application/lifecycle_delegate.h>
#include <ubuntu/application/id.h> #include <ubuntu/application/id.h>
#include <ubuntu/application/options.h> #include <ubuntu/application/options.h>
static void resumedCallback(const UApplicationOptions *options, void* context) static void resumedCallback(const UApplicationOptions */*options*/, void* context)
{ {
Q_UNUSED(options) auto integration = static_cast<QMirClientClientIntegration*>(context);
Q_UNUSED(context) integration->appStateController()->setResumed();
DASSERT(context != NULL);
if (qGuiApp->focusWindow()) {
QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationActive);
} else {
QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationInactive);
}
} }
static void aboutToStopCallback(UApplicationArchive *archive, void* context) static void aboutToStopCallback(UApplicationArchive */*archive*/, void* context)
{ {
Q_UNUSED(archive) auto integration = static_cast<QMirClientClientIntegration*>(context);
DASSERT(context != NULL); auto inputContext = integration->inputContext();
QMirClientClientIntegration* integration = static_cast<QMirClientClientIntegration*>(context);
QPlatformInputContext *inputContext = integration->inputContext();
if (inputContext) { if (inputContext) {
inputContext->hideInputPanel(); inputContext->hideInputPanel();
} else { } else {
qWarning("QMirClientClientIntegration aboutToStopCallback(): no input context"); qCWarning(mirclient) << "aboutToStopCallback(): no input context";
} }
QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationSuspended); integration->appStateController()->setSuspended();
} }
QMirClientClientIntegration::QMirClientClientIntegration() QMirClientClientIntegration::QMirClientClientIntegration(int argc, char **argv)
: QPlatformIntegration() : QPlatformIntegration()
, mNativeInterface(new QMirClientNativeInterface) , mNativeInterface(new QMirClientNativeInterface(this))
, mFontDb(new QGenericUnixFontDatabase) , mFontDb(new QGenericUnixFontDatabase)
, mServices(new QMirClientPlatformServices) , mServices(new QMirClientPlatformServices)
, mClipboard(new QMirClientClipboard) , mAppStateController(new QMirClientAppStateController)
, mScaleFactor(1.0) , mScaleFactor(1.0)
{ {
setupOptions(); {
setupDescription(); QStringList args = QCoreApplication::arguments();
setupOptions(args);
QByteArray sessionName = generateSessionName(args);
setupDescription(sessionName);
}
// Create new application instance // Create new application instance
mInstance = u_application_instance_new_from_description_with_options(mDesc, mOptions); mInstance = u_application_instance_new_from_description_with_options(mDesc, mOptions);
@ -110,19 +118,48 @@ QMirClientClientIntegration::QMirClientClientIntegration()
"running, and the correct socket is being used and is accessible. The shell may have\n" "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"); "rejected the incoming connection, so check its log file");
mNativeInterface->setMirConnection(u_application_instance_get_mir_connection(mInstance)); mMirConnection = u_application_instance_get_mir_connection(mInstance);
// Create default screen. // Choose the default surface format suited to the Mir platform
screenAdded(new QMirClientScreen(u_application_instance_get_mir_connection(mInstance))); QSurfaceFormat defaultFormat;
defaultFormat.setRedBufferSize(8);
defaultFormat.setGreenBufferSize(8);
defaultFormat.setBlueBufferSize(8);
QSurfaceFormat::setDefaultFormat(defaultFormat);
// Initialize EGL.
mEglNativeDisplay = mir_connection_get_egl_native_display(mMirConnection);
ASSERT((mEglDisplay = eglGetDisplay(mEglNativeDisplay)) != EGL_NO_DISPLAY);
ASSERT(eglInitialize(mEglDisplay, nullptr, nullptr) == EGL_TRUE);
// Has debug mode been requsted, either with "-testability" switch or QT_LOAD_TESTABILITY env var
bool testability = qEnvironmentVariableIsSet("QT_LOAD_TESTABILITY");
for (int i=1; !testability && i<argc; i++) {
if (strcmp(argv[i], "-testability") == 0) {
testability = true;
}
}
if (testability) {
mDebugExtension.reset(new QMirClientDebugExtension);
}
}
void QMirClientClientIntegration::initialize()
{
// Init the ScreenObserver
mScreenObserver.reset(new QMirClientScreenObserver(mMirConnection));
connect(mScreenObserver.data(), &QMirClientScreenObserver::screenAdded,
[this](QMirClientScreen *screen) { this->screenAdded(screen); });
connect(mScreenObserver.data(), &QMirClientScreenObserver::screenRemoved,
this, &QMirClientClientIntegration::destroyScreen);
Q_FOREACH (auto screen, mScreenObserver->screens()) {
screenAdded(screen);
}
// Initialize input. // Initialize input.
if (qEnvironmentVariableIsEmpty("QTUBUNTU_NO_INPUT")) {
mInput = new QMirClientInput(this); mInput = new QMirClientInput(this);
mInputContext = QPlatformInputContextFactory::create(); mInputContext = QPlatformInputContextFactory::create();
} else {
mInput = nullptr;
mInputContext = nullptr;
}
// compute the scale factor // compute the scale factor
const int defaultGridUnit = 8; const int defaultGridUnit = 8;
@ -140,10 +177,9 @@ QMirClientClientIntegration::QMirClientClientIntegration()
QMirClientClientIntegration::~QMirClientClientIntegration() QMirClientClientIntegration::~QMirClientClientIntegration()
{ {
eglTerminate(mEglDisplay);
delete mInput; delete mInput;
delete mInputContext; delete mInputContext;
for (QScreen *screen : QGuiApplication::screens())
QPlatformIntegration::destroyScreen(screen->handle());
delete mServices; delete mServices;
} }
@ -152,9 +188,8 @@ QPlatformServices *QMirClientClientIntegration::services() const
return mServices; return mServices;
} }
void QMirClientClientIntegration::setupOptions() void QMirClientClientIntegration::setupOptions(QStringList &args)
{ {
QStringList args = QCoreApplication::arguments();
int argc = args.size() + 1; int argc = args.size() + 1;
char **argv = new char*[argc]; char **argv = new char*[argc];
for (int i = 0; i < argc - 1; i++) for (int i = 0; i < argc - 1; i++)
@ -168,10 +203,11 @@ void QMirClientClientIntegration::setupOptions()
delete [] argv; delete [] argv;
} }
void QMirClientClientIntegration::setupDescription() void QMirClientClientIntegration::setupDescription(QByteArray &sessionName)
{ {
mDesc = u_application_description_new(); mDesc = u_application_description_new();
UApplicationId* id = u_application_id_new_from_stringn("QtUbuntu", 8);
UApplicationId* id = u_application_id_new_from_stringn(sessionName.data(), sessionName.count());
u_application_description_set_application_id(mDesc, id); u_application_description_set_application_id(mDesc, id);
UApplicationLifecycleDelegate* delegate = u_application_lifecycle_delegate_new(); UApplicationLifecycleDelegate* delegate = u_application_lifecycle_delegate_new();
@ -181,43 +217,66 @@ void QMirClientClientIntegration::setupDescription()
u_application_description_set_application_lifecycle_delegate(mDesc, delegate); u_application_description_set_application_lifecycle_delegate(mDesc, delegate);
} }
QByteArray QMirClientClientIntegration::generateSessionName(QStringList &args)
{
// Try to come up with some meaningful session name to uniquely identify this session,
// helping with shell debugging
if (args.count() == 0) {
return QByteArray("QtUbuntu");
} if (args[0].contains("qmlscene")) {
return generateSessionNameFromQmlFile(args);
} else {
// use the executable name
QFileInfo fileInfo(args[0]);
return fileInfo.fileName().toLocal8Bit();
}
}
QByteArray QMirClientClientIntegration::generateSessionNameFromQmlFile(QStringList &args)
{
Q_FOREACH (QString arg, args) {
if (arg.endsWith(".qml")) {
QFileInfo fileInfo(arg);
return fileInfo.fileName().toLocal8Bit();
}
}
// give up
return "qmlscene";
}
QPlatformWindow* QMirClientClientIntegration::createPlatformWindow(QWindow* window) const QPlatformWindow* QMirClientClientIntegration::createPlatformWindow(QWindow* window) const
{ {
return const_cast<QMirClientClientIntegration*>(this)->createPlatformWindow(window); if (window->type() == Qt::Desktop) {
} // Desktop windows should not be backed up by a mir surface as they don't draw anything (nor should).
return new QMirClientDesktopWindow(window);
QPlatformWindow* QMirClientClientIntegration::createPlatformWindow(QWindow* window) } else {
{ return new QMirClientWindow(window, mInput, mNativeInterface, mAppStateController.data(),
return new QMirClientWindow(window, mClipboard, screen(), mEglDisplay, mMirConnection, mDebugExtension.data());
mInput, u_application_instance_get_mir_connection(mInstance)); }
}
QMirClientScreen *QMirClientClientIntegration::screen() const
{
return static_cast<QMirClientScreen *>(QGuiApplication::primaryScreen()->handle());
} }
bool QMirClientClientIntegration::hasCapability(QPlatformIntegration::Capability cap) const bool QMirClientClientIntegration::hasCapability(QPlatformIntegration::Capability cap) const
{ {
switch (cap) { switch (cap) {
case ThreadedPixmaps:
return true;
case OpenGL:
return true;
case ApplicationState:
return true;
case ThreadedOpenGL: case ThreadedOpenGL:
if (qEnvironmentVariableIsEmpty("QTUBUNTU_NO_THREADED_OPENGL")) { if (qEnvironmentVariableIsEmpty("QTUBUNTU_NO_THREADED_OPENGL")) {
return true; return true;
} else { } else {
DLOG("ubuntumirclient: disabled threaded OpenGL"); qCDebug(mirclient, "disabled threaded OpenGL");
return false; return false;
} }
case ThreadedPixmaps:
case OpenGL:
case ApplicationState:
case MultipleWindows: case MultipleWindows:
case NonFullScreenWindows: case NonFullScreenWindows:
#if QT_VERSION > QT_VERSION_CHECK(5, 5, 0)
case SwitchableWidgetComposition:
#endif
case RasterGLSurface: // needed for QQuickWidget
return true; return true;
default: default:
return QPlatformIntegration::hasCapability(cap); return QPlatformIntegration::hasCapability(cap);
@ -237,14 +296,25 @@ QPlatformBackingStore* QMirClientClientIntegration::createPlatformBackingStore(Q
QPlatformOpenGLContext* QMirClientClientIntegration::createPlatformOpenGLContext( QPlatformOpenGLContext* QMirClientClientIntegration::createPlatformOpenGLContext(
QOpenGLContext* context) const QOpenGLContext* context) const
{ {
return const_cast<QMirClientClientIntegration*>(this)->createPlatformOpenGLContext(context); QSurfaceFormat format(context->format());
}
QPlatformOpenGLContext* QMirClientClientIntegration::createPlatformOpenGLContext( auto platformContext = new QMirClientOpenGLContext(format, context->shareHandle(), mEglDisplay);
QOpenGLContext* context) if (!platformContext->isValid()) {
{ // Older Intel Atom-based devices only support OpenGL 1.4 compatibility profile but by default
return new QMirClientOpenGLContext(static_cast<QMirClientScreen*>(context->screen()->handle()), // QML asks for at least OpenGL 2.0. The XCB GLX backend ignores this request and returns a
static_cast<QMirClientOpenGLContext*>(context->shareHandle())); // 1.4 context, but the XCB EGL backend tries to honor it, and fails. The 1.4 context appears to
// have sufficient capabilities on MESA (i915) to render correctly however. So reduce the default
// requested OpenGL version to 1.0 to ensure EGL will give us a working context (lp:1549455).
static const bool isMesa = QString(eglQueryString(mEglDisplay, EGL_VENDOR)).contains(QStringLiteral("Mesa"));
if (isMesa) {
qCDebug(mirclientGraphics, "Attempting to choose OpenGL 1.4 context which may suit Mesa");
format.setMajorVersion(1);
format.setMinorVersion(4);
delete platformContext;
platformContext = new QMirClientOpenGLContext(format, context->shareHandle(), mEglDisplay);
}
}
return platformContext;
} }
QStringList QMirClientClientIntegration::themeNames() const QStringList QMirClientClientIntegration::themeNames() const
@ -277,10 +347,65 @@ QVariant QMirClientClientIntegration::styleHint(StyleHint hint) const
QPlatformClipboard* QMirClientClientIntegration::clipboard() const QPlatformClipboard* QMirClientClientIntegration::clipboard() const
{ {
return mClipboard.data(); static QPlatformClipboard *clipboard = nullptr;
if (!clipboard) {
clipboard = new QMirClientClipboard;
}
return clipboard;
} }
QPlatformNativeInterface* QMirClientClientIntegration::nativeInterface() const QPlatformNativeInterface* QMirClientClientIntegration::nativeInterface() const
{ {
return mNativeInterface; return mNativeInterface;
} }
QPlatformOffscreenSurface *QMirClientClientIntegration::createPlatformOffscreenSurface(
QOffscreenSurface *surface) const
{
return new QEGLPbuffer(mEglDisplay, surface->requestedFormat(), surface);
}
void QMirClientClientIntegration::destroyScreen(QMirClientScreen *screen)
{
// FIXME: on deleting a screen while a Window is on it, Qt will automatically
// move the window to the primaryScreen(). This will trigger a screenChanged
// signal, causing things like QQuickScreenAttached to re-fetch screen properties
// like DPI and physical size. However this is crashing, as Qt is calling virtual
// functions on QPlatformScreen, for reasons unclear. As workaround, move window
// to primaryScreen() before deleting the screen. Might be QTBUG-38650
QScreen *primaryScreen = QGuiApplication::primaryScreen();
if (screen != primaryScreen->handle()) {
uint32_t movedWindowCount = 0;
Q_FOREACH (QWindow *w, QGuiApplication::topLevelWindows()) {
if (w->screen()->handle() == screen) {
QWindowSystemInterface::handleWindowScreenChanged(w, primaryScreen);
++movedWindowCount;
}
}
if (movedWindowCount > 0) {
QWindowSystemInterface::flushWindowSystemEvents();
}
}
qCDebug(mirclient) << "Removing Screen with id" << screen->mirOutputId() << "and geometry" << screen->geometry();
#if QT_VERSION < QT_VERSION_CHECK(5, 5, 0)
delete screen;
#else
QPlatformIntegration::destroyScreen(screen);
#endif
}
#ifndef QT_NO_ACCESSIBILITY
QPlatformAccessibility *QMirClientClientIntegration::accessibility() const
{
#if !defined(QT_NO_ACCESSIBILITY_ATSPI_BRIDGE)
if (!mAccessibility) {
Q_ASSERT_X(QCoreApplication::eventDispatcher(), "QMirClientIntegration",
"Initializing accessibility without event-dispatcher!");
mAccessibility.reset(new QSpiAccessibleBridge());
}
#endif
return mAccessibility.data();
}
#endif

View File

@ -44,20 +44,28 @@
#include <qpa/qplatformintegration.h> #include <qpa/qplatformintegration.h>
#include <QSharedPointer> #include <QSharedPointer>
#include "qmirclientappstatecontroller.h"
#include "qmirclientplatformservices.h" #include "qmirclientplatformservices.h"
#include "qmirclientscreenobserver.h"
// platform-api // platform-api
#include <ubuntu/application/description.h> #include <ubuntu/application/description.h>
#include <ubuntu/application/instance.h> #include <ubuntu/application/instance.h>
class QMirClientClipboard; #include <EGL/egl.h>
class QMirClientDebugExtension;
class QMirClientInput; class QMirClientInput;
class QMirClientNativeInterface; class QMirClientNativeInterface;
class QMirClientScreen; class QMirClientScreen;
class MirConnection;
class QMirClientClientIntegration : public QObject, public QPlatformIntegration
{
Q_OBJECT
class QMirClientClientIntegration : public QPlatformIntegration {
public: public:
QMirClientClientIntegration(); QMirClientClientIntegration(int argc, char **argv);
virtual ~QMirClientClientIntegration(); virtual ~QMirClientClientIntegration();
// QPlatformIntegration methods. // QPlatformIntegration methods.
@ -74,14 +82,26 @@ public:
QPlatformWindow* createPlatformWindow(QWindow* window) const override; QPlatformWindow* createPlatformWindow(QWindow* window) const override;
QPlatformInputContext* inputContext() const override { return mInputContext; } QPlatformInputContext* inputContext() const override { return mInputContext; }
QPlatformClipboard* clipboard() const override; QPlatformClipboard* clipboard() const override;
void initialize() override;
QPlatformOffscreenSurface *createPlatformOffscreenSurface(QOffscreenSurface *surface) const override;
QPlatformAccessibility *accessibility() const override;
QPlatformOpenGLContext* createPlatformOpenGLContext(QOpenGLContext* context); // New methods.
QPlatformWindow* createPlatformWindow(QWindow* window); MirConnection *mirConnection() const { return mMirConnection; }
QMirClientScreen* screen() const; EGLDisplay eglDisplay() const { return mEglDisplay; }
EGLNativeDisplayType eglNativeDisplay() const { return mEglNativeDisplay; }
QMirClientAppStateController *appStateController() const { return mAppStateController.data(); }
QMirClientScreenObserver *screenObserver() const { return mScreenObserver.data(); }
QMirClientDebugExtension *debugExtension() const { return mDebugExtension.data(); }
private Q_SLOTS:
void destroyScreen(QMirClientScreen *screen);
private: private:
void setupOptions(); void setupOptions(QStringList &args);
void setupDescription(); void setupDescription(QByteArray &sessionName);
static QByteArray generateSessionName(QStringList &args);
static QByteArray generateSessionNameFromQmlFile(QStringList &args);
QMirClientNativeInterface* mNativeInterface; QMirClientNativeInterface* mNativeInterface;
QPlatformFontDatabase* mFontDb; QPlatformFontDatabase* mFontDb;
@ -90,13 +110,22 @@ private:
QMirClientInput* mInput; QMirClientInput* mInput;
QPlatformInputContext* mInputContext; QPlatformInputContext* mInputContext;
QSharedPointer<QMirClientClipboard> mClipboard; mutable QScopedPointer<QPlatformAccessibility> mAccessibility;
QScopedPointer<QMirClientDebugExtension> mDebugExtension;
QScopedPointer<QMirClientScreenObserver> mScreenObserver;
QScopedPointer<QMirClientAppStateController> mAppStateController;
qreal mScaleFactor; qreal mScaleFactor;
MirConnection *mMirConnection;
// Platform API stuff // Platform API stuff
UApplicationOptions* mOptions; UApplicationOptions* mOptions;
UApplicationDescription* mDesc; UApplicationDescription* mDesc;
UApplicationInstance* mInstance; UApplicationInstance* mInstance;
// EGL related
EGLDisplay mEglDisplay{EGL_NO_DISPLAY};
EGLNativeDisplayType mEglNativeDisplay;
}; };
#endif // QMIRCLIENTINTEGRATION_H #endif // QMIRCLIENTINTEGRATION_H

View File

@ -41,23 +41,15 @@
#ifndef QMIRCLIENTLOGGING_H #ifndef QMIRCLIENTLOGGING_H
#define QMIRCLIENTLOGGING_H #define QMIRCLIENTLOGGING_H
// Logging and assertion macros. #include <QLoggingCategory>
#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. #define ASSERT(cond) ((!(cond)) ? qt_assert(#cond,__FILE__,__LINE__) : qt_noop())
#if !defined(QT_NO_DEBUG)
#define DLOG(...) LOG(__VA_ARGS__) Q_DECLARE_LOGGING_CATEGORY(mirclient)
#define DLOG_IF(cond,...) LOG_IF((cond), __VA_ARGS__) Q_DECLARE_LOGGING_CATEGORY(mirclientBufferSwap)
#define DASSERT(cond) ASSERT((cond)) Q_DECLARE_LOGGING_CATEGORY(mirclientInput)
#define DNOT_REACHED() NOT_REACHED() Q_DECLARE_LOGGING_CATEGORY(mirclientGraphics)
#else Q_DECLARE_LOGGING_CATEGORY(mirclientCursor)
#define DLOG(...) qt_noop() Q_DECLARE_LOGGING_CATEGORY(mirclientDebug)
#define DLOG_IF(cond,...) qt_noop()
#define DASSERT(cond) qt_noop()
#define DNOT_REACHED() qt_noop()
#endif
#endif // QMIRCLIENTLOGGING_H #endif // QMIRCLIENTLOGGING_H

View File

@ -42,32 +42,36 @@
#include "qmirclientnativeinterface.h" #include "qmirclientnativeinterface.h"
#include "qmirclientscreen.h" #include "qmirclientscreen.h"
#include "qmirclientglcontext.h" #include "qmirclientglcontext.h"
#include "qmirclientwindow.h"
// Qt // Qt
#include <private/qguiapplication_p.h> #include <QtGui/private/qguiapplication_p.h>
#include <QtGui/qopenglcontext.h> #include <QtGui/qopenglcontext.h>
#include <QtGui/qscreen.h> #include <QtGui/qscreen.h>
#include <QtCore/QMap> #include <QtCore/QMap>
class QMirClientResourceMap : public QMap<QByteArray, QMirClientNativeInterface::ResourceType> class UbuntuResourceMap : public QMap<QByteArray, QMirClientNativeInterface::ResourceType>
{ {
public: public:
QMirClientResourceMap() UbuntuResourceMap()
: QMap<QByteArray, QMirClientNativeInterface::ResourceType>() { : QMap<QByteArray, QMirClientNativeInterface::ResourceType>() {
insert("egldisplay", QMirClientNativeInterface::EglDisplay); insert("egldisplay", QMirClientNativeInterface::EglDisplay);
insert("eglcontext", QMirClientNativeInterface::EglContext); insert("eglcontext", QMirClientNativeInterface::EglContext);
insert("nativeorientation", QMirClientNativeInterface::NativeOrientation); insert("nativeorientation", QMirClientNativeInterface::NativeOrientation);
insert("display", QMirClientNativeInterface::Display); insert("display", QMirClientNativeInterface::Display);
insert("mirconnection", QMirClientNativeInterface::MirConnection); insert("mirconnection", QMirClientNativeInterface::MirConnection);
insert("mirsurface", QMirClientNativeInterface::MirSurface);
insert("scale", QMirClientNativeInterface::Scale);
insert("formfactor", QMirClientNativeInterface::FormFactor);
} }
}; };
Q_GLOBAL_STATIC(QMirClientResourceMap, ubuntuResourceMap) Q_GLOBAL_STATIC(UbuntuResourceMap, ubuntuResourceMap)
QMirClientNativeInterface::QMirClientNativeInterface() QMirClientNativeInterface::QMirClientNativeInterface(const QMirClientClientIntegration *integration)
: mGenericEventFilterType(QByteArrayLiteral("Event")) : mIntegration(integration)
, mGenericEventFilterType(QByteArrayLiteral("Event"))
, mNativeOrientation(nullptr) , mNativeOrientation(nullptr)
, mMirConnection(nullptr)
{ {
} }
@ -88,7 +92,7 @@ void* QMirClientNativeInterface::nativeResourceForIntegration(const QByteArray &
const ResourceType resourceType = ubuntuResourceMap()->value(lowerCaseResource); const ResourceType resourceType = ubuntuResourceMap()->value(lowerCaseResource);
if (resourceType == QMirClientNativeInterface::MirConnection) { if (resourceType == QMirClientNativeInterface::MirConnection) {
return mMirConnection; return mIntegration->mirConnection();
} else { } else {
return nullptr; return nullptr;
} }
@ -119,14 +123,11 @@ void* QMirClientNativeInterface::nativeResourceForWindow(const QByteArray& resou
if (!ubuntuResourceMap()->contains(kLowerCaseResource)) if (!ubuntuResourceMap()->contains(kLowerCaseResource))
return NULL; return NULL;
const ResourceType kResourceType = ubuntuResourceMap()->value(kLowerCaseResource); const ResourceType kResourceType = ubuntuResourceMap()->value(kLowerCaseResource);
if (kResourceType == QMirClientNativeInterface::EglDisplay) {
if (window) { switch (kResourceType) {
return static_cast<QMirClientScreen*>(window->screen()->handle())->eglDisplay(); case EglDisplay:
} else { return mIntegration->eglDisplay();
return static_cast<QMirClientScreen*>( case NativeOrientation:
QGuiApplication::primaryScreen()->handle())->eglDisplay();
}
} else if (kResourceType == QMirClientNativeInterface::NativeOrientation) {
// Return the device's native screen orientation. // Return the device's native screen orientation.
if (window) { if (window) {
QMirClientScreen *ubuntuScreen = static_cast<QMirClientScreen*>(window->screen()->handle()); QMirClientScreen *ubuntuScreen = static_cast<QMirClientScreen*>(window->screen()->handle());
@ -136,8 +137,19 @@ void* QMirClientNativeInterface::nativeResourceForWindow(const QByteArray& resou
mNativeOrientation = new Qt::ScreenOrientation(platformScreen->nativeOrientation()); mNativeOrientation = new Qt::ScreenOrientation(platformScreen->nativeOrientation());
} }
return mNativeOrientation; return mNativeOrientation;
case MirSurface:
if (window) {
auto ubuntuWindow = static_cast<QMirClientWindow*>(window->handle());
if (ubuntuWindow) {
return ubuntuWindow->mirSurface();
} else { } else {
return NULL; return nullptr;
}
} else {
return nullptr;
}
default:
return nullptr;
} }
} }
@ -147,10 +159,59 @@ void* QMirClientNativeInterface::nativeResourceForScreen(const QByteArray& resou
if (!ubuntuResourceMap()->contains(kLowerCaseResource)) if (!ubuntuResourceMap()->contains(kLowerCaseResource))
return NULL; return NULL;
const ResourceType kResourceType = ubuntuResourceMap()->value(kLowerCaseResource); const ResourceType kResourceType = ubuntuResourceMap()->value(kLowerCaseResource);
if (kResourceType == QMirClientNativeInterface::Display) {
if (!screen) if (!screen)
screen = QGuiApplication::primaryScreen(); screen = QGuiApplication::primaryScreen();
return static_cast<QMirClientScreen*>(screen->handle())->eglNativeDisplay(); auto ubuntuScreen = static_cast<QMirClientScreen*>(screen->handle());
if (kResourceType == QMirClientNativeInterface::Display) {
return mIntegration->eglNativeDisplay();
// Changes to the following properties are emitted via the QMirClientNativeInterface::screenPropertyChanged
// signal fired by QMirClientScreen. Connect to this signal for these properties updates.
// WARNING: code highly thread unsafe!
} else if (kResourceType == QMirClientNativeInterface::Scale) {
// In application code, read with:
// float scale = *reinterpret_cast<float*>(nativeResourceForScreen("scale", screen()));
return &ubuntuScreen->mScale;
} else if (kResourceType == QMirClientNativeInterface::FormFactor) {
return &ubuntuScreen->mFormFactor;
} else } else
return NULL; return NULL;
} }
// Changes to these properties are emitted via the QMirClientNativeInterface::windowPropertyChanged
// signal fired by QMirClientWindow. Connect to this signal for these properties updates.
QVariantMap QMirClientNativeInterface::windowProperties(QPlatformWindow *window) const
{
QVariantMap propertyMap;
auto w = static_cast<QMirClientWindow*>(window);
if (w) {
propertyMap.insert("scale", w->scale());
propertyMap.insert("formFactor", w->formFactor());
}
return propertyMap;
}
QVariant QMirClientNativeInterface::windowProperty(QPlatformWindow *window, const QString &name) const
{
auto w = static_cast<QMirClientWindow*>(window);
if (!w) {
return QVariant();
}
if (name == QStringLiteral("scale")) {
return w->scale();
} else if (name == QStringLiteral("formFactor")) {
return w->formFactor();
} else {
return QVariant();
}
}
QVariant QMirClientNativeInterface::windowProperty(QPlatformWindow *window, const QString &name, const QVariant &defaultValue) const
{
QVariant returnVal = windowProperty(window, name);
if (!returnVal.isValid()) {
return defaultValue;
} else {
return returnVal;
}
}

View File

@ -43,11 +43,16 @@
#include <qpa/qplatformnativeinterface.h> #include <qpa/qplatformnativeinterface.h>
class QMirClientNativeInterface : public QPlatformNativeInterface { #include "qmirclientintegration.h"
public:
enum ResourceType { EglDisplay, EglContext, NativeOrientation, Display, MirConnection };
QMirClientNativeInterface(); class QPlatformScreen;
class QMirClientNativeInterface : public QPlatformNativeInterface {
Q_OBJECT
public:
enum ResourceType { EglDisplay, EglContext, NativeOrientation, Display, MirConnection, MirSurface, Scale, FormFactor };
QMirClientNativeInterface(const QMirClientClientIntegration *integration);
~QMirClientNativeInterface(); ~QMirClientNativeInterface();
// QPlatformNativeInterface methods. // QPlatformNativeInterface methods.
@ -59,14 +64,20 @@ public:
void* nativeResourceForScreen(const QByteArray& resourceString, void* nativeResourceForScreen(const QByteArray& resourceString,
QScreen* screen) override; QScreen* screen) override;
QVariantMap windowProperties(QPlatformWindow *window) const override;
QVariant windowProperty(QPlatformWindow *window, const QString &name) const override;
QVariant windowProperty(QPlatformWindow *window, const QString &name, const QVariant &defaultValue) const override;
// New methods. // New methods.
const QByteArray& genericEventFilterType() const { return mGenericEventFilterType; } const QByteArray& genericEventFilterType() const { return mGenericEventFilterType; }
void setMirConnection(void *mirConnection) { mMirConnection = mirConnection; }
Q_SIGNALS: // New signals
void screenPropertyChanged(QPlatformScreen *screen, const QString &propertyName);
private: private:
const QMirClientClientIntegration *mIntegration;
const QByteArray mGenericEventFilterType; const QByteArray mGenericEventFilterType;
Qt::ScreenOrientation* mNativeOrientation; Qt::ScreenOrientation* mNativeOrientation;
void *mMirConnection;
}; };
#endif // QMIRCLIENTNATIVEINTERFACE_H #endif // QMIRCLIENTNATIVEINTERFACE_H

View File

@ -40,19 +40,16 @@
#include "qmirclientplugin.h" #include "qmirclientplugin.h"
#include "qmirclientintegration.h" #include "qmirclientintegration.h"
#include "qmirclientlogging.h"
QStringList QMirClientIntegrationPlugin::keys() const Q_LOGGING_CATEGORY(mirclient, "qt.qpa.mirclient", QtWarningMsg)
{
QStringList list;
list << QStringLiteral("mirclient");
return list;
}
QPlatformIntegration* QMirClientIntegrationPlugin::create(const QString &system, QPlatformIntegration *QMirClientIntegrationPlugin::create(const QString &system,
const QStringList &) const QStringList &/*paramList*/,
int &argc, char **argv)
{ {
if (system.toLower() == QLatin1String("mirclient")) { if (system.toLower() == QLatin1String("mirclient")) {
return new QMirClientClientIntegration; return new QMirClientClientIntegration(argc, argv);
} else { } else {
return 0; return 0;
} }

View File

@ -49,8 +49,8 @@ class QMirClientIntegrationPlugin : public QPlatformIntegrationPlugin
Q_PLUGIN_METADATA(IID QPlatformIntegrationFactoryInterface_iid FILE "mirclient.json") Q_PLUGIN_METADATA(IID QPlatformIntegrationFactoryInterface_iid FILE "mirclient.json")
public: public:
QStringList keys() const; QPlatformIntegration *create(const QString &system, const QStringList &paramList,
QPlatformIntegration* create(const QString&, const QStringList&); int &argc, char **argv) override;
}; };
#endif // QMIRCLIENTPLUGIN_H #endif // QMIRCLIENTPLUGIN_H

View File

@ -1,6 +1,6 @@
/**************************************************************************** /****************************************************************************
** **
** Copyright (C) 2014-2015 Canonical, Ltd. ** Copyright (C) 2014-2016 Canonical, Ltd.
** Contact: https://www.qt.io/licensing/ ** Contact: https://www.qt.io/licensing/
** **
** This file is part of the plugins of the Qt Toolkit. ** This file is part of the plugins of the Qt Toolkit.
@ -42,11 +42,12 @@
#include "qmirclientscreen.h" #include "qmirclientscreen.h"
#include "qmirclientlogging.h" #include "qmirclientlogging.h"
#include "qmirclientorientationchangeevent_p.h" #include "qmirclientorientationchangeevent_p.h"
#include "qmirclientnativeinterface.h"
#include <mir_toolkit/mir_client_library.h> #include <mir_toolkit/mir_client_library.h>
// Qt // Qt
#include <QCoreApplication> #include <QGuiApplication>
#include <QtCore/qmath.h> #include <QtCore/qmath.h>
#include <QScreen> #include <QScreen>
#include <QThread> #include <QThread>
@ -55,9 +56,7 @@
#include <memory> #include <memory>
static const int kSwapInterval = 1; static const int overrideDevicePixelRatio = qgetenv("QT_DEVICE_PIXEL_RATIO").toInt();
#if !defined(QT_NO_DEBUG)
static const char *orientationToStr(Qt::ScreenOrientation orientation) { static const char *orientationToStr(Qt::ScreenOrientation orientation) {
switch (orientation) { switch (orientation) {
@ -71,173 +70,33 @@ static const char *orientationToStr(Qt::ScreenOrientation orientation) {
return "inverted portrait"; return "inverted portrait";
case Qt::InvertedLandscapeOrientation: case Qt::InvertedLandscapeOrientation:
return "inverted landscape"; return "inverted landscape";
default:
return "INVALID!";
} }
Q_UNREACHABLE();
} }
static void printEglConfig(EGLDisplay display, EGLConfig config) {
DASSERT(display != EGL_NO_DISPLAY);
DASSERT(config != nullptr);
static const struct { const EGLint attrib; const char* name; } kAttribs[] = {
{ EGL_BUFFER_SIZE, "EGL_BUFFER_SIZE" },
{ EGL_ALPHA_SIZE, "EGL_ALPHA_SIZE" },
{ EGL_BLUE_SIZE, "EGL_BLUE_SIZE" },
{ EGL_GREEN_SIZE, "EGL_GREEN_SIZE" },
{ EGL_RED_SIZE, "EGL_RED_SIZE" },
{ EGL_DEPTH_SIZE, "EGL_DEPTH_SIZE" },
{ EGL_STENCIL_SIZE, "EGL_STENCIL_SIZE" },
{ EGL_CONFIG_CAVEAT, "EGL_CONFIG_CAVEAT" },
{ EGL_CONFIG_ID, "EGL_CONFIG_ID" },
{ EGL_LEVEL, "EGL_LEVEL" },
{ EGL_MAX_PBUFFER_HEIGHT, "EGL_MAX_PBUFFER_HEIGHT" },
{ EGL_MAX_PBUFFER_PIXELS, "EGL_MAX_PBUFFER_PIXELS" },
{ EGL_MAX_PBUFFER_WIDTH, "EGL_MAX_PBUFFER_WIDTH" },
{ EGL_NATIVE_RENDERABLE, "EGL_NATIVE_RENDERABLE" },
{ EGL_NATIVE_VISUAL_ID, "EGL_NATIVE_VISUAL_ID" },
{ EGL_NATIVE_VISUAL_TYPE, "EGL_NATIVE_VISUAL_TYPE" },
{ EGL_SAMPLES, "EGL_SAMPLES" },
{ EGL_SAMPLE_BUFFERS, "EGL_SAMPLE_BUFFERS" },
{ EGL_SURFACE_TYPE, "EGL_SURFACE_TYPE" },
{ EGL_TRANSPARENT_TYPE, "EGL_TRANSPARENT_TYPE" },
{ EGL_TRANSPARENT_BLUE_VALUE, "EGL_TRANSPARENT_BLUE_VALUE" },
{ EGL_TRANSPARENT_GREEN_VALUE, "EGL_TRANSPARENT_GREEN_VALUE" },
{ EGL_TRANSPARENT_RED_VALUE, "EGL_TRANSPARENT_RED_VALUE" },
{ EGL_BIND_TO_TEXTURE_RGB, "EGL_BIND_TO_TEXTURE_RGB" },
{ EGL_BIND_TO_TEXTURE_RGBA, "EGL_BIND_TO_TEXTURE_RGBA" },
{ EGL_MIN_SWAP_INTERVAL, "EGL_MIN_SWAP_INTERVAL" },
{ EGL_MAX_SWAP_INTERVAL, "EGL_MAX_SWAP_INTERVAL" },
{ -1, NULL }
};
const char* string = eglQueryString(display, EGL_VENDOR);
LOG("EGL vendor: %s", string);
string = eglQueryString(display, EGL_VERSION);
LOG("EGL version: %s", string);
string = eglQueryString(display, EGL_EXTENSIONS);
LOG("EGL extensions: %s", string);
LOG("EGL configuration attibutes:");
for (int index = 0; kAttribs[index].attrib != -1; index++) {
EGLint value;
if (eglGetConfigAttrib(display, config, kAttribs[index].attrib, &value))
LOG(" %s: %d", kAttribs[index].name, static_cast<int>(value));
}
}
#endif
const QEvent::Type OrientationChangeEvent::mType = const QEvent::Type OrientationChangeEvent::mType =
static_cast<QEvent::Type>(QEvent::registerEventType()); static_cast<QEvent::Type>(QEvent::registerEventType());
static const MirDisplayOutput *find_active_output(
const MirDisplayConfiguration *conf)
{
const MirDisplayOutput *output = NULL;
for (uint32_t d = 0; d < conf->num_outputs; d++)
{
const MirDisplayOutput *out = conf->outputs + d;
if (out->used && QMirClientScreen::QMirClientScreen(const MirOutput *output, MirConnection *connection)
out->connected && : mDevicePixelRatio(1.0)
out->num_modes && , mFormat(QImage::Format_RGB32)
out->current_mode < out->num_modes)
{
output = out;
break;
}
}
return output;
}
QMirClientScreen::QMirClientScreen(MirConnection *connection)
: mFormat(QImage::Format_RGB32)
, mDepth(32) , mDepth(32)
, mDpi{0}
, mFormFactor{mir_form_factor_unknown}
, mScale{1.0}
, mOutputId(0) , mOutputId(0)
, mSurfaceFormat()
, mEglDisplay(EGL_NO_DISPLAY)
, mEglConfig(nullptr)
, mCursor(connection) , mCursor(connection)
{ {
// Initialize EGL. setMirOutput(output);
ASSERT(eglBindAPI(EGL_OPENGL_ES_API) == EGL_TRUE);
mEglNativeDisplay = mir_connection_get_egl_native_display(connection);
ASSERT((mEglDisplay = eglGetDisplay(mEglNativeDisplay)) != EGL_NO_DISPLAY);
ASSERT(eglInitialize(mEglDisplay, nullptr, nullptr) == EGL_TRUE);
// Configure EGL buffers format.
mSurfaceFormat.setRedBufferSize(8);
mSurfaceFormat.setGreenBufferSize(8);
mSurfaceFormat.setBlueBufferSize(8);
mSurfaceFormat.setAlphaBufferSize(8);
mSurfaceFormat.setDepthBufferSize(24);
mSurfaceFormat.setStencilBufferSize(8);
if (!qEnvironmentVariableIsEmpty("QTUBUNTU_MULTISAMPLE")) {
mSurfaceFormat.setSamples(4);
DLOG("ubuntumirclient: setting MSAA to 4 samples");
}
#ifdef QTUBUNTU_USE_OPENGL
mSurfaceFormat.setRenderableType(QSurfaceFormat::OpenGL);
#else
mSurfaceFormat.setRenderableType(QSurfaceFormat::OpenGLES);
#endif
mEglConfig = q_configFromGLFormat(mEglDisplay, mSurfaceFormat, true);
#if !defined(QT_NO_DEBUG)
printEglConfig(mEglDisplay, mEglConfig);
#endif
// Set vblank swap interval.
int swapInterval = kSwapInterval;
QByteArray swapIntervalString = qgetenv("QTUBUNTU_SWAPINTERVAL");
if (!swapIntervalString.isEmpty()) {
bool ok;
swapInterval = swapIntervalString.toInt(&ok);
if (!ok)
swapInterval = kSwapInterval;
}
DLOG("ubuntumirclient: setting swap interval to %d", swapInterval);
eglSwapInterval(mEglDisplay, swapInterval);
// Get screen resolution.
auto configDeleter = [](MirDisplayConfiguration *config) { mir_display_config_destroy(config); };
using configUp = std::unique_ptr<MirDisplayConfiguration, decltype(configDeleter)>;
configUp displayConfig(mir_connection_create_display_config(connection), configDeleter);
ASSERT(displayConfig != nullptr);
auto const displayOutput = find_active_output(displayConfig.get());
ASSERT(displayOutput != nullptr);
mOutputId = displayOutput->output_id;
mPhysicalSize = QSizeF(displayOutput->physical_width_mm, displayOutput->physical_height_mm);
DLOG("ubuntumirclient: screen physical size: %.2fx%.2f", mPhysicalSize.width(), mPhysicalSize.height());
const MirDisplayMode *mode = &displayOutput->modes[displayOutput->current_mode];
const int kScreenWidth = mode->horizontal_resolution;
const int kScreenHeight = mode->vertical_resolution;
DASSERT(kScreenWidth > 0 && kScreenHeight > 0);
DLOG("ubuntumirclient: screen resolution: %dx%d", kScreenWidth, kScreenHeight);
mGeometry = QRect(0, 0, kScreenWidth, kScreenHeight);
DLOG("QQMirClientScreen::QQMirClientScreen (this=%p)", this);
// Set the default orientation based on the initial screen dimmensions.
mNativeOrientation = (mGeometry.width() >= mGeometry.height()) ? Qt::LandscapeOrientation : Qt::PortraitOrientation;
// If it's a landscape device (i.e. some tablets), start in landscape, otherwise portrait
mCurrentOrientation = (mNativeOrientation == Qt::LandscapeOrientation) ? Qt::LandscapeOrientation : Qt::PortraitOrientation;
} }
QMirClientScreen::~QMirClientScreen() QMirClientScreen::~QMirClientScreen()
{ {
eglTerminate(mEglDisplay);
} }
void QMirClientScreen::customEvent(QEvent* event) { void QMirClientScreen::customEvent(QEvent* event) {
DASSERT(QThread::currentThread() == thread()); Q_ASSERT(QThread::currentThread() == thread());
OrientationChangeEvent* oReadingEvent = static_cast<OrientationChangeEvent*>(event); OrientationChangeEvent* oReadingEvent = static_cast<OrientationChangeEvent*>(event);
switch (oReadingEvent->mOrientation) { switch (oReadingEvent->mOrientation) {
@ -261,14 +120,10 @@ void QMirClientScreen::customEvent(QEvent* event) {
Qt::InvertedLandscapeOrientation : Qt::InvertedPortraitOrientation; Qt::InvertedLandscapeOrientation : Qt::InvertedPortraitOrientation;
break; break;
} }
default: {
DLOG("QMirClientScreen::customEvent - Unknown orientation.");
return;
}
} }
// Raise the event signal so that client apps know the orientation changed // Raise the event signal so that client apps know the orientation changed
DLOG("QMirClientScreen::customEvent - handling orientation change to %s", orientationToStr(mCurrentOrientation)); qCDebug(mirclient, "QMirClientScreen::customEvent - handling orientation change to %s", orientationToStr(mCurrentOrientation));
QWindowSystemInterface::handleScreenOrientationChange(screen(), mCurrentOrientation); QWindowSystemInterface::handleScreenOrientationChange(screen(), mCurrentOrientation);
} }
@ -289,7 +144,7 @@ void QMirClientScreen::handleWindowSurfaceResize(int windowWidth, int windowHeig
mGeometry.setWidth(currGeometry.height()); mGeometry.setWidth(currGeometry.height());
mGeometry.setHeight(currGeometry.width()); mGeometry.setHeight(currGeometry.width());
DLOG("QMirClientScreen::handleWindowSurfaceResize - new screen geometry (w=%d, h=%d)", qCDebug(mirclient, "QMirClientScreen::handleWindowSurfaceResize - new screen geometry (w=%d, h=%d)",
mGeometry.width(), mGeometry.height()); mGeometry.width(), mGeometry.height());
QWindowSystemInterface::handleScreenGeometryChange(screen(), QWindowSystemInterface::handleScreenGeometryChange(screen(),
mGeometry /* newGeometry */, mGeometry /* newGeometry */,
@ -300,7 +155,108 @@ void QMirClientScreen::handleWindowSurfaceResize(int windowWidth, int windowHeig
} else { } else {
mCurrentOrientation = Qt::LandscapeOrientation; mCurrentOrientation = Qt::LandscapeOrientation;
} }
DLOG("QMirClientScreen::handleWindowSurfaceResize - new orientation %s",orientationToStr(mCurrentOrientation)); qCDebug(mirclient, "QMirClientScreen::handleWindowSurfaceResize - new orientation %s",orientationToStr(mCurrentOrientation));
QWindowSystemInterface::handleScreenOrientationChange(screen(), mCurrentOrientation); QWindowSystemInterface::handleScreenOrientationChange(screen(), mCurrentOrientation);
} }
} }
void QMirClientScreen::setMirOutput(const MirOutput *output)
{
// Physical screen size (in mm)
mPhysicalSize.setWidth(mir_output_get_physical_width_mm(output));
mPhysicalSize.setHeight(mir_output_get_physical_height_mm(output));
// Pixel Format
// mFormat = qImageFormatFromMirPixelFormat(mir_output_get_current_pixel_format(output)); // GERRY: TODO
// Pixel depth
mDepth = 8 * MIR_BYTES_PER_PIXEL(mir_output_get_current_pixel_format(output));
// Mode = Resolution & refresh rate
const MirOutputMode *mode = mir_output_get_current_mode(output);
mNativeGeometry.setX(mir_output_get_position_x(output));
mNativeGeometry.setY(mir_output_get_position_y(output));
mNativeGeometry.setWidth(mir_output_mode_get_width(mode));
mNativeGeometry.setHeight(mir_output_mode_get_height(mode));
mRefreshRate = mir_output_mode_get_refresh_rate(mode);
// UI scale & DPR
mScale = mir_output_get_scale_factor(output);
if (overrideDevicePixelRatio > 0) {
mDevicePixelRatio = overrideDevicePixelRatio;
} else {
mDevicePixelRatio = 1.0; // FIXME - need to determine suitable DPR for the specified scale
}
mFormFactor = mir_output_get_form_factor(output);
mOutputId = mir_output_get_id(output);
mGeometry.setX(mNativeGeometry.x());
mGeometry.setY(mNativeGeometry.y());
mGeometry.setWidth(mNativeGeometry.width());
mGeometry.setHeight(mNativeGeometry.height());
// Set the default orientation based on the initial screen dimensions.
mNativeOrientation = (mGeometry.width() >= mGeometry.height()) ? Qt::LandscapeOrientation : Qt::PortraitOrientation;
// If it's a landscape device (i.e. some tablets), start in landscape, otherwise portrait
mCurrentOrientation = (mNativeOrientation == Qt::LandscapeOrientation) ? Qt::LandscapeOrientation : Qt::PortraitOrientation;
}
void QMirClientScreen::updateMirOutput(const MirOutput *output)
{
auto oldRefreshRate = mRefreshRate;
auto oldScale = mScale;
auto oldFormFactor = mFormFactor;
auto oldGeometry = mGeometry;
setMirOutput(output);
// Emit change signals in particular order
if (oldGeometry != mGeometry) {
QWindowSystemInterface::handleScreenGeometryChange(screen(),
mGeometry /* newGeometry */,
mGeometry /* newAvailableGeometry */);
}
if (!qFuzzyCompare(mRefreshRate, oldRefreshRate)) {
QWindowSystemInterface::handleScreenRefreshRateChange(screen(), mRefreshRate);
}
auto nativeInterface = static_cast<QMirClientNativeInterface *>(qGuiApp->platformNativeInterface());
if (!qFuzzyCompare(mScale, oldScale)) {
nativeInterface->screenPropertyChanged(this, QStringLiteral("scale"));
}
if (mFormFactor != oldFormFactor) {
nativeInterface->screenPropertyChanged(this, QStringLiteral("formFactor"));
}
}
void QMirClientScreen::setAdditionalMirDisplayProperties(float scale, MirFormFactor formFactor, int dpi)
{
if (mDpi != dpi) {
mDpi = dpi;
QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(screen(), dpi, dpi);
}
auto nativeInterface = static_cast<QMirClientNativeInterface *>(qGuiApp->platformNativeInterface());
if (!qFuzzyCompare(mScale, scale)) {
mScale = scale;
nativeInterface->screenPropertyChanged(this, QStringLiteral("scale"));
}
if (mFormFactor != formFactor) {
mFormFactor = formFactor;
nativeInterface->screenPropertyChanged(this, QStringLiteral("formFactor"));
}
}
QDpi QMirClientScreen::logicalDpi() const
{
if (mDpi > 0) {
return QDpi(mDpi, mDpi);
} else {
return QPlatformScreen::logicalDpi();
}
}

View File

@ -1,6 +1,6 @@
/**************************************************************************** /****************************************************************************
** **
** Copyright (C) 2014-2015 Canonical, Ltd. ** Copyright (C) 2014-2016 Canonical, Ltd.
** Contact: https://www.qt.io/licensing/ ** Contact: https://www.qt.io/licensing/
** **
** This file is part of the plugins of the Qt Toolkit. ** This file is part of the plugins of the Qt Toolkit.
@ -43,17 +43,19 @@
#include <qpa/qplatformscreen.h> #include <qpa/qplatformscreen.h>
#include <QSurfaceFormat> #include <QSurfaceFormat>
#include <EGL/egl.h>
#include <mir_toolkit/common.h> // just for MirFormFactor enum
#include "qmirclientcursor.h" #include "qmirclientcursor.h"
struct MirConnection; struct MirConnection;
struct MirOutput;
class QMirClientScreen : public QObject, public QPlatformScreen class QMirClientScreen : public QObject, public QPlatformScreen
{ {
Q_OBJECT Q_OBJECT
public: public:
QMirClientScreen(MirConnection *connection); QMirClientScreen(const MirOutput *output, MirConnection *connection);
virtual ~QMirClientScreen(); virtual ~QMirClientScreen();
// QPlatformScreen methods. // QPlatformScreen methods.
@ -62,34 +64,43 @@ public:
QRect geometry() const override { return mGeometry; } QRect geometry() const override { return mGeometry; }
QRect availableGeometry() const override { return mGeometry; } QRect availableGeometry() const override { return mGeometry; }
QSizeF physicalSize() const override { return mPhysicalSize; } QSizeF physicalSize() const override { return mPhysicalSize; }
qreal devicePixelRatio() const override { return mDevicePixelRatio; }
QDpi logicalDpi() const override;
Qt::ScreenOrientation nativeOrientation() const override { return mNativeOrientation; } Qt::ScreenOrientation nativeOrientation() const override { return mNativeOrientation; }
Qt::ScreenOrientation orientation() const override { return mNativeOrientation; } Qt::ScreenOrientation orientation() const override { return mNativeOrientation; }
QPlatformCursor *cursor() const override { return const_cast<QMirClientCursor*>(&mCursor); } QPlatformCursor *cursor() const override { return const_cast<QMirClientCursor*>(&mCursor); }
// New methods. // Additional Screen properties from Mir
QSurfaceFormat surfaceFormat() const { return mSurfaceFormat; } int mirOutputId() const { return mOutputId; }
EGLDisplay eglDisplay() const { return mEglDisplay; } MirFormFactor formFactor() const { return mFormFactor; }
EGLConfig eglConfig() const { return mEglConfig; } float scale() const { return mScale; }
EGLNativeDisplayType eglNativeDisplay() const { return mEglNativeDisplay; }
// Internally used methods
void updateMirOutput(const MirOutput *output);
void setAdditionalMirDisplayProperties(float scale, MirFormFactor formFactor, int dpi);
void handleWindowSurfaceResize(int width, int height); void handleWindowSurfaceResize(int width, int height);
uint32_t mirOutputId() const { return mOutputId; }
// QObject methods. // QObject methods.
void customEvent(QEvent* event) override; void customEvent(QEvent* event) override;
private: private:
QRect mGeometry; void setMirOutput(const MirOutput *output);
QRect mGeometry, mNativeGeometry;
QSizeF mPhysicalSize; QSizeF mPhysicalSize;
qreal mDevicePixelRatio;
Qt::ScreenOrientation mNativeOrientation; Qt::ScreenOrientation mNativeOrientation;
Qt::ScreenOrientation mCurrentOrientation; Qt::ScreenOrientation mCurrentOrientation;
QImage::Format mFormat; QImage::Format mFormat;
int mDepth; int mDepth;
uint32_t mOutputId; int mDpi;
QSurfaceFormat mSurfaceFormat; qreal mRefreshRate;
EGLDisplay mEglDisplay; MirFormFactor mFormFactor;
EGLConfig mEglConfig; float mScale;
EGLNativeDisplayType mEglNativeDisplay; int mOutputId;
QMirClientCursor mCursor; QMirClientCursor mCursor;
friend class QMirClientNativeInterface;
}; };
#endif // QMIRCLIENTSCREEN_H #endif // QMIRCLIENTSCREEN_H

View File

@ -0,0 +1,161 @@
/****************************************************************************
**
** Copyright (C) 2016 Canonical, Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qmirclientscreenobserver.h"
#include "qmirclientscreen.h"
#include "qmirclientwindow.h"
#include "qmirclientlogging.h"
// Qt
#include <QMetaObject>
#include <QPointer>
// Mir
#include <mirclient/mir_toolkit/mir_connection.h>
#include <mirclient/mir_toolkit/mir_display_configuration.h>
#include <memory>
namespace {
static void displayConfigurationChangedCallback(MirConnection */*connection*/, void* context)
{
ASSERT(context != NULL);
QMirClientScreenObserver *observer = static_cast<QMirClientScreenObserver *>(context);
QMetaObject::invokeMethod(observer, "update");
}
const char *mirFormFactorToStr(MirFormFactor formFactor)
{
switch (formFactor) {
case mir_form_factor_unknown: return "unknown";
case mir_form_factor_phone: return "phone";
case mir_form_factor_tablet: return "tablet";
case mir_form_factor_monitor: return "monitor";
case mir_form_factor_tv: return "tv";
case mir_form_factor_projector: return "projector";
}
Q_UNREACHABLE();
}
} // anonymous namespace
QMirClientScreenObserver::QMirClientScreenObserver(MirConnection *mirConnection)
: mMirConnection(mirConnection)
{
mir_connection_set_display_config_change_callback(mirConnection, ::displayConfigurationChangedCallback, this);
update();
}
void QMirClientScreenObserver::update()
{
// Wrap MirDisplayConfiguration to always delete when out of scope
auto configDeleter = [](MirDisplayConfig *config) { mir_display_config_release(config); };
using configUp = std::unique_ptr<MirDisplayConfig, decltype(configDeleter)>;
configUp displayConfig(mir_connection_create_display_configuration(mMirConnection), configDeleter);
// Mir only tells us something changed, it is up to us to figure out what.
QList<QMirClientScreen*> newScreenList;
QList<QMirClientScreen*> oldScreenList = mScreenList;
mScreenList.clear();
for (int i = 0; i < mir_display_config_get_num_outputs(displayConfig.get()); i++) {
const MirOutput *output = mir_display_config_get_output(displayConfig.get(), i);
if (mir_output_is_enabled(output)) {
QMirClientScreen *screen = findScreenWithId(oldScreenList, mir_output_get_id(output));
if (screen) { // we've already set up this display before
screen->updateMirOutput(output);
oldScreenList.removeAll(screen);
} else {
// new display, so create QMirClientScreen for it
screen = new QMirClientScreen(output, mMirConnection);
newScreenList.append(screen);
qCDebug(mirclient) << "Added Screen with id" << mir_output_get_id(output)
<< "and geometry" << screen->geometry();
}
mScreenList.append(screen);
}
}
// Announce old & unused Screens, should be deleted by the slot
Q_FOREACH (const auto screen, oldScreenList) {
Q_EMIT screenRemoved(screen);
}
/*
* Mir's MirDisplayOutput does not include formFactor or scale for some reason, but Qt
* will want that information on creating the QScreen. Only way we get that info is when
* Mir positions a Window on that Screen. See "handleScreenPropertiesChange" method
*/
// Announce new Screens
Q_FOREACH (const auto screen, newScreenList) {
Q_EMIT screenAdded(screen);
}
qCDebug(mirclient) << "=======================================";
for (auto screen: mScreenList) {
qCDebug(mirclient) << screen << "- id:" << screen->mirOutputId()
<< "geometry:" << screen->geometry()
<< "form factor:" << mirFormFactorToStr(screen->formFactor())
<< "scale:" << screen->scale();
}
qCDebug(mirclient) << "=======================================";
}
QMirClientScreen *QMirClientScreenObserver::findScreenWithId(int id)
{
return findScreenWithId(mScreenList, id);
}
QMirClientScreen *QMirClientScreenObserver::findScreenWithId(const QList<QMirClientScreen *> &list, int id)
{
Q_FOREACH (const auto screen, list) {
if (screen->mirOutputId() == id) {
return screen;
}
}
return nullptr;
}
void QMirClientScreenObserver::handleScreenPropertiesChange(QMirClientScreen *screen, int dpi,
MirFormFactor formFactor, float scale)
{
screen->setAdditionalMirDisplayProperties(scale, formFactor, dpi);
}

View File

@ -0,0 +1,78 @@
/****************************************************************************
**
** Copyright (C) 2016 Canonical, Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QMIRCLIENTSCREENOBSERVER_H
#define QMIRCLIENTSCREENOBSERVER_H
#include <QObject>
#include <mir_toolkit/mir_connection.h>
class QMirClientScreen;
class QMirClientScreenObserver : public QObject
{
Q_OBJECT
public:
QMirClientScreenObserver(MirConnection *connection);
QList<QMirClientScreen*> screens() const { return mScreenList; }
QMirClientScreen *findScreenWithId(int id);
void handleScreenPropertiesChange(QMirClientScreen *screen, int dpi,
MirFormFactor formFactor, float scale);
Q_SIGNALS:
void screenAdded(QMirClientScreen *screen);
void screenRemoved(QMirClientScreen *screen);
private Q_SLOTS:
void update();
private:
QMirClientScreen *findScreenWithId(const QList<QMirClientScreen *> &list, int id);
void removeScreen(QMirClientScreen *screen);
MirConnection *mMirConnection;
QList<QMirClientScreen*> mScreenList;
};
#endif // QMIRCLIENTSCREENOBSERVER_H

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
/**************************************************************************** /****************************************************************************
** **
** Copyright (C) 2014-2015 Canonical, Ltd. ** Copyright (C) 2014-2016 Canonical, Ltd.
** Contact: https://www.qt.io/licensing/ ** Contact: https://www.qt.io/licensing/
** **
** This file is part of the plugins of the Qt Toolkit. ** This file is part of the plugins of the Qt Toolkit.
@ -45,44 +45,74 @@
#include <QSharedPointer> #include <QSharedPointer>
#include <QMutex> #include <QMutex>
#include <mir_toolkit/common.h> // needed only for MirFormFactor enum
#include <memory> #include <memory>
class QMirClientClipboard; #include <EGL/egl.h>
class QMirClientAppStateController;
class QMirClientDebugExtension;
class QMirClientNativeInterface;
class QMirClientInput; class QMirClientInput;
class QMirClientScreen; class QMirClientScreen;
class QMirClientSurface; class UbuntuSurface;
struct MirConnection;
struct MirSurface; struct MirSurface;
class MirConnection;
class QMirClientWindow : public QObject, public QPlatformWindow class QMirClientWindow : public QObject, public QPlatformWindow
{ {
Q_OBJECT Q_OBJECT
public: public:
QMirClientWindow(QWindow *w, const QSharedPointer<QMirClientClipboard> &clipboard, QMirClientScreen *screen, QMirClientWindow(QWindow *w, QMirClientInput *input, QMirClientNativeInterface* native,
QMirClientInput *input, MirConnection *mirConnection); QMirClientAppStateController *appState, EGLDisplay eglDisplay,
MirConnection *mirConnection, QMirClientDebugExtension *debugExt);
virtual ~QMirClientWindow(); virtual ~QMirClientWindow();
// QPlatformWindow methods. // QPlatformWindow methods.
WId winId() const override; WId winId() const override;
QRect geometry() const override;
void setGeometry(const QRect&) override; void setGeometry(const QRect&) override;
void setWindowState(Qt::WindowState state) override; void setWindowState(Qt::WindowState state) override;
void setWindowFlags(Qt::WindowFlags flags) override;
void setVisible(bool visible) override; void setVisible(bool visible) override;
void setWindowTitle(const QString &title) override; void setWindowTitle(const QString &title) override;
void propagateSizeHints() override; void propagateSizeHints() override;
bool isExposed() const override;
QPoint mapToGlobal(const QPoint &pos) const override;
QSurfaceFormat format() const override;
// Additional Window properties exposed by NativeInterface
MirFormFactor formFactor() const { return mFormFactor; }
float scale() const { return mScale; }
// New methods. // New methods.
void *eglSurface() const; void *eglSurface() const;
MirSurface *mirSurface() const; MirSurface *mirSurface() const;
void handleSurfaceResized(int width, int height); void handleSurfaceResized(int width, int height);
void handleSurfaceFocused(); void handleSurfaceExposeChange(bool exposed);
void handleSurfaceFocusChanged(bool focused);
void handleSurfaceVisibilityChanged(bool visible);
void handleSurfaceStateChanged(Qt::WindowState state);
void onSwapBuffersDone(); void onSwapBuffersDone();
void handleScreenPropertiesChange(MirFormFactor formFactor, float scale);
QString persistentSurfaceId();
private: private:
void updatePanelHeightHack(Qt::WindowState); void updateSurfaceState();
mutable QMutex mMutex; mutable QMutex mMutex;
const WId mId; const WId mId;
const QSharedPointer<QMirClientClipboard> mClipboard; Qt::WindowState mWindowState;
std::unique_ptr<QMirClientSurface> mSurface; Qt::WindowFlags mWindowFlags;
bool mWindowVisible;
bool mWindowExposed;
QMirClientAppStateController *mAppStateController;
QMirClientDebugExtension *mDebugExtention;
QMirClientNativeInterface *mNativeInterface;
std::unique_ptr<UbuntuSurface> mSurface;
float mScale;
MirFormFactor mFormFactor;
}; };
#endif // QMIRCLIENTWINDOW_H #endif // QMIRCLIENTWINDOW_H