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",
"test": "qpa/mirclient",
"sources": [
{ "type": "pkgConfig", "args": "egl mirclient ubuntu-platform-api" }
{ "type": "pkgConfig", "args": "egl mirclient ubuntu-platform-api libcontent-hub" }
]
},
"mtdev": {

View File

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

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

View File

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

View File

@ -39,41 +39,36 @@
#include "qmirclientclipboard.h"
#include "qmirclientlogging.h"
#include "qmirclientwindow.h"
#include <QDBusPendingCallWatcher>
#include <QGuiApplication>
#include <QSignalBlocker>
#include <QtCore/QMimeData>
#include <QtCore/QStringList>
#include <QDBusInterface>
#include <QDBusPendingCallWatcher>
#include <QDBusPendingReply>
// FIXME(loicm) The clipboard data format is not defined by Ubuntu Platform API
// which makes it impossible to have non-Qt applications communicate with Qt
// applications through the clipboard API. The solution would be to have
// Ubuntu Platform define the data format or propose an API that supports
// embedding different mime types in the clipboard.
// content-hub
#include <com/ubuntu/content/hub.h>
// Data format:
// number of mime types (sizeof(int))
// data layout ((4 * sizeof(int)) * number of mime types)
// mime type string offset (sizeof(int))
// mime type string size (sizeof(int))
// data offset (sizeof(int))
// data size (sizeof(int))
// data (n bytes)
namespace {
const int maxFormatsCount = 16;
const int maxBufferSize = 4 * 1024 * 1024; // 4 Mb
}
// get this cumbersome nested namespace out of the way
using namespace com::ubuntu::content;
QMirClientClipboard::QMirClientClipboard()
: mMimeData(new QMimeData)
, mIsOutdated(true)
, mUpdatesDisabled(false)
, mDBusSetupDone(false)
, mContentHub(Hub::Client::instance())
{
connect(mContentHub, &Hub::pasteboardChanged, this, [this]() {
if (mClipboardState == QMirClientClipboard::SyncedClipboard) {
mClipboardState = QMirClientClipboard::OutdatedClipboard;
emitChanged(QClipboard::Clipboard);
}
});
connect(qGuiApp, &QGuiApplication::applicationStateChanged,
this, &QMirClientClipboard::onApplicationStateChanged);
requestMimeData();
}
QMirClientClipboard::~QMirClientClipboard()
@ -81,202 +76,39 @@ QMirClientClipboard::~QMirClientClipboard()
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)
{
if (mode != QClipboard::Clipboard)
return nullptr;
if (mIsOutdated && mPendingGetContentsCall.isNull()) {
requestDBusClipboardContents();
// Blocks dataChanged() signal from being emitted. Makes no sense to emit it from
// 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;
}
void QMirClientClipboard::setMimeData(QMimeData* mimeData, QClipboard::Mode mode)
{
if (mode != QClipboard::Clipboard)
return;
QWindow *focusWindow = QGuiApplication::focusWindow();
if (focusWindow && mode == QClipboard::Clipboard && mimeData != nullptr) {
QString surfaceId = static_cast<QMirClientWindow*>(focusWindow->handle())->persistentSurfaceId();
if (!mPendingGetContentsCall.isNull()) {
// Ignore whatever comes from the system clipboard as we are going to change it anyway
QObject::disconnect(mPendingGetContentsCall.data(), 0, this, 0);
mUpdatesDisabled = true;
mPendingGetContentsCall->waitForFinished();
mUpdatesDisabled = false;
delete mPendingGetContentsCall.data();
}
QDBusPendingCall reply = mContentHub->createPaste(surfaceId, *mimeData);
if (mimeData != nullptr) {
QByteArray serializedMimeData = serializeMimeData(mimeData);
if (!serializedMimeData.isEmpty()) {
setDBusClipboardContents(serializedMimeData);
}
// Don't care whether it succeeded
QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply, this);
connect(watcher, &QDBusPendingCallWatcher::finished,
watcher, &QObject::deleteLater);
mMimeData = mimeData;
mClipboardState = SyncedClipboard;
emitChanged(QClipboard::Clipboard);
}
}
@ -292,25 +124,58 @@ bool QMirClientClipboard::ownsMode(QClipboard::Mode mode) const
return false;
}
void QMirClientClipboard::setDBusClipboardContents(const QByteArray &clipboardContents)
void QMirClientClipboard::onApplicationStateChanged(Qt::ApplicationState state)
{
if (!mDBusSetupDone) {
setupDBus();
if (state == Qt::ApplicationActive) {
// Only focused or active applications might be allowed to paste, so we probably
// missed changes in the clipboard while we were hidden, inactive or, more importantly,
// suspended.
requestMimeData();
}
if (!mPendingSetContentsCall.isNull()) {
// Ignore any previous set call as we are going to overwrite it anyway
QObject::disconnect(mPendingSetContentsCall.data(), 0, this, 0);
mUpdatesDisabled = true;
mPendingSetContentsCall->waitForFinished();
mUpdatesDisabled = false;
delete mPendingSetContentsCall.data();
}
QDBusPendingCall pendingCall = mDBusClipboard->asyncCall(QStringLiteral("SetContents"), clipboardContents);
mPendingSetContentsCall = new QDBusPendingCallWatcher(pendingCall, this);
QObject::connect(mPendingSetContentsCall.data(), &QDBusPendingCallWatcher::finished,
this, &QMirClientClipboard::onDBusClipboardSetContentsFinished);
}
void QMirClientClipboard::updateMimeData()
{
if (qGuiApp->applicationState() != Qt::ApplicationActive) {
// Don't even bother asking as content-hub would probably ignore our request (and should).
return;
}
delete mMimeData;
QWindow *focusWindow = QGuiApplication::focusWindow();
if (focusWindow) {
QString surfaceId = static_cast<QMirClientWindow*>(focusWindow->handle())->persistentSurfaceId();
mMimeData = mContentHub->latestPaste(surfaceId);
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 <QPointer>
class QDBusInterface;
namespace com {
namespace ubuntu {
namespace content {
class Hub;
}
}
}
class QDBusPendingCallWatcher;
class QMirClientClipboard : public QObject, public QPlatformClipboard
@ -61,31 +69,24 @@ public:
bool supportsMode(QClipboard::Mode mode) const override;
bool ownsMode(QClipboard::Mode mode) const override;
void requestDBusClipboardContents();
private Q_SLOTS:
void onDBusClipboardGetContentsFinished(QDBusPendingCallWatcher*);
void onDBusClipboardSetContentsFinished(QDBusPendingCallWatcher*);
void updateMimeData(const QByteArray &serializedMimeData);
void onApplicationStateChanged(Qt::ApplicationState state);
private:
void setupDBus();
QByteArray serializeMimeData(QMimeData *mimeData) const;
QMimeData *deserializeMimeData(const QByteArray &serializedMimeData) const;
void setDBusClipboardContents(const QByteArray &clipboardContents);
void updateMimeData();
void requestMimeData();
QMimeData *mMimeData;
bool mIsOutdated;
QPointer<QDBusInterface> mDBusClipboard;
enum {
OutdatedClipboard, // Our mimeData is outdated, need to fetch latest from ContentHub
SyncingClipboard, // Our mimeData is outdated and we are waiting for ContentHub to reply with the latest paste
SyncedClipboard // Our mimeData is in sync with what ContentHub has
} mClipboardState{OutdatedClipboard};
QPointer<QDBusPendingCallWatcher> mPendingGetContentsCall;
QPointer<QDBusPendingCallWatcher> mPendingSetContentsCall;
com::ubuntu::content::Hub *mContentHub;
bool mUpdatesDisabled;
bool mDBusSetupDone;
QDBusPendingCallWatcher *mPasteReply{nullptr};
};
#endif // QMIRCLIENTCLIPBOARD_H

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

@ -41,23 +41,15 @@
#ifndef QMIRCLIENTLOGGING_H
#define QMIRCLIENTLOGGING_H
// Logging and assertion macros.
#define LOG(...) qDebug(__VA_ARGS__)
#define LOG_IF(cond,...) do { if (cond) qDebug(__VA_ARGS__); } while (0)
#define ASSERT(cond) ((!(cond)) ? qt_assert(#cond,__FILE__,__LINE__) : qt_noop())
#define NOT_REACHED() qt_assert("Not reached!",__FILE__,__LINE__)
#include <QLoggingCategory>
// Logging and assertion macros are compiled out for release builds.
#if !defined(QT_NO_DEBUG)
#define DLOG(...) LOG(__VA_ARGS__)
#define DLOG_IF(cond,...) LOG_IF((cond), __VA_ARGS__)
#define DASSERT(cond) ASSERT((cond))
#define DNOT_REACHED() NOT_REACHED()
#else
#define DLOG(...) qt_noop()
#define DLOG_IF(cond,...) qt_noop()
#define DASSERT(cond) qt_noop()
#define DNOT_REACHED() qt_noop()
#endif
#define ASSERT(cond) ((!(cond)) ? qt_assert(#cond,__FILE__,__LINE__) : qt_noop())
Q_DECLARE_LOGGING_CATEGORY(mirclient)
Q_DECLARE_LOGGING_CATEGORY(mirclientBufferSwap)
Q_DECLARE_LOGGING_CATEGORY(mirclientInput)
Q_DECLARE_LOGGING_CATEGORY(mirclientGraphics)
Q_DECLARE_LOGGING_CATEGORY(mirclientCursor)
Q_DECLARE_LOGGING_CATEGORY(mirclientDebug)
#endif // QMIRCLIENTLOGGING_H

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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