summaryrefslogtreecommitdiff
path: root/src/client/qwaylanddisplay.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/qwaylanddisplay.cpp')
-rw-r--r--src/client/qwaylanddisplay.cpp161
1 files changed, 143 insertions, 18 deletions
diff --git a/src/client/qwaylanddisplay.cpp b/src/client/qwaylanddisplay.cpp
index 46c85a6a..5c3431a0 100644
--- a/src/client/qwaylanddisplay.cpp
+++ b/src/client/qwaylanddisplay.cpp
@@ -65,18 +65,6 @@
#include <tuple> // for std::tie
-static void checkWaylandError(struct wl_display *display)
-{
- int ecode = wl_display_get_error(display);
- if ((ecode == EPIPE || ecode == ECONNRESET)) {
- // special case this to provide a nicer error
- qWarning("The Wayland connection broke. Did the Wayland compositor die?");
- } else {
- qWarning("The Wayland connection experienced a fatal error: %s", strerror(ecode));
- }
- _exit(1);
-}
-
QT_BEGIN_NAMESPACE
namespace QtWaylandClient {
@@ -114,9 +102,13 @@ public:
* 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) {
- checkWaylandError(m_wldisplay);
+ Q_EMIT waylandError();
+ m_quitting = true;
return;
}
@@ -154,6 +146,7 @@ public:
Q_SIGNALS:
void needReadAndDispatch();
+ void waylandError();
protected:
void run() override
@@ -328,11 +321,17 @@ 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);
@@ -404,7 +403,110 @@ void QWaylandDisplay::ensureScreen()
Q_ASSERT(!QGuiApplication::screens().empty());
}
-// Called in main thread, either from queued signal or directly.
+void QWaylandDisplay::reconnect()
+{
+ qCWarning(lcQpaWayland) << "Attempting wayland reconnect";
+ m_eventThread->stop();
+ m_frameEventQueueThread->stop();
+ m_eventThread->wait();
+ m_frameEventQueueThread->wait();
+
+ qDeleteAll(mWaitingScreens);
+ mWaitingScreens.clear();
+
+ // mCompositor
+ mShm.reset();
+ mCursorThemes.clear();
+ mCursor.reset();
+ mDndSelectionHandler.reset();
+ mWindowExtension.reset();
+ mSubCompositor.reset();
+ mTouchExtension.reset();
+ mQtKeyExtension.reset();
+ mWindowManagerIntegration.reset();
+ mTabletManager.reset();
+ mPointerGestures.reset();
+#if QT_CONFIG(wayland_client_primary_selection)
+ mPrimarySelectionManager.reset();
+#endif
+ mTextInputMethodManager.reset();
+ mTextInputManagerv1.reset();
+ mTextInputManagerv2.reset();
+ mTextInputManagerv4.reset();
+ mHardwareIntegration.reset();
+ mXdgOutputManager.reset();
+ mViewporter.reset();
+ mFractionalScaleManager.reset();
+
+ mWaylandIntegration->reset();
+
+ qDeleteAll(std::exchange(mInputDevices, {}));
+ mLastInputDevice = nullptr;
+
+ auto screens = mScreens;
+ mScreens.clear();
+
+ for (const RegistryGlobal &global : mGlobals) {
+ emit globalRemoved(global);
+ }
+ mGlobals.clear();
+
+ mLastInputSerial = 0;
+ mLastInputWindow.clear();
+ mLastKeyboardFocus.clear();
+ mActiveWindows.clear();
+
+ const auto windows = QGuiApplication::allWindows();
+ for (auto window : windows) {
+ if (auto waylandWindow = dynamic_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 = dynamic_cast<QWaylandWindow*>((window)->handle());
+ if (waylandWindow && waylandWindow->wlSurface()) {
+ waylandWindow->reset();
+ recreateWindows.push_back(waylandWindow);
+ }
+ }
+
+ if (mSyncCallback) {
+ wl_callback_destroy(mSyncCallback);
+ mSyncCallback = nullptr;
+ }
+
+ mDisplay = wl_display_connect(nullptr);
+ if (!mDisplay)
+ _exit(1);
+
+ setupConnection();
+ initialize();
+
+ if (m_frameEventQueue)
+ wl_event_queue_destroy(m_frameEventQueue);
+ initEventThread();
+
+ emit reconnected();
+
+ 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::flushRequests()
{
m_eventThread->readAndDispatchEvents();
@@ -419,6 +521,8 @@ void QWaylandDisplay::initEventThread()
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.
@@ -428,10 +532,31 @@ void QWaylandDisplay::initEventThread()
m_frameEventQueueThread->start();
}
+void QWaylandDisplay::checkWaylandError()
+{
+ 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::blockingReadEvents()
{
- if (wl_display_dispatch(mDisplay) < 0)
- checkWaylandError(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
+ qWarning("The Wayland connection experienced a fatal error during blocking read event: %s", strerror(ecode));
+ _exit(-1);
+ }
}
void QWaylandDisplay::checkTextInputProtocol()