summaryrefslogtreecommitdiffstats
path: root/src/client/qwaylanddisplay.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/qwaylanddisplay.cpp')
-rw-r--r--src/client/qwaylanddisplay.cpp907
1 files changed, 647 insertions, 260 deletions
diff --git a/src/client/qwaylanddisplay.cpp b/src/client/qwaylanddisplay.cpp
index e759c5d64..e6ed0772f 100644
--- a/src/client/qwaylanddisplay.cpp
+++ b/src/client/qwaylanddisplay.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company 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$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qwaylanddisplay_p.h"
@@ -60,6 +24,9 @@
#include <wayland-cursor.h>
#endif
#include "qwaylandhardwareintegration_p.h"
+#include "qwaylandtextinputv1_p.h"
+#include "qwaylandtextinputv2_p.h"
+#include "qwaylandtextinputv3_p.h"
#include "qwaylandinputcontext_p.h"
#include "qwaylandinputmethodcontext_p.h"
@@ -68,14 +35,23 @@
#include "qwaylandclientbufferintegration_p.h"
#include "qwaylandextendedsurface_p.h"
+#include "qwaylandpointergestures_p.h"
#include "qwaylandsubsurface_p.h"
#include "qwaylandtouch_p.h"
+#if QT_CONFIG(tabletevent)
#include "qwaylandtabletv2_p.h"
+#endif
#include "qwaylandqtkey_p.h"
+#include <QtWaylandClient/private/qwayland-text-input-unstable-v1.h>
#include <QtWaylandClient/private/qwayland-text-input-unstable-v2.h>
+#include <QtWaylandClient/private/qwayland-text-input-unstable-v3.h>
#include <QtWaylandClient/private/qwayland-wp-primary-selection-unstable-v1.h>
#include <QtWaylandClient/private/qwayland-qt-text-input-method-unstable-v1.h>
+#include <QtWaylandClient/private/qwayland-fractional-scale-v1.h>
+#include <QtWaylandClient/private/qwayland-viewporter.h>
+#include <QtWaylandClient/private/qwayland-cursor-shape-v1.h>
+#include <QtWaylandClient/private/qwayland-xdg-toplevel-drag-v1.h>
#include <QtCore/private/qcore_unix_p.h>
@@ -93,18 +69,205 @@ QT_BEGIN_NAMESPACE
namespace QtWaylandClient {
+class EventThread : public QThread
+{
+ Q_OBJECT
+public:
+ enum OperatingMode {
+ EmitToDispatch, // Emit the signal, allow dispatching in a differnt thread.
+ SelfDispatch, // Dispatch the events inside this thread.
+ };
+
+ EventThread(struct wl_display * wl, struct wl_event_queue * ev_queue,
+ OperatingMode mode)
+ : m_fd(wl_display_get_fd(wl))
+ , m_pipefd{ -1, -1 }
+ , m_wldisplay(wl)
+ , m_wlevqueue(ev_queue)
+ , m_mode(mode)
+ , m_reading(true)
+ , m_quitting(false)
+ {
+ setObjectName(QStringLiteral("WaylandEventThread"));
+ }
+
+ void readAndDispatchEvents()
+ {
+ /*
+ * Dispatch pending events and flush the requests at least once. If the event thread
+ * is not reading, try to call _prepare_read() to allow the event thread to poll().
+ * If that fails, re-try dispatch & flush again until _prepare_read() is successful.
+ *
+ * This allow any call to readAndDispatchEvents() to start event thread's polling,
+ * not only the one issued from event thread's waitForReading(), which means functions
+ * called from dispatch_pending() can safely spin an event loop.
+ */
+ if (m_quitting)
+ return;
+
+ for (;;) {
+ if (dispatchQueuePending() < 0) {
+ Q_EMIT waylandError();
+ m_quitting = true;
+ return;
+ }
+
+ wl_display_flush(m_wldisplay);
+
+ // We have to check if event thread is reading every time we dispatch
+ // something, as that may recursively call this function.
+ if (m_reading.loadAcquire())
+ break;
+
+ if (prepareReadQueue() == 0) {
+ QMutexLocker l(&m_mutex);
+ m_reading.storeRelease(true);
+ m_cond.wakeOne();
+ break;
+ }
+ }
+ }
+
+ void stop()
+ {
+ // We have to both write to the pipe and set the flag, as the thread may be
+ // either in the poll() or waiting for _prepare_read().
+ if (m_pipefd[1] != -1 && write(m_pipefd[1], "\0", 1) == -1)
+ qWarning("Failed to write to the pipe: %s.", strerror(errno));
+
+ {
+ QMutexLocker l(&m_mutex);
+ m_quitting = true;
+ m_cond.wakeOne();
+ }
+
+ wait();
+ }
+
+Q_SIGNALS:
+ void needReadAndDispatch();
+ void waylandError();
+
+protected:
+ void run() override
+ {
+ // we use this pipe to make the loop exit otherwise if we simply used a flag on the loop condition, if stop() gets
+ // called while poll() is blocking the thread will never quit since there are no wayland messages coming anymore.
+ struct Pipe
+ {
+ Pipe(int *fds)
+ : fds(fds)
+ {
+ if (qt_safe_pipe(fds) != 0)
+ qWarning("Pipe creation failed. Quitting may hang.");
+ }
+ ~Pipe()
+ {
+ if (fds[0] != -1) {
+ close(fds[0]);
+ close(fds[1]);
+ }
+ }
+
+ int *fds;
+ } pipe(m_pipefd);
+
+ // Make the main thread call wl_prepare_read(), dispatch the pending messages and flush the
+ // outbound ones. Wait until it's done before proceeding, unless we're told to quit.
+ while (waitForReading()) {
+ if (!m_reading.loadRelaxed())
+ break;
+
+ pollfd fds[2] = { { m_fd, POLLIN, 0 }, { m_pipefd[0], POLLIN, 0 } };
+ poll(fds, 2, -1);
+
+ if (fds[1].revents & POLLIN) {
+ // we don't really care to read the byte that was written here since we're closing down
+ wl_display_cancel_read(m_wldisplay);
+ break;
+ }
+
+ if (fds[0].revents & POLLIN)
+ wl_display_read_events(m_wldisplay);
+ // The polll was succesfull and the event thread did the wl_display_read_events(). On the next iteration of the loop
+ // the event sent to the main thread will cause it to dispatch the messages just read, unless the loop exits in which
+ // case we don't care anymore about them.
+ else
+ wl_display_cancel_read(m_wldisplay);
+ }
+ }
+
+private:
+ bool waitForReading()
+ {
+ Q_ASSERT(QThread::currentThread() == this);
+
+ m_reading.storeRelease(false);
+
+ if (m_mode == SelfDispatch) {
+ readAndDispatchEvents();
+ } else {
+ Q_EMIT needReadAndDispatch();
+
+ QMutexLocker lock(&m_mutex);
+ // m_reading might be set from our emit or some other invocation of
+ // readAndDispatchEvents().
+ while (!m_reading.loadRelaxed() && !m_quitting)
+ m_cond.wait(&m_mutex);
+ }
+
+ return !m_quitting;
+ }
+
+ int dispatchQueuePending()
+ {
+ if (m_wlevqueue)
+ return wl_display_dispatch_queue_pending(m_wldisplay, m_wlevqueue);
+ else
+ return wl_display_dispatch_pending(m_wldisplay);
+ }
+
+ int prepareReadQueue()
+ {
+ if (m_wlevqueue)
+ return wl_display_prepare_read_queue(m_wldisplay, m_wlevqueue);
+ else
+ return wl_display_prepare_read(m_wldisplay);
+ }
+
+ int m_fd;
+ int m_pipefd[2];
+ wl_display *m_wldisplay;
+ wl_event_queue *m_wlevqueue;
+ OperatingMode m_mode;
+
+ /* Concurrency note when operating in EmitToDispatch mode:
+ * m_reading is set to false inside event thread's waitForReading(), and is
+ * set to true inside main thread's readAndDispatchEvents().
+ * The lock is not taken when setting m_reading to false, as the main thread
+ * is not actively waiting for it to turn false. However, the lock is taken
+ * inside readAndDispatchEvents() before setting m_reading to true,
+ * as the event thread is actively waiting for it under the wait condition.
+ */
+
+ QAtomicInteger<bool> m_reading;
+ bool m_quitting;
+ QMutex m_mutex;
+ QWaitCondition m_cond;
+};
+
Q_LOGGING_CATEGORY(lcQpaWayland, "qt.qpa.wayland"); // for general (uncategorized) Wayland platform logging
struct wl_surface *QWaylandDisplay::createSurface(void *handle)
{
- struct wl_surface *surface = mCompositor.create_surface();
+ struct wl_surface *surface = mGlobals.compositor->create_surface();
wl_surface_set_user_data(surface, handle);
return surface;
}
struct ::wl_region *QWaylandDisplay::createRegion(const QRegion &qregion)
{
- struct ::wl_region *region = mCompositor.create_region();
+ struct ::wl_region *region = mGlobals.compositor->create_region();
for (const QRect &rect : qregion)
wl_region_add(region, rect.x(), rect.y(), rect.width(), rect.height());
@@ -114,7 +277,7 @@ struct ::wl_region *QWaylandDisplay::createRegion(const QRegion &qregion)
::wl_subsurface *QWaylandDisplay::createSubSurface(QWaylandWindow *window, QWaylandWindow *parent)
{
- if (!mSubCompositor) {
+ if (!mGlobals.subCompositor) {
qCWarning(lcQpaWayland) << "Can't create subsurface, not supported by the compositor.";
return nullptr;
}
@@ -123,7 +286,18 @@ struct ::wl_region *QWaylandDisplay::createRegion(const QRegion &qregion)
Q_ASSERT(parent->wlSurface());
Q_ASSERT(window->wlSurface());
- return mSubCompositor->get_subsurface(window->wlSurface(), parent->wlSurface());
+ return mGlobals.subCompositor->get_subsurface(window->wlSurface(), parent->wlSurface());
+}
+
+::wp_viewport *QWaylandDisplay::createViewport(QWaylandWindow *window)
+{
+ if (!mGlobals.viewporter) {
+ qCWarning(lcQpaWayland) << "Can't create wp_viewport, not supported by the compositor.";
+ return nullptr;
+ }
+
+ Q_ASSERT(window->wlSurface());
+ return mGlobals.viewporter->get_viewport(window->wlSurface());
}
QWaylandShellIntegration *QWaylandDisplay::shellIntegration() const
@@ -138,7 +312,7 @@ QWaylandClientBufferIntegration * QWaylandDisplay::clientBufferIntegration() con
QWaylandWindowManagerIntegration *QWaylandDisplay::windowManagerIntegration() const
{
- return mWindowManagerIntegration.data();
+ return mGlobals.windowManagerIntegration.get();
}
QWaylandDisplay::QWaylandDisplay(QWaylandIntegration *waylandIntegration)
@@ -147,52 +321,85 @@ QWaylandDisplay::QWaylandDisplay(QWaylandIntegration *waylandIntegration)
qRegisterMetaType<uint32_t>("uint32_t");
mDisplay = wl_display_connect(nullptr);
- if (!mDisplay) {
+ if (mDisplay) {
+ setupConnection();
+ } else {
qErrnoWarning(errno, "Failed to create wl_display");
- return;
}
+ mWaylandTryReconnect = qEnvironmentVariableIsSet("QT_WAYLAND_RECONNECT");
+}
+
+void QWaylandDisplay::setupConnection()
+{
struct ::wl_registry *registry = wl_display_get_registry(mDisplay);
init(registry);
- mWindowManagerIntegration.reset(new QWaylandWindowManagerIntegration(this));
-
#if QT_CONFIG(xkbcommon)
mXkbContext.reset(xkb_context_new(XKB_CONTEXT_NO_FLAGS));
if (!mXkbContext)
qCWarning(lcQpaWayland, "failed to create xkb context");
#endif
-
- forceRoundTrip();
-
- if (!mWaitingScreens.isEmpty()) {
- // Give wl_output.done and zxdg_output_v1.done events a chance to arrive
- forceRoundTrip();
- }
+ if (mWaylandInputContextRequested)
+ checkTextInputProtocol();
}
QWaylandDisplay::~QWaylandDisplay(void)
{
+ if (m_eventThread)
+ m_eventThread->stop();
+
+ if (m_frameEventQueueThread)
+ m_frameEventQueueThread->stop();
+
if (mSyncCallback)
wl_callback_destroy(mSyncCallback);
- qDeleteAll(qExchange(mInputDevices, {}));
+ qDeleteAll(std::exchange(mInputDevices, {}));
- for (QWaylandScreen *screen : qExchange(mScreens, {})) {
+ for (QWaylandScreen *screen : std::exchange(mScreens, {})) {
QWindowSystemInterface::handleScreenRemoved(screen);
}
qDeleteAll(mWaitingScreens);
-#if QT_CONFIG(wayland_datadevice)
- delete mDndSelectionHandler.take();
-#endif
#if QT_CONFIG(cursor)
mCursorThemes.clear();
#endif
+
+ if (m_frameEventQueue)
+ wl_event_queue_destroy(m_frameEventQueue);
+
+ // Reset the globals manually since they need to be destroyed before the wl_display
+ mGlobals = {};
+
+ if (object())
+ wl_registry_destroy(object());
+
if (mDisplay)
wl_display_disconnect(mDisplay);
}
+// Steps which is called just after constructor. This separates registry_global() out of the constructor
+// so that factory functions in integration can be overridden.
+bool QWaylandDisplay::initialize()
+{
+ if (!isInitialized())
+ return false;
+
+ forceRoundTrip();
+
+ emit connected();
+
+ if (!mWaitingScreens.isEmpty()) {
+ // Give wl_output.done and zxdg_output_v1.done events a chance to arrive
+ forceRoundTrip();
+ }
+ if (mWaylandInputContextRequested)
+ mTextInputManagerIndex = INT_MAX;
+
+ return qEnvironmentVariableIntValue("QT_WAYLAND_DONT_CHECK_SHELL_INTEGRATION") || shellIntegration();
+}
+
void QWaylandDisplay::ensureScreen()
{
if (!mScreens.empty() || mPlaceholderScreen)
@@ -205,104 +412,183 @@ void QWaylandDisplay::ensureScreen()
Q_ASSERT(!QGuiApplication::screens().empty());
}
-void QWaylandDisplay::checkError() const
+void QWaylandDisplay::reconnect()
{
- int ecode = wl_display_get_error(mDisplay);
- if ((ecode == EPIPE || ecode == ECONNRESET)) {
- // special case this to provide a nicer error
- qFatal("The Wayland connection broke. Did the Wayland compositor die?");
- } else {
- qFatal("The Wayland connection experienced a fatal error: %s", strerror(ecode));
+ qCWarning(lcQpaWayland) << "Attempting wayland reconnect";
+ m_eventThread->stop();
+ m_frameEventQueueThread->stop();
+ m_eventThread->wait();
+ m_frameEventQueueThread->wait();
+
+ qDeleteAll(mWaitingScreens);
+ mWaitingScreens.clear();
+
+ while (!mScreens.isEmpty()) {
+ auto screen = mScreens.takeLast();
+ ensureScreen();
+ QWindowSystemInterface::handleScreenRemoved(screen);
}
-}
-void QWaylandDisplay::flushRequests()
-{
- if (wl_display_prepare_read(mDisplay) == 0) {
- wl_display_read_events(mDisplay);
+ mCursorThemes.clear();
+ mCursor.reset();
+
+ mGlobals = GlobalHolder();
+
+ mWaylandIntegration->reset();
+
+ qDeleteAll(std::exchange(mInputDevices, {}));
+ mLastInputDevice = nullptr;
+
+ for (const RegistryGlobal &global : mRegistryGlobals) {
+ emit globalRemoved(global);
}
+ mRegistryGlobals.clear();
- if (wl_display_dispatch_pending(mDisplay) < 0)
- checkError();
+ mLastInputSerial = 0;
+ mLastInputWindow.clear();
+ mLastKeyboardFocus.clear();
+ mActiveWindows.clear();
- {
- QReadLocker locker(&m_frameQueueLock);
- for (const FrameQueue &q : mExternalQueues) {
- QMutexLocker locker(q.mutex);
- while (wl_display_prepare_read_queue(mDisplay, q.queue) != 0)
- wl_display_dispatch_queue_pending(mDisplay, q.queue);
- wl_display_read_events(mDisplay);
- wl_display_dispatch_queue_pending(mDisplay, q.queue);
+ const auto windows = QGuiApplication::allWindows();
+ for (auto window : windows) {
+ if (auto waylandWindow = static_cast<QWaylandWindow *>(window->handle()))
+ waylandWindow->closeChildPopups();
+ }
+ // Remove windows that do not need to be recreated and now closed popups
+ QList<QWaylandWindow *> recreateWindows;
+ for (auto window : std::as_const(windows)) {
+ auto waylandWindow = static_cast<QWaylandWindow*>(window->handle());
+ if (waylandWindow && waylandWindow->wlSurface()) {
+ waylandWindow->reset();
+ recreateWindows.push_back(waylandWindow);
}
}
- wl_display_flush(mDisplay);
-}
+ if (mSyncCallback) {
+ wl_callback_destroy(mSyncCallback);
+ mSyncCallback = nullptr;
+ }
-void QWaylandDisplay::blockingReadEvents()
-{
- if (wl_display_dispatch(mDisplay) < 0)
- checkError();
+ if (object())
+ wl_registry_destroy(object());
+ mDisplay = wl_display_connect(nullptr);
+ if (!mDisplay)
+ _exit(1);
+
+ setupConnection();
+ initialize();
+
+ if (m_frameEventQueue)
+ wl_event_queue_destroy(m_frameEventQueue);
+ initEventThread();
+
+ auto needsRecreate = [](QPlatformWindow *window) {
+ return window && !static_cast<QWaylandWindow *>(window)->wlSurface();
+ };
+ auto window = recreateWindows.begin();
+ while (!recreateWindows.isEmpty()) {
+ if (!needsRecreate((*window)->QPlatformWindow::parent()) && !needsRecreate((*window)->transientParent())) {
+ (*window)->reinit();
+ window = recreateWindows.erase(window);
+ } else {
+ ++window;
+ }
+ if (window == recreateWindows.end())
+ window = recreateWindows.begin();
+ }
+
+ mWaylandIntegration->reconfigureInputContext();
}
-void QWaylandDisplay::destroyFrameQueue(const QWaylandDisplay::FrameQueue &q)
+void QWaylandDisplay::flushRequests()
{
- QWriteLocker locker(&m_frameQueueLock);
- auto it = std::find_if(mExternalQueues.begin(),
- mExternalQueues.end(),
- [&q] (const QWaylandDisplay::FrameQueue &other){ return other.queue == q.queue; });
- Q_ASSERT(it != mExternalQueues.end());
- mExternalQueues.erase(it);
- if (q.queue != nullptr)
- wl_event_queue_destroy(q.queue);
- delete q.mutex;
+ m_eventThread->readAndDispatchEvents();
}
-QWaylandDisplay::FrameQueue QWaylandDisplay::createFrameQueue()
+// We have to wait until we have an eventDispatcher before creating the eventThread,
+// otherwise forceRoundTrip() may block inside _events_read() because eventThread is
+// polling.
+void QWaylandDisplay::initEventThread()
{
- QWriteLocker locker(&m_frameQueueLock);
- FrameQueue q{createEventQueue()};
- mExternalQueues.append(q);
- return q;
+ m_eventThread.reset(
+ new EventThread(mDisplay, /* default queue */ nullptr, EventThread::EmitToDispatch));
+ connect(m_eventThread.get(), &EventThread::needReadAndDispatch, this,
+ &QWaylandDisplay::flushRequests, Qt::QueuedConnection);
+ connect(m_eventThread.get(), &EventThread::waylandError, this,
+ &QWaylandDisplay::checkWaylandError, Qt::QueuedConnection);
+ m_eventThread->start();
+
+ // wl_display_disconnect() free this.
+ m_frameEventQueue = wl_display_create_queue(mDisplay);
+ m_frameEventQueueThread.reset(
+ new EventThread(mDisplay, m_frameEventQueue, EventThread::SelfDispatch));
+ m_frameEventQueueThread->start();
}
-wl_event_queue *QWaylandDisplay::createEventQueue()
+void QWaylandDisplay::checkWaylandError()
{
- return wl_display_create_queue(mDisplay);
+ int ecode = wl_display_get_error(mDisplay);
+ if ((ecode == EPIPE || ecode == ECONNRESET)) {
+ qWarning("The Wayland connection broke. Did the Wayland compositor die?");
+ if (mWaylandTryReconnect) {
+ reconnect();
+ return;
+ }
+ } else {
+ qWarning("The Wayland connection experienced a fatal error: %s", strerror(ecode));
+ }
+ _exit(-1);
}
-void QWaylandDisplay::dispatchQueueWhile(wl_event_queue *queue, std::function<bool ()> condition, int timeout)
+void QWaylandDisplay::blockingReadEvents()
{
- if (!condition())
- return;
-
- QElapsedTimer timer;
- timer.start();
- struct pollfd pFd = qt_make_pollfd(wl_display_get_fd(mDisplay), POLLIN);
- while (timeout == -1 || timer.elapsed() < timeout) {
- while (wl_display_prepare_read_queue(mDisplay, queue) != 0)
- wl_display_dispatch_queue_pending(mDisplay, queue);
-
- wl_display_flush(mDisplay);
-
- const int remaining = qMax(timeout - timer.elapsed(), 0ll);
- const int pollTimeout = timeout == -1 ? -1 : remaining;
- if (qt_poll_msecs(&pFd, 1, pollTimeout) > 0)
- wl_display_read_events(mDisplay);
+ if (wl_display_dispatch(mDisplay) < 0) {
+ int ecode = wl_display_get_error(mDisplay);
+ if ((ecode == EPIPE || ecode == ECONNRESET))
+ qWarning("The Wayland connection broke during blocking read event. Did the Wayland compositor die?");
else
- wl_display_cancel_read(mDisplay);
-
- if (wl_display_dispatch_queue_pending(mDisplay, queue) < 0)
- checkError();
+ qWarning("The Wayland connection experienced a fatal error during blocking read event: %s", strerror(ecode));
+ _exit(-1);
+ }
+}
- if (!condition())
- break;
+void QWaylandDisplay::checkTextInputProtocol()
+{
+ QStringList tips, timps; // for text input protocols and text input manager protocols
+ // zwp_text_input_v2 is preferred over zwp_text_input_v3 because:
+ // - Currently, v3 is not as feature rich as v2.
+ // - While v2 is not upstreamed, it is well supported by KWin since Plasma 5 and Plasma
+ // Mobile uses some v2 only.
+ tips << QLatin1String(QtWayland::qt_text_input_method_v1::interface()->name)
+ << QLatin1String(QtWayland::zwp_text_input_v2::interface()->name)
+ << QLatin1String(QtWayland::zwp_text_input_v3::interface()->name)
+ << QLatin1String(QtWayland::zwp_text_input_v1::interface()->name);
+ timps << QLatin1String(QtWayland::qt_text_input_method_manager_v1::interface()->name)
+ << QLatin1String(QtWayland::zwp_text_input_manager_v2::interface()->name)
+ << QLatin1String(QtWayland::zwp_text_input_manager_v3::interface()->name)
+ << QLatin1String(QtWayland::zwp_text_input_manager_v1::interface()->name);
+
+ QString tiProtocols = QString::fromLocal8Bit(qgetenv("QT_WAYLAND_TEXT_INPUT_PROTOCOL"));
+ qCDebug(lcQpaWayland) << "QT_WAYLAND_TEXT_INPUT_PROTOCOL=" << tiProtocols;
+ QStringList keys;
+ if (!tiProtocols.isEmpty()) {
+ keys = tiProtocols.split(QLatin1Char(';'));
+ QList<QString>::iterator it = keys.begin();
+ while (it != keys.end()) {
+ if (tips.contains(*it))
+ mTextInputManagerList.append(timps.at(tips.indexOf(*it)));
+ else
+ qCDebug(lcQpaWayland) << "text input: unknown protocol - " << *it;
+ ++it;
+ }
}
+ if (mTextInputManagerList.isEmpty()) // fallback
+ mTextInputManagerList = timps;
}
QWaylandScreen *QWaylandDisplay::screenForOutput(struct wl_output *output) const
{
- for (auto screen : qAsConst(mScreens)) {
+ for (auto screen : std::as_const(mScreens)) {
if (screen->output() == output)
return screen;
}
@@ -322,83 +608,183 @@ void QWaylandDisplay::handleScreenInitialized(QWaylandScreen *screen)
}
}
-void QWaylandDisplay::waitForScreens()
+template <typename T, auto f>
+struct WithDestructor : public T
{
- flushRequests();
-
- while (true) {
- bool screensReady = !mScreens.isEmpty();
-
- for (int ii = 0; screensReady && ii < mScreens.count(); ++ii) {
- if (mScreens.at(ii)->geometry() == QRect(0, 0, 0, 0))
- screensReady = false;
- }
-
- if (!screensReady)
- blockingReadEvents();
- else
- return;
+ using T::T;
+ ~WithDestructor()
+ {
+ f(this->object());
}
-}
+};
void QWaylandDisplay::registry_global(uint32_t id, const QString &interface, uint32_t version)
{
struct ::wl_registry *registry = object();
- if (interface == QStringLiteral("wl_output")) {
- mWaitingScreens << new QWaylandScreen(this, version, id);
- } else if (interface == QStringLiteral("wl_compositor")) {
- mCompositorVersion = qMin((int)version, 4);
- mCompositor.init(registry, id, mCompositorVersion);
- } else if (interface == QStringLiteral("wl_shm")) {
- mShm.reset(new QWaylandShm(this, version, id));
- } else if (interface == QStringLiteral("wl_seat")) {
+ static QStringList interfaceBlacklist = qEnvironmentVariable("QT_WAYLAND_DISABLED_INTERFACES").split(u',');
+ if (interfaceBlacklist.contains(interface)) {
+ return;
+ }
+
+ if (interface == QLatin1String(QtWayland::wl_output::interface()->name)) {
+ mWaitingScreens << mWaylandIntegration->createPlatformScreen(this, version, id);
+ } else if (interface == QLatin1String(QtWayland::wl_compositor::interface()->name)) {
+ mGlobals.compositor.reset(
+ new WithDestructor<QtWayland::wl_compositor, wl_compositor_destroy>(
+ registry, id, qMin((int)version, 6)));
+ } else if (interface == QLatin1String(QWaylandShm::interface()->name)) {
+ mGlobals.shm.reset(new QWaylandShm(this, version, id));
+ } else if (interface == QLatin1String(QWaylandInputDevice::interface()->name)) {
QWaylandInputDevice *inputDevice = mWaylandIntegration->createInputDevice(this, version, id);
mInputDevices.append(inputDevice);
#if QT_CONFIG(wayland_datadevice)
- } else if (interface == QStringLiteral("wl_data_device_manager")) {
- mDndSelectionHandler.reset(new QWaylandDataDeviceManager(this, id));
+ } else if (interface == QLatin1String(QWaylandDataDeviceManager::interface()->name)) {
+ mGlobals.dndSelectionHandler.reset(new QWaylandDataDeviceManager(this, version, id));
+#endif
+ } else if (interface == QLatin1String(QtWayland::qt_surface_extension::interface()->name)) {
+ mGlobals.surfaceExtension.reset(
+ new WithDestructor<QtWayland::qt_surface_extension, qt_surface_extension_destroy>(
+ registry, id, 1));
+ } else if (interface == QLatin1String(QtWayland::wl_subcompositor::interface()->name)) {
+ mGlobals.subCompositor.reset(
+ new WithDestructor<QtWayland::wl_subcompositor, wl_subcompositor_destroy>(registry,
+ id, 1));
+ } else if (interface == QLatin1String(QWaylandTouchExtension::interface()->name)) {
+ mGlobals.touchExtension.reset(new QWaylandTouchExtension(this, id));
+ } else if (interface == QLatin1String(QWaylandQtKeyExtension::interface()->name)) {
+ mGlobals.qtKeyExtension.reset(new QWaylandQtKeyExtension(this, id));
+#if QT_CONFIG(tabletevent)
+ } else if (interface == QLatin1String(QWaylandTabletManagerV2::interface()->name)) {
+ mGlobals.tabletManager.reset(new QWaylandTabletManagerV2(this, id, qMin(1, int(version))));
#endif
- } else if (interface == QStringLiteral("qt_surface_extension")) {
- mWindowExtension.reset(new QtWayland::qt_surface_extension(registry, id, 1));
- } else if (interface == QStringLiteral("wl_subcompositor")) {
- mSubCompositor.reset(new QtWayland::wl_subcompositor(registry, id, 1));
- } else if (interface == QStringLiteral("qt_touch_extension")) {
- mTouchExtension.reset(new QWaylandTouchExtension(this, id));
- } else if (interface == QStringLiteral("zqt_key_v1")) {
- mQtKeyExtension.reset(new QWaylandQtKeyExtension(this, id));
- } else if (interface == QStringLiteral("zwp_tablet_manager_v2")) {
- mTabletManager.reset(new QWaylandTabletManagerV2(this, id, qMin(1, int(version))));
+ } else if (interface == QLatin1String(QWaylandPointerGestures::interface()->name)) {
+ mGlobals.pointerGestures.reset(new QWaylandPointerGestures(this, id, 1));
#if QT_CONFIG(wayland_client_primary_selection)
- } else if (interface == QStringLiteral("zwp_primary_selection_device_manager_v1")) {
- mPrimarySelectionManager.reset(new QWaylandPrimarySelectionDeviceManagerV1(this, id, 1));
+ } else if (interface == QLatin1String(QWaylandPrimarySelectionDeviceManagerV1::interface()->name)) {
+ mGlobals.primarySelectionManager.reset(
+ new QWaylandPrimarySelectionDeviceManagerV1(this, id, 1));
+ for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices))
+ inputDevice->setPrimarySelectionDevice(
+ mGlobals.primarySelectionManager->createDevice(inputDevice));
#endif
- } else if (interface == QStringLiteral("qt_text_input_method_manager_v1") && !mClientSideInputContextRequested) {
- mTextInputMethodManager.reset(new QtWayland::qt_text_input_method_manager_v1(registry, id, 1));
- for (QWaylandInputDevice *inputDevice : qAsConst(mInputDevices))
- inputDevice->setTextInputMethod(new QWaylandTextInputMethod(this, mTextInputMethodManager->get_text_input_method(inputDevice->wl_seat())));
+ } else if (interface == QLatin1String(QtWayland::qt_text_input_method_manager_v1::interface()->name)
+ && (mTextInputManagerList.contains(interface) && mTextInputManagerList.indexOf(interface) < mTextInputManagerIndex)) {
+ qCDebug(lcQpaWayland) << "text input: register qt_text_input_method_manager_v1";
+ if (mTextInputManagerIndex < INT_MAX) {
+ mGlobals.textInputManagerv1.reset();
+ mGlobals.textInputManagerv2.reset();
+ mGlobals.textInputManagerv3.reset();
+ for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices))
+ inputDevice->setTextInput(nullptr);
+ }
+
+ mGlobals.textInputMethodManager.reset(
+ new WithDestructor<QtWayland::qt_text_input_method_manager_v1,
+ qt_text_input_method_manager_v1_destroy>(registry, id, 1));
+ for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices))
+ inputDevice->setTextInputMethod(new QWaylandTextInputMethod(
+ this,
+ mGlobals.textInputMethodManager->get_text_input_method(
+ inputDevice->wl_seat())));
+ mWaylandIntegration->reconfigureInputContext();
+ mTextInputManagerIndex = mTextInputManagerList.indexOf(interface);
+ } else if (interface == QLatin1String(QtWayland::zwp_text_input_manager_v1::interface()->name)
+ && (mTextInputManagerList.contains(interface) && mTextInputManagerList.indexOf(interface) < mTextInputManagerIndex)) {
+ qCDebug(lcQpaWayland) << "text input: register zwp_text_input_v1";
+ if (mTextInputManagerIndex < INT_MAX) {
+ mGlobals.textInputMethodManager.reset();
+ mGlobals.textInputManagerv2.reset();
+ mGlobals.textInputManagerv3.reset();
+ for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices))
+ inputDevice->setTextInputMethod(nullptr);
+ }
+
+ mGlobals.textInputManagerv1.reset(
+ new WithDestructor<QtWayland::zwp_text_input_manager_v1,
+ zwp_text_input_manager_v1_destroy>(registry, id, 1));
+ for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices)) {
+ auto textInput =
+ new QWaylandTextInputv1(this, mGlobals.textInputManagerv1->create_text_input());
+ textInput->setSeat(inputDevice->wl_seat());
+ inputDevice->setTextInput(textInput);
+ }
+
+ mWaylandIntegration->reconfigureInputContext();
+ mTextInputManagerIndex = mTextInputManagerList.indexOf(interface);
+ } else if (interface == QLatin1String(QtWayland::zwp_text_input_manager_v2::interface()->name)
+ && (mTextInputManagerList.contains(interface) && mTextInputManagerList.indexOf(interface) < mTextInputManagerIndex)) {
+ qCDebug(lcQpaWayland) << "text input: register zwp_text_input_v2";
+ if (mTextInputManagerIndex < INT_MAX) {
+ mGlobals.textInputMethodManager.reset();
+ mGlobals.textInputManagerv1.reset();
+ mGlobals.textInputManagerv3.reset();
+ for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices))
+ inputDevice->setTextInputMethod(nullptr);
+ }
+
+ mGlobals.textInputManagerv2.reset(
+ new WithDestructor<QtWayland::zwp_text_input_manager_v2,
+ zwp_text_input_manager_v2_destroy>(registry, id, 1));
+ for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices))
+ inputDevice->setTextInput(new QWaylandTextInputv2(
+ this, mGlobals.textInputManagerv2->get_text_input(inputDevice->wl_seat())));
mWaylandIntegration->reconfigureInputContext();
- } else if (interface == QStringLiteral("zwp_text_input_manager_v2") && !mClientSideInputContextRequested) {
- mTextInputManager.reset(new QtWayland::zwp_text_input_manager_v2(registry, id, 1));
- for (QWaylandInputDevice *inputDevice : qAsConst(mInputDevices))
- inputDevice->setTextInput(new QWaylandTextInput(this, mTextInputManager->get_text_input(inputDevice->wl_seat())));
+ mTextInputManagerIndex = mTextInputManagerList.indexOf(interface);
+ } else if (interface == QLatin1String(QtWayland::zwp_text_input_manager_v3::interface()->name)
+ && (mTextInputManagerList.contains(interface) && mTextInputManagerList.indexOf(interface) < mTextInputManagerIndex)) {
+ qCDebug(lcQpaWayland) << "text input: register zwp_text_input_v3";
+ if (mTextInputManagerIndex < INT_MAX) {
+ mGlobals.textInputMethodManager.reset();
+ mGlobals.textInputManagerv2.reset();
+ for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices))
+ inputDevice->setTextInputMethod(nullptr);
+ }
+ mGlobals.textInputManagerv3.reset(
+ new WithDestructor<QtWayland::zwp_text_input_manager_v3,
+ zwp_text_input_manager_v3_destroy>(registry, id, 1));
+ for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices))
+ inputDevice->setTextInput(new QWaylandTextInputv3(
+ this, mGlobals.textInputManagerv3->get_text_input(inputDevice->wl_seat())));
+
mWaylandIntegration->reconfigureInputContext();
- } else if (interface == QStringLiteral("qt_hardware_integration")) {
+ mTextInputManagerIndex = mTextInputManagerList.indexOf(interface);
+ }else if (interface == QLatin1String(QWaylandHardwareIntegration::interface()->name)) {
bool disableHardwareIntegration = qEnvironmentVariableIntValue("QT_WAYLAND_DISABLE_HW_INTEGRATION");
if (!disableHardwareIntegration) {
- mHardwareIntegration.reset(new QWaylandHardwareIntegration(registry, id));
+ mGlobals.hardwareIntegration.reset(new QWaylandHardwareIntegration(registry, id));
// make a roundtrip here since we need to receive the events sent by
// qt_hardware_integration before creating windows
forceRoundTrip();
}
- } else if (interface == QLatin1String("zxdg_output_manager_v1")) {
- mXdgOutputManager.reset(new QWaylandXdgOutputManagerV1(this, id, version));
- for (auto *screen : qAsConst(mWaitingScreens))
+ } else if (interface == QLatin1String(QWaylandXdgOutputManagerV1::interface()->name)) {
+ mGlobals.xdgOutputManager.reset(new QWaylandXdgOutputManagerV1(this, id, version));
+ for (auto *screen : std::as_const(mWaitingScreens))
screen->initXdgOutput(xdgOutputManager());
- forceRoundTrip();
+ } else if (interface == QLatin1String(QtWayland::wp_fractional_scale_manager_v1::interface()->name)) {
+ mGlobals.fractionalScaleManager.reset(
+ new WithDestructor<QtWayland::wp_fractional_scale_manager_v1,
+ wp_fractional_scale_manager_v1_destroy>(registry, id, 1));
+ } else if (interface == QLatin1String("wp_viewporter")) {
+ mGlobals.viewporter.reset(
+ new WithDestructor<QtWayland::wp_viewporter, wp_viewporter_destroy>(
+ registry, id, qMin(1u, version)));
+ } else if (interface == QLatin1String(QtWayland::wp_cursor_shape_manager_v1::interface()->name)) {
+ mGlobals.cursorShapeManager.reset(new WithDestructor<QtWayland::wp_cursor_shape_manager_v1,
+ wp_cursor_shape_manager_v1_destroy>(
+ registry, id, std::min(1u, version)));
+ } else if (
+ interface == QLatin1String(QtWayland::xdg_toplevel_drag_manager_v1::interface()->name)) {
+ mGlobals.xdgToplevelDragManager.reset(
+ new WithDestructor<QtWayland::xdg_toplevel_drag_manager_v1,
+ xdg_toplevel_drag_manager_v1_destroy>(registry, id, 1));
+ } else if (interface == QLatin1String(QtWayland::qt_windowmanager::interface()->name)) {
+ mGlobals.windowManagerIntegration.reset(
+ new QWaylandWindowManagerIntegration(this, id, version));
}
- mGlobals.append(RegistryGlobal(id, interface, version, registry));
+ mRegistryGlobals.append(RegistryGlobal(id, interface, version, registry));
+ emit globalAdded(mRegistryGlobals.back());
const auto copy = mRegistryListeners; // be prepared for listeners unregistering on notification
for (Listener l : copy)
@@ -407,10 +793,10 @@ void QWaylandDisplay::registry_global(uint32_t id, const QString &interface, uin
void QWaylandDisplay::registry_global_remove(uint32_t id)
{
- for (int i = 0, ie = mGlobals.count(); i != ie; ++i) {
- RegistryGlobal &global = mGlobals[i];
+ for (int i = 0, ie = mRegistryGlobals.size(); i != ie; ++i) {
+ RegistryGlobal &global = mRegistryGlobals[i];
if (global.id == id) {
- if (global.interface == QStringLiteral("wl_output")) {
+ if (global.interface == QLatin1String(QtWayland::wl_output::interface()->name)) {
for (auto *screen : mWaitingScreens) {
if (screen->outputId() == id) {
mWaitingScreens.removeOne(screen);
@@ -419,7 +805,7 @@ void QWaylandDisplay::registry_global_remove(uint32_t id)
}
}
- for (QWaylandScreen *screen : qAsConst(mScreens)) {
+ for (QWaylandScreen *screen : std::as_const(mScreens)) {
if (screen->outputId() == id) {
mScreens.removeOne(screen);
// If this is the last screen, we have to add a fake screen, or Qt will break.
@@ -429,19 +815,38 @@ void QWaylandDisplay::registry_global_remove(uint32_t id)
}
}
}
- if (global.interface == QStringLiteral("zwp_text_input_manager_v2")) {
- mTextInputManager.reset();
- for (QWaylandInputDevice *inputDevice : qAsConst(mInputDevices))
+ if (global.interface == QLatin1String(QtWayland::zwp_text_input_manager_v1::interface()->name)) {
+ mGlobals.textInputManagerv1.reset();
+ for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices))
inputDevice->setTextInput(nullptr);
mWaylandIntegration->reconfigureInputContext();
}
- if (global.interface == QStringLiteral("qt_text_input_method_manager_v1")) {
- mTextInputMethodManager.reset();
- for (QWaylandInputDevice *inputDevice : qAsConst(mInputDevices))
+ if (global.interface == QLatin1String(QtWayland::zwp_text_input_manager_v2::interface()->name)) {
+ mGlobals.textInputManagerv2.reset();
+ for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices))
+ inputDevice->setTextInput(nullptr);
+ mWaylandIntegration->reconfigureInputContext();
+ }
+ if (global.interface == QLatin1String(QtWayland::zwp_text_input_manager_v3::interface()->name)) {
+ mGlobals.textInputManagerv3.reset();
+ for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices))
+ inputDevice->setTextInput(nullptr);
+ mWaylandIntegration->reconfigureInputContext();
+ }
+ if (global.interface == QLatin1String(QtWayland::qt_text_input_method_manager_v1::interface()->name)) {
+ mGlobals.textInputMethodManager.reset();
+ for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices))
inputDevice->setTextInputMethod(nullptr);
mWaylandIntegration->reconfigureInputContext();
}
- mGlobals.removeAt(i);
+#if QT_CONFIG(wayland_client_primary_selection)
+ if (global.interface == QLatin1String(QtWayland::zwp_primary_selection_device_manager_v1::interface()->name)) {
+ mGlobals.primarySelectionManager.reset();
+ for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices))
+ inputDevice->setPrimarySelectionDevice(nullptr);
+ }
+#endif
+ emit globalRemoved(mRegistryGlobals.takeAt(i));
break;
}
}
@@ -449,7 +854,7 @@ void QWaylandDisplay::registry_global_remove(uint32_t id)
bool QWaylandDisplay::hasRegistryGlobal(QStringView interfaceName) const
{
- for (const RegistryGlobal &global : mGlobals)
+ for (const RegistryGlobal &global : mRegistryGlobals)
if (global.interface == interfaceName)
return true;
@@ -460,71 +865,22 @@ void QWaylandDisplay::addRegistryListener(RegistryListener listener, void *data)
{
Listener l = { listener, data };
mRegistryListeners.append(l);
- for (int i = 0, ie = mGlobals.count(); i != ie; ++i)
- (*l.listener)(l.data, mGlobals[i].registry, mGlobals[i].id, mGlobals[i].interface, mGlobals[i].version);
+ for (int i = 0, ie = mRegistryGlobals.size(); i != ie; ++i)
+ (*l.listener)(l.data, mRegistryGlobals[i].registry, mRegistryGlobals[i].id,
+ mRegistryGlobals[i].interface, mRegistryGlobals[i].version);
}
void QWaylandDisplay::removeListener(RegistryListener listener, void *data)
{
- std::remove_if(mRegistryListeners.begin(), mRegistryListeners.end(), [=](Listener l){
+ auto iter = std::remove_if(mRegistryListeners.begin(), mRegistryListeners.end(), [=](Listener l){
return (l.listener == listener && l.data == data);
});
+ mRegistryListeners.erase(iter, mRegistryListeners.end());
}
-uint32_t QWaylandDisplay::currentTimeMillisec()
-{
- //### we throw away the time information
- struct timeval tv;
- int ret = gettimeofday(&tv, nullptr);
- if (ret == 0)
- return tv.tv_sec*1000 + tv.tv_usec/1000;
- return 0;
-}
-
-static void
-sync_callback(void *data, struct wl_callback *callback, uint32_t serial)
-{
- Q_UNUSED(serial);
- bool *done = static_cast<bool *>(data);
-
- *done = true;
-
- // If the wl_callback done event is received after the condition check in the while loop in
- // forceRoundTrip(), but before the call to processEvents, the call to processEvents may block
- // forever if no more events are posted (eventhough the callback is handled in response to the
- // aboutToBlock signal). Hence, we wake up the event dispatcher so forceRoundTrip may return.
- // (QTBUG-64696)
- if (auto *dispatcher = QThread::currentThread()->eventDispatcher())
- dispatcher->wakeUp();
-
- wl_callback_destroy(callback);
-}
-
-static const struct wl_callback_listener sync_listener = {
- sync_callback
-};
-
void QWaylandDisplay::forceRoundTrip()
{
- // wl_display_roundtrip() works on the main queue only,
- // but we use a separate one, so basically reimplement it here
- int ret = 0;
- bool done = false;
- wl_callback *callback = wl_display_sync(mDisplay);
- wl_callback_add_listener(callback, &sync_listener, &done);
- flushRequests();
- if (QThread::currentThread()->eventDispatcher()) {
- while (!done && ret >= 0) {
- QThread::currentThread()->eventDispatcher()->processEvents(QEventLoop::WaitForMoreEvents);
- ret = wl_display_dispatch_pending(mDisplay);
- }
- } else {
- while (!done && ret >= 0)
- ret = wl_display_dispatch(mDisplay);
- }
-
- if (ret == -1 && !done)
- wl_callback_destroy(callback);
+ wl_display_roundtrip(mDisplay);
}
bool QWaylandDisplay::supportsWindowDecoration() const
@@ -535,7 +891,12 @@ bool QWaylandDisplay::supportsWindowDecoration() const
if (disabled)
return false;
- static bool integrationSupport = clientBufferIntegration() && clientBufferIntegration()->supportsWindowDecoration();
+ // Don't initialize client buffer integration just to check whether it can have a decoration.
+ if (!mWaylandIntegration->mClientBufferIntegrationInitialized)
+ return true;
+
+ // We can do software-rendered decorations, only disable them if the integration explicitly says it can't.
+ static bool integrationSupport = !clientBufferIntegration() || clientBufferIntegration()->supportsWindowDecoration();
return integrationSupport;
}
@@ -577,6 +938,9 @@ void QWaylandDisplay::handleWindowDeactivated(QWaylandWindow *window)
mActiveWindows.removeOne(window);
+ if (QCoreApplication::closingDown())
+ return;
+
if (auto *decoration = window->decoration())
decoration->update();
}
@@ -588,14 +952,10 @@ void QWaylandDisplay::handleKeyboardFocusChanged(QWaylandInputDevice *inputDevic
if (mLastKeyboardFocus == keyboardFocus)
return;
- if (mWaylandIntegration->mShellIntegration) {
- mWaylandIntegration->mShellIntegration->handleKeyboardFocusChanged(keyboardFocus, mLastKeyboardFocus);
- } else {
- if (keyboardFocus)
- handleWindowActivated(keyboardFocus);
- if (mLastKeyboardFocus)
- handleWindowDeactivated(mLastKeyboardFocus);
- }
+ if (keyboardFocus)
+ handleWindowActivated(keyboardFocus);
+ if (mLastKeyboardFocus)
+ handleWindowDeactivated(mLastKeyboardFocus);
mLastKeyboardFocus = keyboardFocus;
}
@@ -613,7 +973,20 @@ void QWaylandDisplay::handleWaylandSync()
// handleWindowActivated() calls immediately.
QWindow *activeWindow = mActiveWindows.empty() ? nullptr : mActiveWindows.last()->window();
if (activeWindow != QGuiApplication::focusWindow())
- QWindowSystemInterface::handleWindowActivated(activeWindow);
+ QWindowSystemInterface::handleFocusWindowChanged(activeWindow);
+
+ if (!activeWindow) {
+ if (lastInputDevice()) {
+#if QT_CONFIG(clipboard)
+ if (auto *dataDevice = lastInputDevice()->dataDevice())
+ dataDevice->invalidateSelectionOffer();
+#endif
+#if QT_CONFIG(wayland_client_primary_selection)
+ if (auto *device = lastInputDevice()->primarySelectionDevice())
+ device->invalidateSelectionOffer();
+#endif
+ }
+ }
}
const wl_callback_listener QWaylandDisplay::syncCallbackListener = {
@@ -637,7 +1010,18 @@ void QWaylandDisplay::requestWaylandSync()
QWaylandInputDevice *QWaylandDisplay::defaultInputDevice() const
{
- return mInputDevices.isEmpty() ? 0 : mInputDevices.first();
+ return mInputDevices.isEmpty() ? nullptr : mInputDevices.first();
+}
+
+bool QWaylandDisplay::isKeyboardAvailable() const
+{
+ return std::any_of(
+ mInputDevices.constBegin(), mInputDevices.constEnd(),
+ [](const QWaylandInputDevice *device) { return device->keyboard() != nullptr; });
+}
+
+bool QWaylandDisplay::isWaylandInputContextRequested() const {
+ return mWaylandInputContextRequested;
}
#if QT_CONFIG(cursor)
@@ -645,7 +1029,7 @@ QWaylandInputDevice *QWaylandDisplay::defaultInputDevice() const
QWaylandCursor *QWaylandDisplay::waylandCursor()
{
if (!mCursor)
- mCursor.reset(new QWaylandCursor(this));
+ mCursor.reset(mWaylandIntegration->createPlatformCursor(this));
return mCursor.data();
}
@@ -682,3 +1066,6 @@ QWaylandCursorTheme *QWaylandDisplay::loadCursorTheme(const QString &name, int p
} // namespace QtWaylandClient
QT_END_NAMESPACE
+
+#include "qwaylanddisplay.moc"
+#include "moc_qwaylanddisplay_p.cpp"