forked from quickshell/quickshell
		
	wayland/toplevel: add pending state for outputs entered before qscreen init
Fixes a crash in sway, and potentially other compositors, when a toplevel enters an output before Qt has created a QScreen for it.
This commit is contained in:
		
							parent
							
								
									c3ed3b0ee2
								
							
						
					
					
						commit
						aeb347ba91
					
				
					 2 changed files with 65 additions and 4 deletions
				
			
		| 
						 | 
					@ -7,6 +7,7 @@
 | 
				
			||||||
#include <private/qwaylandscreen_p.h>
 | 
					#include <private/qwaylandscreen_p.h>
 | 
				
			||||||
#include <private/qwaylandwindow_p.h>
 | 
					#include <private/qwaylandwindow_p.h>
 | 
				
			||||||
#include <qcontainerfwd.h>
 | 
					#include <qcontainerfwd.h>
 | 
				
			||||||
 | 
					#include <qguiapplication.h>
 | 
				
			||||||
#include <qlogging.h>
 | 
					#include <qlogging.h>
 | 
				
			||||||
#include <qloggingcategory.h>
 | 
					#include <qloggingcategory.h>
 | 
				
			||||||
#include <qobject.h>
 | 
					#include <qobject.h>
 | 
				
			||||||
| 
						 | 
					@ -181,22 +182,58 @@ void ToplevelHandle::zwlr_foreign_toplevel_handle_v1_state(wl_array* stateArray)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void ToplevelHandle::zwlr_foreign_toplevel_handle_v1_output_enter(wl_output* output) {
 | 
					void ToplevelHandle::zwlr_foreign_toplevel_handle_v1_output_enter(wl_output* output) {
 | 
				
			||||||
	auto* display = QtWaylandClient::QWaylandIntegration::instance()->display();
 | 
						auto* display = QtWaylandClient::QWaylandIntegration::instance()->display();
 | 
				
			||||||
	auto* screen = display->screenForOutput(output)->screen();
 | 
					
 | 
				
			||||||
 | 
						auto* platformScreen = display->screenForOutput(output);
 | 
				
			||||||
 | 
						if (!platformScreen) {
 | 
				
			||||||
 | 
							qCDebug(logToplevelManagement) << this << "got pending output enter" << output;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (this->mPendingVisibleScreens.isEmpty()) {
 | 
				
			||||||
 | 
								QObject::connect(
 | 
				
			||||||
 | 
								    static_cast<QGuiApplication*>(QGuiApplication::instance()), // NOLINT
 | 
				
			||||||
 | 
								    &QGuiApplication::screenAdded,
 | 
				
			||||||
 | 
								    this,
 | 
				
			||||||
 | 
								    &ToplevelHandle::onScreenAdded
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							this->mPendingVisibleScreens.append(output);
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto* screen = platformScreen->screen();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	qCDebug(logToplevelManagement) << this << "got output enter" << screen;
 | 
						qCDebug(logToplevelManagement) << this << "got output enter" << screen;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	this->mVisibleScreens.push_back(screen);
 | 
						this->mVisibleScreens.append(screen);
 | 
				
			||||||
	emit this->visibleScreenAdded(screen);
 | 
						emit this->visibleScreenAdded(screen);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void ToplevelHandle::zwlr_foreign_toplevel_handle_v1_output_leave(wl_output* output) {
 | 
					void ToplevelHandle::zwlr_foreign_toplevel_handle_v1_output_leave(wl_output* output) {
 | 
				
			||||||
	auto* display = QtWaylandClient::QWaylandIntegration::instance()->display();
 | 
						auto* display = QtWaylandClient::QWaylandIntegration::instance()->display();
 | 
				
			||||||
	auto* screen = display->screenForOutput(output)->screen();
 | 
						auto* platformScreen = display->screenForOutput(output);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!this->mPendingVisibleScreens.isEmpty()) {
 | 
				
			||||||
 | 
							this->mPendingVisibleScreens.removeOne(output);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (this->mPendingVisibleScreens.isEmpty()) {
 | 
				
			||||||
 | 
								qCDebug(logToplevelManagement) << this << "got pending output leave" << output;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								QObject::disconnect(
 | 
				
			||||||
 | 
								    static_cast<QGuiApplication*>(QGuiApplication::instance()), // NOLINT
 | 
				
			||||||
 | 
								    nullptr,
 | 
				
			||||||
 | 
								    this,
 | 
				
			||||||
 | 
								    nullptr
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!platformScreen) return;
 | 
				
			||||||
 | 
						auto* screen = platformScreen->screen();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	qCDebug(logToplevelManagement) << this << "got output leave" << screen;
 | 
						qCDebug(logToplevelManagement) << this << "got output leave" << screen;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	emit this->visibleScreenRemoved(screen);
 | 
					 | 
				
			||||||
	this->mVisibleScreens.removeOne(screen);
 | 
						this->mVisibleScreens.removeOne(screen);
 | 
				
			||||||
 | 
						emit this->visibleScreenRemoved(screen);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void ToplevelHandle::zwlr_foreign_toplevel_handle_v1_parent(
 | 
					void ToplevelHandle::zwlr_foreign_toplevel_handle_v1_parent(
 | 
				
			||||||
| 
						 | 
					@ -225,4 +262,26 @@ void ToplevelHandle::onParentClosed() {
 | 
				
			||||||
	emit this->parentChanged();
 | 
						emit this->parentChanged();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ToplevelHandle::onScreenAdded(QScreen* screen) {
 | 
				
			||||||
 | 
						auto* waylandScreen = dynamic_cast<QtWaylandClient::QWaylandScreen*>(screen->handle());
 | 
				
			||||||
 | 
						if (!waylandScreen) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto* output = waylandScreen->output();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (this->mPendingVisibleScreens.removeOne(output)) {
 | 
				
			||||||
 | 
							qCDebug(logToplevelManagement) << this << "got pending entered output init" << screen;
 | 
				
			||||||
 | 
							this->mVisibleScreens.append(screen);
 | 
				
			||||||
 | 
							emit this->visibleScreenAdded(screen);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (this->mPendingVisibleScreens.isEmpty()) {
 | 
				
			||||||
 | 
							QObject::disconnect(
 | 
				
			||||||
 | 
							    static_cast<QGuiApplication*>(QGuiApplication::instance()), // NOLINT
 | 
				
			||||||
 | 
							    nullptr,
 | 
				
			||||||
 | 
							    this,
 | 
				
			||||||
 | 
							    nullptr
 | 
				
			||||||
 | 
							);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
} // namespace qs::wayland::toplevel_management::impl
 | 
					} // namespace qs::wayland::toplevel_management::impl
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -51,6 +51,7 @@ signals:
 | 
				
			||||||
private slots:
 | 
					private slots:
 | 
				
			||||||
	void onParentClosed();
 | 
						void onParentClosed();
 | 
				
			||||||
	void onRectWindowDestroyed();
 | 
						void onRectWindowDestroyed();
 | 
				
			||||||
 | 
						void onScreenAdded(QScreen* screen);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
	void zwlr_foreign_toplevel_handle_v1_done() override;
 | 
						void zwlr_foreign_toplevel_handle_v1_done() override;
 | 
				
			||||||
| 
						 | 
					@ -66,6 +67,7 @@ private:
 | 
				
			||||||
	QString mAppId;
 | 
						QString mAppId;
 | 
				
			||||||
	QString mTitle;
 | 
						QString mTitle;
 | 
				
			||||||
	QVector<QScreen*> mVisibleScreens;
 | 
						QVector<QScreen*> mVisibleScreens;
 | 
				
			||||||
 | 
						QVector<wl_output*> mPendingVisibleScreens;
 | 
				
			||||||
	ToplevelHandle* mParent = nullptr;
 | 
						ToplevelHandle* mParent = nullptr;
 | 
				
			||||||
	bool mActivated = false;
 | 
						bool mActivated = false;
 | 
				
			||||||
	bool mMaximized = false;
 | 
						bool mMaximized = false;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue