This is required to trigger more complex shortcut sequences. For example, with 'us' keyboard layout to enter '%' or '+' you have to press 'Shift' button. Previosly the following shortcuts could not be triggered: 'Shift' + '5' '%' 'Ctrl' + '+' 'Ctrl' + 'Shift' + '=' The same function also ensures that these shortcuts work with non-latin keyboard layouts. Change-Id: Id50c7bb28cf76b9f7a861ced7894b2cacae6ed65 Reviewed-by: David Edmundson <davidedmundson@kde.org> Reviewed-by: Gatis Paeglis <gatis.paeglis@qt.io> Reviewed-by: Johan Helsing <johan.helsing@qt.io>
513 lines
18 KiB
C++
513 lines
18 KiB
C++
/****************************************************************************
|
|
**
|
|
** Copyright (C) 2016 The Qt Company Ltd.
|
|
** Contact: https://www.qt.io/licensing/
|
|
**
|
|
** This file is part of the config.tests 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 "qwaylandintegration_p.h"
|
|
|
|
#include "qwaylanddisplay_p.h"
|
|
#include "qwaylandshmwindow_p.h"
|
|
#include "qwaylandinputdevice_p.h"
|
|
#include "qwaylandinputcontext_p.h"
|
|
#include "qwaylandshmbackingstore_p.h"
|
|
#include "qwaylandnativeinterface_p.h"
|
|
#if QT_CONFIG(clipboard)
|
|
#include "qwaylandclipboard_p.h"
|
|
#endif
|
|
#include "qwaylanddnd_p.h"
|
|
#include "qwaylandwindowmanagerintegration_p.h"
|
|
#include "qwaylandscreen_p.h"
|
|
|
|
#if defined(Q_OS_MACOS)
|
|
# include <QtFontDatabaseSupport/private/qcoretextfontdatabase_p.h>
|
|
# include <QtFontDatabaseSupport/private/qfontengine_coretext_p.h>
|
|
#else
|
|
# include <QtFontDatabaseSupport/private/qgenericunixfontdatabase_p.h>
|
|
#endif
|
|
#include <QtEventDispatcherSupport/private/qgenericunixeventdispatcher_p.h>
|
|
#include <QtThemeSupport/private/qgenericunixthemes_p.h>
|
|
|
|
#include <QtGui/private/qguiapplication_p.h>
|
|
|
|
#include <qpa/qwindowsysteminterface.h>
|
|
#include <qpa/qplatformcursor.h>
|
|
#include <QtGui/QSurfaceFormat>
|
|
#include <QtGui/QOpenGLContext>
|
|
#include <QSocketNotifier>
|
|
|
|
#include <qpa/qplatforminputcontextfactory_p.h>
|
|
#include <qpa/qplatformaccessibility.h>
|
|
#include <qpa/qplatforminputcontext.h>
|
|
|
|
#include "qwaylandhardwareintegration_p.h"
|
|
#include "qwaylandclientbufferintegration_p.h"
|
|
#include "qwaylandclientbufferintegrationfactory_p.h"
|
|
|
|
#include "qwaylandserverbufferintegration_p.h"
|
|
#include "qwaylandserverbufferintegrationfactory_p.h"
|
|
|
|
#include "qwaylandshellintegration_p.h"
|
|
#include "qwaylandshellintegrationfactory_p.h"
|
|
|
|
#include "qwaylandinputdeviceintegration_p.h"
|
|
#include "qwaylandinputdeviceintegrationfactory_p.h"
|
|
|
|
#ifndef QT_NO_ACCESSIBILITY_ATSPI_BRIDGE
|
|
#include <QtLinuxAccessibilitySupport/private/bridge_p.h>
|
|
#endif
|
|
|
|
#if QT_CONFIG(xkbcommon)
|
|
#include <QtXkbCommonSupport/private/qxkbcommon_p.h>
|
|
#endif
|
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
namespace QtWaylandClient {
|
|
|
|
class GenericWaylandTheme: public QGenericUnixTheme
|
|
{
|
|
public:
|
|
static QStringList themeNames()
|
|
{
|
|
QStringList result;
|
|
|
|
if (QGuiApplication::desktopSettingsAware()) {
|
|
const QByteArray desktopEnvironment = QGuiApplicationPrivate::platformIntegration()->services()->desktopEnvironment();
|
|
|
|
if (desktopEnvironment == QByteArrayLiteral("KDE")) {
|
|
#if QT_CONFIG(settings)
|
|
result.push_back(QStringLiteral("kde"));
|
|
#endif
|
|
} else if (!desktopEnvironment.isEmpty() &&
|
|
desktopEnvironment != QByteArrayLiteral("UNKNOWN") &&
|
|
desktopEnvironment != QByteArrayLiteral("GNOME") &&
|
|
desktopEnvironment != QByteArrayLiteral("UNITY") &&
|
|
desktopEnvironment != QByteArrayLiteral("MATE") &&
|
|
desktopEnvironment != QByteArrayLiteral("XFCE") &&
|
|
desktopEnvironment != QByteArrayLiteral("LXDE"))
|
|
// Ignore X11 desktop environments
|
|
result.push_back(QString::fromLocal8Bit(desktopEnvironment.toLower()));
|
|
}
|
|
|
|
if (result.isEmpty())
|
|
result.push_back(QLatin1String(QGenericUnixTheme::name));
|
|
|
|
return result;
|
|
}
|
|
};
|
|
|
|
QWaylandIntegration::QWaylandIntegration()
|
|
#if defined(Q_OS_MACOS)
|
|
: mFontDb(new QCoreTextFontDatabaseEngineFactory<QCoreTextFontEngine>)
|
|
#else
|
|
: mFontDb(new QGenericUnixFontDatabase())
|
|
#endif
|
|
, mNativeInterface(new QWaylandNativeInterface(this))
|
|
{
|
|
initializeInputDeviceIntegration();
|
|
mDisplay.reset(new QWaylandDisplay(this));
|
|
if (!mDisplay->isInitialized()) {
|
|
mFailed = true;
|
|
return;
|
|
}
|
|
#if QT_CONFIG(clipboard)
|
|
mClipboard.reset(new QWaylandClipboard(mDisplay.data()));
|
|
#endif
|
|
#if QT_CONFIG(draganddrop)
|
|
mDrag.reset(new QWaylandDrag(mDisplay.data()));
|
|
#endif
|
|
|
|
reconfigureInputContext();
|
|
}
|
|
|
|
QWaylandIntegration::~QWaylandIntegration()
|
|
{
|
|
}
|
|
|
|
QPlatformNativeInterface * QWaylandIntegration::nativeInterface() const
|
|
{
|
|
return mNativeInterface.data();
|
|
}
|
|
|
|
bool QWaylandIntegration::hasCapability(QPlatformIntegration::Capability cap) const
|
|
{
|
|
switch (cap) {
|
|
case ThreadedPixmaps: return true;
|
|
case OpenGL:
|
|
return mDisplay->clientBufferIntegration();
|
|
case ThreadedOpenGL:
|
|
return mDisplay->clientBufferIntegration() && mDisplay->clientBufferIntegration()->supportsThreadedOpenGL();
|
|
case BufferQueueingOpenGL:
|
|
return true;
|
|
case MultipleWindows:
|
|
case NonFullScreenWindows:
|
|
return true;
|
|
case RasterGLSurface:
|
|
return true;
|
|
case WindowActivation:
|
|
return false;
|
|
default: return QPlatformIntegration::hasCapability(cap);
|
|
}
|
|
}
|
|
|
|
QPlatformWindow *QWaylandIntegration::createPlatformWindow(QWindow *window) const
|
|
{
|
|
if ((window->surfaceType() == QWindow::OpenGLSurface || window->surfaceType() == QWindow::RasterGLSurface)
|
|
&& mDisplay->clientBufferIntegration())
|
|
return mDisplay->clientBufferIntegration()->createEglWindow(window);
|
|
|
|
return new QWaylandShmWindow(window);
|
|
}
|
|
|
|
#if QT_CONFIG(opengl)
|
|
QPlatformOpenGLContext *QWaylandIntegration::createPlatformOpenGLContext(QOpenGLContext *context) const
|
|
{
|
|
if (mDisplay->clientBufferIntegration())
|
|
return mDisplay->clientBufferIntegration()->createPlatformOpenGLContext(context->format(), context->shareHandle());
|
|
return nullptr;
|
|
}
|
|
#endif // opengl
|
|
|
|
QPlatformBackingStore *QWaylandIntegration::createPlatformBackingStore(QWindow *window) const
|
|
{
|
|
return new QWaylandShmBackingStore(window);
|
|
}
|
|
|
|
QAbstractEventDispatcher *QWaylandIntegration::createEventDispatcher() const
|
|
{
|
|
return createUnixEventDispatcher();
|
|
}
|
|
|
|
void QWaylandIntegration::initialize()
|
|
{
|
|
QAbstractEventDispatcher *dispatcher = QGuiApplicationPrivate::eventDispatcher;
|
|
QObject::connect(dispatcher, SIGNAL(aboutToBlock()), mDisplay.data(), SLOT(flushRequests()));
|
|
QObject::connect(dispatcher, SIGNAL(awake()), mDisplay.data(), SLOT(flushRequests()));
|
|
|
|
int fd = wl_display_get_fd(mDisplay->wl_display());
|
|
QSocketNotifier *sn = new QSocketNotifier(fd, QSocketNotifier::Read, mDisplay.data());
|
|
QObject::connect(sn, SIGNAL(activated(int)), mDisplay.data(), SLOT(flushRequests()));
|
|
|
|
if (mDisplay->screens().isEmpty()) {
|
|
qWarning() << "Running on a compositor with no screens is not supported";
|
|
::exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
QPlatformFontDatabase *QWaylandIntegration::fontDatabase() const
|
|
{
|
|
return mFontDb.data();
|
|
}
|
|
|
|
#if QT_CONFIG(clipboard)
|
|
QPlatformClipboard *QWaylandIntegration::clipboard() const
|
|
{
|
|
return mClipboard.data();
|
|
}
|
|
#endif
|
|
|
|
#if QT_CONFIG(draganddrop)
|
|
QPlatformDrag *QWaylandIntegration::drag() const
|
|
{
|
|
return mDrag.data();
|
|
}
|
|
#endif // draganddrop
|
|
|
|
QPlatformInputContext *QWaylandIntegration::inputContext() const
|
|
{
|
|
return mInputContext.data();
|
|
}
|
|
|
|
QVariant QWaylandIntegration::styleHint(StyleHint hint) const
|
|
{
|
|
if (hint == ShowIsFullScreen && mDisplay->windowManagerIntegration())
|
|
return mDisplay->windowManagerIntegration()->showIsFullScreen();
|
|
|
|
switch (hint) {
|
|
case QPlatformIntegration::FontSmoothingGamma:
|
|
return qreal(1.0);
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return QPlatformIntegration::styleHint(hint);
|
|
}
|
|
|
|
#if QT_CONFIG(accessibility)
|
|
QPlatformAccessibility *QWaylandIntegration::accessibility() const
|
|
{
|
|
if (!mAccessibility) {
|
|
#ifndef QT_NO_ACCESSIBILITY_ATSPI_BRIDGE
|
|
Q_ASSERT_X(QCoreApplication::eventDispatcher(), "QXcbIntegration",
|
|
"Initializing accessibility without event-dispatcher!");
|
|
mAccessibility.reset(new QSpiAccessibleBridge());
|
|
#else
|
|
mAccessibility.reset(new QPlatformAccessibility());
|
|
#endif
|
|
}
|
|
return mAccessibility.data();
|
|
}
|
|
#endif
|
|
|
|
QPlatformServices *QWaylandIntegration::services() const
|
|
{
|
|
return mDisplay->windowManagerIntegration();
|
|
}
|
|
|
|
QWaylandDisplay *QWaylandIntegration::display() const
|
|
{
|
|
return mDisplay.data();
|
|
}
|
|
|
|
QList<int> QWaylandIntegration::possibleKeys(const QKeyEvent *event) const
|
|
{
|
|
if (auto *seat = mDisplay->currentInputDevice())
|
|
return seat->possibleKeys(event);
|
|
return {};
|
|
}
|
|
|
|
QStringList QWaylandIntegration::themeNames() const
|
|
{
|
|
return GenericWaylandTheme::themeNames();
|
|
}
|
|
|
|
QPlatformTheme *QWaylandIntegration::createPlatformTheme(const QString &name) const
|
|
{
|
|
return GenericWaylandTheme::createUnixTheme(name);
|
|
}
|
|
|
|
QWaylandClientBufferIntegration *QWaylandIntegration::clientBufferIntegration() const
|
|
{
|
|
if (!mClientBufferIntegrationInitialized)
|
|
const_cast<QWaylandIntegration *>(this)->initializeClientBufferIntegration();
|
|
|
|
return mClientBufferIntegration && mClientBufferIntegration->isValid() ? mClientBufferIntegration.data() : nullptr;
|
|
}
|
|
|
|
QWaylandServerBufferIntegration *QWaylandIntegration::serverBufferIntegration() const
|
|
{
|
|
if (!mServerBufferIntegrationInitialized)
|
|
const_cast<QWaylandIntegration *>(this)->initializeServerBufferIntegration();
|
|
|
|
return mServerBufferIntegration.data();
|
|
}
|
|
|
|
QWaylandShellIntegration *QWaylandIntegration::shellIntegration() const
|
|
{
|
|
if (!mShellIntegrationInitialized)
|
|
const_cast<QWaylandIntegration *>(this)->initializeShellIntegration();
|
|
|
|
return mShellIntegration.data();
|
|
}
|
|
|
|
void QWaylandIntegration::initializeClientBufferIntegration()
|
|
{
|
|
mClientBufferIntegrationInitialized = true;
|
|
|
|
QString targetKey = QString::fromLocal8Bit(qgetenv("QT_WAYLAND_CLIENT_BUFFER_INTEGRATION"));
|
|
|
|
if (targetKey.isEmpty()) {
|
|
if (mDisplay->hardwareIntegration()
|
|
&& mDisplay->hardwareIntegration()->clientBufferIntegration() != QLatin1String("wayland-eglstream-controller")
|
|
&& mDisplay->hardwareIntegration()->clientBufferIntegration() != QLatin1String("linux-dmabuf-unstable-v1")) {
|
|
targetKey = mDisplay->hardwareIntegration()->clientBufferIntegration();
|
|
} else {
|
|
targetKey = QLatin1Literal("wayland-egl");
|
|
}
|
|
}
|
|
|
|
if (targetKey.isEmpty()) {
|
|
qWarning("Failed to determine what client buffer integration to use");
|
|
return;
|
|
}
|
|
|
|
QStringList keys = QWaylandClientBufferIntegrationFactory::keys();
|
|
qCDebug(lcQpaWayland) << "Available client buffer integrations:" << keys;
|
|
|
|
if (keys.contains(targetKey))
|
|
mClientBufferIntegration.reset(QWaylandClientBufferIntegrationFactory::create(targetKey, QStringList()));
|
|
|
|
if (mClientBufferIntegration) {
|
|
qCDebug(lcQpaWayland) << "Initializing client buffer integration" << targetKey;
|
|
mClientBufferIntegration->initialize(mDisplay.data());
|
|
} else {
|
|
qCWarning(lcQpaWayland) << "Failed to load client buffer integration:" << targetKey;
|
|
qCWarning(lcQpaWayland) << "Available client buffer integrations:" << keys;
|
|
}
|
|
}
|
|
|
|
void QWaylandIntegration::initializeServerBufferIntegration()
|
|
{
|
|
mServerBufferIntegrationInitialized = true;
|
|
|
|
QString targetKey = QString::fromLocal8Bit(qgetenv("QT_WAYLAND_SERVER_BUFFER_INTEGRATION"));
|
|
|
|
if (targetKey.isEmpty() && mDisplay->hardwareIntegration())
|
|
targetKey = mDisplay->hardwareIntegration()->serverBufferIntegration();
|
|
|
|
if (targetKey.isEmpty()) {
|
|
qWarning("Failed to determine what server buffer integration to use");
|
|
return;
|
|
}
|
|
|
|
QStringList keys = QWaylandServerBufferIntegrationFactory::keys();
|
|
qCDebug(lcQpaWayland) << "Available server buffer integrations:" << keys;
|
|
|
|
if (keys.contains(targetKey))
|
|
mServerBufferIntegration.reset(QWaylandServerBufferIntegrationFactory::create(targetKey, QStringList()));
|
|
|
|
if (mServerBufferIntegration) {
|
|
qCDebug(lcQpaWayland) << "Initializing server buffer integration" << targetKey;
|
|
mServerBufferIntegration->initialize(mDisplay.data());
|
|
} else {
|
|
qCWarning(lcQpaWayland) << "Failed to load server buffer integration: " << targetKey;
|
|
qCWarning(lcQpaWayland) << "Available server buffer integrations:" << keys;
|
|
}
|
|
}
|
|
|
|
void QWaylandIntegration::initializeShellIntegration()
|
|
{
|
|
mShellIntegrationInitialized = true;
|
|
|
|
QByteArray integrationNames = qgetenv("QT_WAYLAND_SHELL_INTEGRATION");
|
|
QString targetKeys = QString::fromLocal8Bit(integrationNames);
|
|
|
|
QStringList preferredShells;
|
|
if (!targetKeys.isEmpty()) {
|
|
preferredShells = targetKeys.split(QLatin1Char(';'));
|
|
} else {
|
|
preferredShells << QLatin1String("xdg-shell");
|
|
preferredShells << QLatin1String("xdg-shell-v6");
|
|
QString useXdgShell = QString::fromLocal8Bit(qgetenv("QT_WAYLAND_USE_XDG_SHELL"));
|
|
if (!useXdgShell.isEmpty() && useXdgShell != QLatin1String("0")) {
|
|
qWarning() << "QT_WAYLAND_USE_XDG_SHELL is deprecated, "
|
|
"please specify the shell using QT_WAYLAND_SHELL_INTEGRATION instead";
|
|
preferredShells << QLatin1String("xdg-shell-v5");
|
|
}
|
|
preferredShells << QLatin1String("wl-shell") << QLatin1String("ivi-shell");
|
|
}
|
|
|
|
Q_FOREACH (QString preferredShell, preferredShells) {
|
|
mShellIntegration.reset(createShellIntegration(preferredShell));
|
|
if (mShellIntegration) {
|
|
qCDebug(lcQpaWayland, "Using the '%s' shell integration", qPrintable(preferredShell));
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!mShellIntegration) {
|
|
qCWarning(lcQpaWayland) << "Loading shell integration failed.";
|
|
qCWarning(lcQpaWayland) << "Attempted to load the following shells" << preferredShells;
|
|
}
|
|
}
|
|
|
|
QWaylandInputDevice *QWaylandIntegration::createInputDevice(QWaylandDisplay *display, int version, uint32_t id)
|
|
{
|
|
if (mInputDeviceIntegration) {
|
|
return mInputDeviceIntegration->createInputDevice(display, version, id);
|
|
}
|
|
return new QWaylandInputDevice(display, version, id);
|
|
}
|
|
|
|
void QWaylandIntegration::initializeInputDeviceIntegration()
|
|
{
|
|
QByteArray integrationName = qgetenv("QT_WAYLAND_INPUTDEVICE_INTEGRATION");
|
|
QString targetKey = QString::fromLocal8Bit(integrationName);
|
|
|
|
if (targetKey.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
QStringList keys = QWaylandInputDeviceIntegrationFactory::keys();
|
|
if (keys.contains(targetKey)) {
|
|
mInputDeviceIntegration.reset(QWaylandInputDeviceIntegrationFactory::create(targetKey, QStringList()));
|
|
qDebug("Using the '%s' input device integration", qPrintable(targetKey));
|
|
} else {
|
|
qWarning("Wayland inputdevice integration '%s' not found, using default", qPrintable(targetKey));
|
|
}
|
|
}
|
|
|
|
void QWaylandIntegration::reconfigureInputContext()
|
|
{
|
|
if (!mDisplay) {
|
|
// This function can be called from QWaylandDisplay::registry_global() when we
|
|
// are in process of constructing QWaylandDisplay. Configuring input context
|
|
// in that case is done by calling reconfigureInputContext() from QWaylandIntegration
|
|
// constructor, after QWaylandDisplay has been constructed.
|
|
return;
|
|
}
|
|
|
|
const QString &requested = QPlatformInputContextFactory::requested();
|
|
if (requested == QLatin1String("qtvirtualkeyboard"))
|
|
qCWarning(lcQpaWayland) << "qtvirtualkeyboard currently is not supported at client-side,"
|
|
" use QT_IM_MODULE=qtvirtualkeyboard at compositor-side.";
|
|
|
|
if (requested.isNull())
|
|
mInputContext.reset(new QWaylandInputContext(mDisplay.data()));
|
|
else
|
|
mInputContext.reset(QPlatformInputContextFactory::create(requested));
|
|
|
|
const QString defaultInputContext(QStringLiteral("compose"));
|
|
if ((!mInputContext || !mInputContext->isValid()) && requested != defaultInputContext)
|
|
mInputContext.reset(QPlatformInputContextFactory::create(defaultInputContext));
|
|
|
|
#if QT_CONFIG(xkbcommon)
|
|
QXkbCommon::setXkbContext(mInputContext.data(), mDisplay->xkbContext());
|
|
#endif
|
|
|
|
// Even if compositor-side input context handling has been requested, we fallback to
|
|
// client-side handling if compositor does not provide the text-input extension. This
|
|
// is why we need to check here which input context actually is being used.
|
|
mDisplay->mUsingInputContextFromCompositor = qobject_cast<QWaylandInputContext *>(mInputContext.data());
|
|
|
|
qCDebug(lcQpaWayland) << "using input method:" << inputContext()->metaObject()->className();
|
|
}
|
|
|
|
QWaylandShellIntegration *QWaylandIntegration::createShellIntegration(const QString &integrationName)
|
|
{
|
|
if (QWaylandShellIntegrationFactory::keys().contains(integrationName)) {
|
|
return QWaylandShellIntegrationFactory::create(integrationName, mDisplay.data());
|
|
} else {
|
|
qCWarning(lcQpaWayland) << "No shell integration named" << integrationName << "found";
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
QT_END_NAMESPACE
|