diff --git a/.github/ISSUE_TEMPLATE/crash.yml b/.github/ISSUE_TEMPLATE/crash.yml
index 80fa827..958c884 100644
--- a/.github/ISSUE_TEMPLATE/crash.yml
+++ b/.github/ISSUE_TEMPLATE/crash.yml
@@ -1,82 +1,17 @@
name: Crash Report (v1)
-description: Quickshell has crashed
-labels: ["bug", "crash"]
+description: Quickshell has crashed (old)
+labels: ["unactionable"]
body:
- - type: textarea
- id: crashinfo
+ - type: markdown
attributes:
- label: General crash information
- description: |
- Paste the contents of the `info.txt` file in your crash folder here.
- value: " General information
-
-
- ```
-
-
-
- ```
-
-
- "
- validations:
- required: true
- - type: textarea
- id: userinfo
+ value: |
+ Thank you for taking the time to click the report button.
+ At this point most of the worst issues in 0.2.1 and before have been fixed and we are
+ preparing for a new release. Please do not submit this report.
+ - type: checkboxes
+ id: donotcheck
attributes:
- label: What caused the crash
- description: |
- Any information likely to help debug the crash. What were you doing when the crash occurred,
- what changes did you make, can you get it to happen again?
- - type: textarea
- id: dump
- attributes:
- label: Minidump
- description: |
- Attach `minidump.dmp.log` here. If it is too big to upload, compress it.
-
- You may skip this step if quickshell crashed while processing a password
- or other sensitive information. If you skipped it write why instead.
- validations:
- required: true
- - type: textarea
- id: logs
- attributes:
- label: Log file
- description: |
- Attach `log.qslog.log` here. If it is too big to upload, compress it.
-
- You can preview the log if you'd like using `quickshell read-log `.
- validations:
- required: true
- - type: textarea
- id: config
- attributes:
- label: Configuration
- description: |
- Attach your configuration here, preferrably in full (not just one file).
- Compress it into a zip, tar, etc.
-
- This will help us reproduce the crash ourselves.
- - type: textarea
- id: bt
- attributes:
- label: Backtrace
- description: |
- If you have gdb installed and use systemd, or otherwise know how to get a backtrace,
- we would appreciate one. (You may have gdb installed without knowing it)
-
- 1. Run `coredumpctl debug ` where `pid` is the number shown after "Crashed process ID"
- in the crash reporter.
- 2. Once it loads, type `bt -full` (then enter)
- 3. Copy the output and attach it as a file or in a spoiler.
- - type: textarea
- id: exe
- attributes:
- label: Executable
- description: |
- If the crash folder contains a executable.txt file, upload it here. If not you can ignore this field.
- If it is too big to upload, compress it.
-
- Note: executable.txt is the quickshell binary. It has a .txt extension due to github's limitations on
- filetypes.
+ label: Read the text above. Do not submit the report.
+ options:
+ - label: Yes I want this report to be deleted.
+ required: true
diff --git a/.github/ISSUE_TEMPLATE/crash2.yml b/.github/ISSUE_TEMPLATE/crash2.yml
index 84beef8..86f490c 100644
--- a/.github/ISSUE_TEMPLATE/crash2.yml
+++ b/.github/ISSUE_TEMPLATE/crash2.yml
@@ -9,21 +9,21 @@ body:
description: |
Any information likely to help debug the crash. What were you doing when the crash occurred,
what changes did you make, can you get it to happen again?
- - type: textarea
+ - type: upload
id: report
attributes:
label: Report file
description: Attach `report.txt` here.
validations:
required: true
- - type: textarea
+ - type: upload
id: logs
attributes:
label: Log file
description: |
Attach `log.qslog.log` here. If it is too big to upload, compress it.
- You can preview the log if you'd like using `quickshell read-log `.
+ You can preview the log if you'd like using `qs log -r '*=true'`.
validations:
required: true
- type: textarea
@@ -31,7 +31,7 @@ body:
attributes:
label: Configuration
description: |
- Attach your configuration here, preferrably in full (not just one file).
+ Attach or link your configuration here, preferrably in full (not just one file).
Compress it into a zip, tar, etc.
This will help us reproduce the crash ourselves.
diff --git a/changelog/next.md b/changelog/next.md
index cbfd51b..a8981b9 100644
--- a/changelog/next.md
+++ b/changelog/next.md
@@ -48,6 +48,7 @@ set shell id.
- Fixed volumes not initializing if a pipewire device was already loaded before its node.
- Fixed hyprland active toplevel not resetting after window closes.
- Fixed hyprland ipc window names and titles being reversed.
+- Fixed a hyprland ipc crash when refreshing toplevels before workspaces.
- Fixed missing signals for system tray item title and description updates.
- Fixed asynchronous loaders not working after reload.
- Fixed asynchronous loaders not working before window creation.
@@ -58,6 +59,8 @@ set shell id.
- Fixed ToplevelManager not clearing activeToplevel on deactivation.
- Desktop action order is now preserved.
- Fixed partial socket reads in greetd and hyprland on slow machines.
+- Worked around Qt bug causing crashes when plugging and unplugging monitors.
+- Fixed HyprlandFocusGrab crashing if windows were destroyed after being passed to it.
## Packaging Changes
diff --git a/src/core/logging.cpp b/src/core/logging.cpp
index d24225b..893c56e 100644
--- a/src/core/logging.cpp
+++ b/src/core/logging.cpp
@@ -31,6 +31,9 @@
#include
#include
#endif
+#ifdef __FreeBSD__
+#include
+#endif
#include "instanceinfo.hpp"
#include "logcat.hpp"
@@ -67,7 +70,7 @@ bool copyFileData(int sourceFd, int destFd, qint64 size) {
return true;
#else
std::array buffer = {};
- auto remaining = totalTarget;
+ auto remaining = usize;
while (remaining > 0) {
auto chunk = std::min(remaining, buffer.size());
diff --git a/src/core/platformmenu.cpp b/src/core/platformmenu.cpp
index 427dde0..d8901e2 100644
--- a/src/core/platformmenu.cpp
+++ b/src/core/platformmenu.cpp
@@ -18,7 +18,6 @@
#include
#include "../window/proxywindow.hpp"
-#include "../window/windowinterface.hpp"
#include "iconprovider.hpp"
#include "model.hpp"
#include "platformmenu_p.hpp"
@@ -91,10 +90,8 @@ bool PlatformMenuEntry::display(QObject* parentWindow, int relativeX, int relati
} else if (parentWindow == nullptr) {
qCritical() << "Cannot display PlatformMenuEntry with null parent window.";
return false;
- } else if (auto* proxy = qobject_cast(parentWindow)) {
+ } else if (auto* proxy = ProxyWindowBase::forObject(parentWindow)) {
window = proxy->backingWindow();
- } else if (auto* interface = qobject_cast(parentWindow)) {
- window = interface->proxyWindow()->backingWindow();
} else {
qCritical() << "PlatformMenuEntry.display() must be called with a window.";
return false;
diff --git a/src/core/popupanchor.cpp b/src/core/popupanchor.cpp
index 151dd5d..ca817c9 100644
--- a/src/core/popupanchor.cpp
+++ b/src/core/popupanchor.cpp
@@ -11,7 +11,6 @@
#include
#include "../window/proxywindow.hpp"
-#include "../window/windowinterface.hpp"
#include "types.hpp"
bool PopupAnchorState::operator==(const PopupAnchorState& other) const {
@@ -40,10 +39,8 @@ void PopupAnchor::setWindowInternal(QObject* window) {
}
if (window) {
- if (auto* proxy = qobject_cast(window)) {
+ if (auto* proxy = ProxyWindowBase::forObject(window)) {
this->bProxyWindow = proxy;
- } else if (auto* interface = qobject_cast(window)) {
- this->bProxyWindow = interface->proxyWindow();
} else {
qWarning() << "Tried to set popup anchor window to" << window
<< "which is not a quickshell window.";
diff --git a/src/services/pam/conversation.cpp b/src/services/pam/conversation.cpp
index f8f5a09..1fb4c04 100644
--- a/src/services/pam/conversation.cpp
+++ b/src/services/pam/conversation.cpp
@@ -8,6 +8,9 @@
#include
#include
#include
+#ifdef __FreeBSD__
+#include
+#endif
#include "../../core/logcat.hpp"
#include "ipc.hpp"
diff --git a/src/wayland/CMakeLists.txt b/src/wayland/CMakeLists.txt
index db53f37..4a67558 100644
--- a/src/wayland/CMakeLists.txt
+++ b/src/wayland/CMakeLists.txt
@@ -73,6 +73,7 @@ endfunction()
# -----
qt_add_library(quickshell-wayland STATIC
+ wl_proxy_safe_deref.cpp
platformmenu.cpp
popupanchor.cpp
xdgshell.cpp
@@ -80,6 +81,10 @@ qt_add_library(quickshell-wayland STATIC
output_tracking.cpp
)
+# required for wl_proxy_safe_deref
+target_link_libraries(quickshell-wayland PRIVATE ${CMAKE_DL_LIBS})
+target_link_options(quickshell PRIVATE "LINKER:--export-dynamic-symbol=wl_proxy_get_listener")
+
# required to make sure the constructor is linked
add_library(quickshell-wayland-init OBJECT init.cpp)
diff --git a/src/wayland/hyprland/focus_grab/qml.cpp b/src/wayland/hyprland/focus_grab/qml.cpp
index e26a75a..cf1ac24 100644
--- a/src/wayland/hyprland/focus_grab/qml.cpp
+++ b/src/wayland/hyprland/focus_grab/qml.cpp
@@ -9,7 +9,6 @@
#include
#include "../../../window/proxywindow.hpp"
-#include "../../../window/windowinterface.hpp"
#include "grab.hpp"
#include "manager.hpp"
@@ -38,8 +37,51 @@ QObjectList HyprlandFocusGrab::windows() const { return this->windowObjects; }
void HyprlandFocusGrab::setWindows(QObjectList windows) {
if (windows == this->windowObjects) return;
+ if (this->grab) this->grab->startTransaction();
+
+ for (auto* obj: this->windowObjects) {
+ if (windows.contains(obj)) continue;
+ QObject::disconnect(obj, nullptr, this, nullptr);
+
+ auto* proxy = ProxyWindowBase::forObject(obj);
+ if (!proxy) continue;
+
+ QObject::disconnect(proxy, nullptr, this, nullptr);
+
+ if (this->grab && proxy->backingWindow()) {
+ this->grab->removeWindow(proxy->backingWindow());
+ }
+ }
+
+ for (auto it = windows.begin(); it != windows.end();) {
+ auto* proxy = ProxyWindowBase::forObject(*it);
+ if (!proxy) {
+ it = windows.erase(it);
+ continue;
+ }
+
+ if (this->windowObjects.contains(*it)) {
+ ++it;
+ continue;
+ }
+
+ QObject::connect(*it, &QObject::destroyed, this, &HyprlandFocusGrab::onObjectDestroyed);
+ QObject::connect(
+ proxy,
+ &ProxyWindowBase::windowConnected,
+ this,
+ &HyprlandFocusGrab::onProxyConnected
+ );
+
+ if (this->grab && proxy->backingWindow()) {
+ this->grab->addWindow(proxy->backingWindow());
+ }
+
+ ++it;
+ }
+
+ if (this->grab) this->grab->completeTransaction();
this->windowObjects = std::move(windows);
- this->syncWindows();
emit this->windowsChanged();
}
@@ -75,59 +117,18 @@ void HyprlandFocusGrab::tryActivate() {
QObject::connect(this->grab, &FocusGrab::cleared, this, &HyprlandFocusGrab::onGrabCleared);
this->grab->startTransaction();
- for (auto* proxy: this->trackedProxies) {
- if (proxy->backingWindow() != nullptr) {
+ for (auto* obj: this->windowObjects) {
+ auto* proxy = ProxyWindowBase::forObject(obj);
+ if (proxy && proxy->backingWindow()) {
this->grab->addWindow(proxy->backingWindow());
}
}
this->grab->completeTransaction();
}
-void HyprlandFocusGrab::syncWindows() {
- auto newProxy = QList();
- for (auto* windowObject: this->windowObjects) {
- auto* proxyWindow = qobject_cast(windowObject);
-
- if (proxyWindow == nullptr) {
- if (auto* iface = qobject_cast(windowObject)) {
- proxyWindow = iface->proxyWindow();
- }
- }
-
- if (proxyWindow != nullptr) {
- newProxy.push_back(proxyWindow);
- }
- }
-
- if (this->grab) this->grab->startTransaction();
-
- for (auto* oldWindow: this->trackedProxies) {
- if (!newProxy.contains(oldWindow)) {
- QObject::disconnect(oldWindow, nullptr, this, nullptr);
-
- if (this->grab != nullptr && oldWindow->backingWindow() != nullptr) {
- this->grab->removeWindow(oldWindow->backingWindow());
- }
- }
- }
-
- for (auto* newProxy: newProxy) {
- if (!this->trackedProxies.contains(newProxy)) {
- QObject::connect(
- newProxy,
- &ProxyWindowBase::windowConnected,
- this,
- &HyprlandFocusGrab::onProxyConnected
- );
-
- if (this->grab != nullptr && newProxy->backingWindow() != nullptr) {
- this->grab->addWindow(newProxy->backingWindow());
- }
- }
- }
-
- this->trackedProxies = newProxy;
- if (this->grab) this->grab->completeTransaction();
+void HyprlandFocusGrab::onObjectDestroyed(QObject* object) {
+ this->windowObjects.removeOne(object);
+ emit this->windowsChanged();
}
} // namespace qs::hyprland
diff --git a/src/wayland/hyprland/focus_grab/qml.hpp b/src/wayland/hyprland/focus_grab/qml.hpp
index 705b0d3..97a10de 100644
--- a/src/wayland/hyprland/focus_grab/qml.hpp
+++ b/src/wayland/hyprland/focus_grab/qml.hpp
@@ -96,15 +96,13 @@ private slots:
void onGrabActivated();
void onGrabCleared();
void onProxyConnected();
+ void onObjectDestroyed(QObject* object);
private:
void tryActivate();
- void syncWindows();
bool targetActive = false;
QObjectList windowObjects;
- QList trackedProxies;
- QList trackedWindows;
focus_grab::FocusGrab* grab = nullptr;
};
diff --git a/src/wayland/hyprland/ipc/connection.cpp b/src/wayland/hyprland/ipc/connection.cpp
index d2d5105..d15701d 100644
--- a/src/wayland/hyprland/ipc/connection.cpp
+++ b/src/wayland/hyprland/ipc/connection.cpp
@@ -729,7 +729,7 @@ void HyprlandIpc::refreshToplevels() {
}
auto* workspace = toplevel->bindableWorkspace().value();
- workspace->insertToplevel(toplevel);
+ if (workspace) workspace->insertToplevel(toplevel);
}
});
}
diff --git a/src/wayland/hyprland/ipc/hyprland_toplevel.cpp b/src/wayland/hyprland/ipc/hyprland_toplevel.cpp
index 7b07bc8..43b9838 100644
--- a/src/wayland/hyprland/ipc/hyprland_toplevel.cpp
+++ b/src/wayland/hyprland/ipc/hyprland_toplevel.cpp
@@ -72,20 +72,16 @@ void HyprlandToplevel::updateFromObject(const QVariantMap& object) {
Qt::beginPropertyUpdateGroup();
bool ok = false;
auto address = addressStr.toULongLong(&ok, 16);
- if (!ok || !address) {
- return;
- }
+ if (ok && address) this->setAddress(address);
- this->setAddress(address);
this->bTitle = title;
auto workspaceMap = object.value("workspace").toMap();
auto workspaceName = workspaceMap.value("name").toString();
- auto* workspace = this->ipc->findWorkspaceByName(workspaceName, false);
- if (!workspace) return;
+ auto* workspace = this->ipc->findWorkspaceByName(workspaceName, true);
+ if (workspace) this->setWorkspace(workspace);
- this->setWorkspace(workspace);
this->bLastIpcObject = object;
Qt::endPropertyUpdateGroup();
}
diff --git a/src/wayland/hyprland/surface/qml.cpp b/src/wayland/hyprland/surface/qml.cpp
index c4f7d67..4575842 100644
--- a/src/wayland/hyprland/surface/qml.cpp
+++ b/src/wayland/hyprland/surface/qml.cpp
@@ -14,7 +14,6 @@
#include "../../../core/region.hpp"
#include "../../../window/proxywindow.hpp"
-#include "../../../window/windowinterface.hpp"
#include "manager.hpp"
#include "surface.hpp"
@@ -23,13 +22,7 @@ using QtWaylandClient::QWaylandWindow;
namespace qs::hyprland::surface {
HyprlandWindow* HyprlandWindow::qmlAttachedProperties(QObject* object) {
- auto* proxyWindow = qobject_cast(object);
-
- if (!proxyWindow) {
- if (auto* iface = qobject_cast(object)) {
- proxyWindow = iface->proxyWindow();
- }
- }
+ auto* proxyWindow = ProxyWindowBase::forObject(object);
if (!proxyWindow) return nullptr;
return new HyprlandWindow(proxyWindow);
diff --git a/src/wayland/idle_inhibit/inhibitor.cpp b/src/wayland/idle_inhibit/inhibitor.cpp
index efeeae1..bfea7a0 100644
--- a/src/wayland/idle_inhibit/inhibitor.cpp
+++ b/src/wayland/idle_inhibit/inhibitor.cpp
@@ -6,7 +6,6 @@
#include
#include "../../window/proxywindow.hpp"
-#include "../../window/windowinterface.hpp"
#include "proto.hpp"
namespace qs::wayland::idle_inhibit {
@@ -25,27 +24,13 @@ QObject* IdleInhibitor::window() const { return this->bWindowObject; }
void IdleInhibitor::setWindow(QObject* window) {
if (window == this->bWindowObject) return;
- auto* proxyWindow = qobject_cast(window);
-
- if (proxyWindow == nullptr) {
- if (auto* iface = qobject_cast(window)) {
- proxyWindow = iface->proxyWindow();
- }
- }
-
+ auto* proxyWindow = ProxyWindowBase::forObject(window);
this->bWindowObject = proxyWindow ? window : nullptr;
}
void IdleInhibitor::boundWindowChanged() {
auto* window = this->bBoundWindow.value();
- auto* proxyWindow = qobject_cast(window);
-
- if (proxyWindow == nullptr) {
- if (auto* iface = qobject_cast(window)) {
- proxyWindow = iface->proxyWindow();
- }
- }
-
+ auto* proxyWindow = ProxyWindowBase::forObject(window);
if (proxyWindow == this->proxyWindow) return;
if (this->mWaylandWindow) {
diff --git a/src/wayland/init.cpp b/src/wayland/init.cpp
index e56eee3..790cebb 100644
--- a/src/wayland/init.cpp
+++ b/src/wayland/init.cpp
@@ -10,6 +10,7 @@
#include "wlr_layershell/wlr_layershell.hpp"
#endif
+void installWlProxySafeDeref(); // NOLINT(misc-use-internal-linkage)
void installPlatformMenuHook(); // NOLINT(misc-use-internal-linkage)
void installPopupPositioner(); // NOLINT(misc-use-internal-linkage)
@@ -33,6 +34,7 @@ class WaylandPlugin: public QsEnginePlugin {
}
void init() override {
+ installWlProxySafeDeref();
installPlatformMenuHook();
installPopupPositioner();
}
diff --git a/src/wayland/shortcuts_inhibit/inhibitor.cpp b/src/wayland/shortcuts_inhibit/inhibitor.cpp
index 2fca9bc..a91d5e2 100644
--- a/src/wayland/shortcuts_inhibit/inhibitor.cpp
+++ b/src/wayland/shortcuts_inhibit/inhibitor.cpp
@@ -9,7 +9,6 @@
#include
#include "../../window/proxywindow.hpp"
-#include "../../window/windowinterface.hpp"
#include "proto.hpp"
namespace qs::wayland::shortcuts_inhibit {
@@ -48,14 +47,7 @@ ShortcutInhibitor::~ShortcutInhibitor() {
void ShortcutInhibitor::onBoundWindowChanged() {
auto* window = this->bBoundWindow.value();
- auto* proxyWindow = qobject_cast(window);
-
- if (!proxyWindow) {
- if (auto* iface = qobject_cast(window)) {
- proxyWindow = iface->proxyWindow();
- }
- }
-
+ auto* proxyWindow = ProxyWindowBase::forObject(window);
if (proxyWindow == this->proxyWindow) return;
if (this->proxyWindow) {
diff --git a/src/wayland/toplevel_management/qml.cpp b/src/wayland/toplevel_management/qml.cpp
index 6a1d96b..cb53381 100644
--- a/src/wayland/toplevel_management/qml.cpp
+++ b/src/wayland/toplevel_management/qml.cpp
@@ -9,7 +9,6 @@
#include "../../core/qmlscreen.hpp"
#include "../../core/util.hpp"
#include "../../window/proxywindow.hpp"
-#include "../../window/windowinterface.hpp"
#include "../output_tracking.hpp"
#include "handle.hpp"
#include "manager.hpp"
@@ -73,13 +72,7 @@ void Toplevel::fullscreenOn(QuickshellScreenInfo* screen) {
}
void Toplevel::setRectangle(QObject* window, QRect rect) {
- auto* proxyWindow = qobject_cast(window);
-
- if (proxyWindow == nullptr) {
- if (auto* iface = qobject_cast(window)) {
- proxyWindow = iface->proxyWindow();
- }
- }
+ auto* proxyWindow = ProxyWindowBase::forObject(window);
if (proxyWindow != this->rectWindow) {
if (this->rectWindow != nullptr) {
diff --git a/src/wayland/windowmanager/windowset.cpp b/src/wayland/windowmanager/windowset.cpp
index 796cfe2..74e273d 100644
--- a/src/wayland/windowmanager/windowset.cpp
+++ b/src/wayland/windowmanager/windowset.cpp
@@ -8,9 +8,9 @@
#include
#include
+#include "../../windowmanager/screenprojection.hpp"
#include "../../windowmanager/windowmanager.hpp"
#include "../../windowmanager/windowset.hpp"
-#include "../../windowmanager/screenprojection.hpp"
#include "ext_workspace.hpp"
namespace qs::wm::wayland {
diff --git a/src/wayland/wl_proxy_safe_deref.cpp b/src/wayland/wl_proxy_safe_deref.cpp
new file mode 100644
index 0000000..2664a99
--- /dev/null
+++ b/src/wayland/wl_proxy_safe_deref.cpp
@@ -0,0 +1,41 @@
+#include
+#include
+#include
+#include
+#include
+
+#include "../core/logcat.hpp"
+
+namespace {
+QS_LOGGING_CATEGORY(logDeref, "quickshell.wayland.safederef", QtWarningMsg);
+using wl_proxy_get_listener_t = const void* (*) (wl_proxy*);
+wl_proxy_get_listener_t original_wl_proxy_get_listener = nullptr; // NOLINT
+} // namespace
+
+extern "C" {
+WL_EXPORT const void* wl_proxy_get_listener(struct wl_proxy* proxy) {
+ // Avoid null derefs of protocol objects in qtbase.
+ // https://qt-project.atlassian.net/browse/QTBUG-145022
+ if (!proxy) [[unlikely]] {
+ qCCritical(logDeref) << "wl_proxy_get_listener called with a null proxy!";
+ return nullptr;
+ }
+
+ return original_wl_proxy_get_listener(proxy);
+}
+}
+
+// NOLINTBEGIN (concurrency-mt-unsafe)
+void installWlProxySafeDeref() {
+ dlerror(); // clear old errors
+
+ original_wl_proxy_get_listener =
+ reinterpret_cast(dlsym(RTLD_NEXT, "wl_proxy_get_listener"));
+
+ if (auto* error = dlerror()) {
+ qCCritical(logDeref) << "Failed to find wl_proxy_get_listener for hooking:" << error;
+ } else {
+ qCInfo(logDeref) << "Installed wl_proxy_get_listener hook.";
+ }
+}
+// NOLINTEND
diff --git a/src/wayland/wlr_layershell/surface.cpp b/src/wayland/wlr_layershell/surface.cpp
index 4a5015e..0b0e7d7 100644
--- a/src/wayland/wlr_layershell/surface.cpp
+++ b/src/wayland/wlr_layershell/surface.cpp
@@ -17,6 +17,7 @@
#include
#include
#include
+#include
#include "../../window/panelinterface.hpp"
#include "shell_integration.hpp"
@@ -247,9 +248,19 @@ void LayerSurface::commit() {
}
void LayerSurface::attachPopup(QtWaylandClient::QWaylandShellSurface* popup) {
- std::any role = popup->surfaceRole();
-
- if (auto* popupRole = std::any_cast<::xdg_popup*>(&role)) { // NOLINT
+#ifdef __FreeBSD__
+ // FreeBSD uses an alternate RTTI matching strategy by default which does
+ // not work across modules, preventing std::any from downcasting. On
+ // FreeBSD, Qt is built with a patch to expose the surface role through a
+ // pointer instead of an any, which does not have this problem.
+ // See https://bugs.kde.org/show_bug.cgi?id=479679
+ if (auto* xdgPopup = static_cast<::xdg_popup*>(popup->nativeResource("xdg_popup"))) {
+ this->get_popup(xdgPopup);
+ return;
+ }
+#endif
+ auto role = popup->surfaceRole(); // NOLINT
+ if (auto* popupRole = std::any_cast<::xdg_popup*>(&role)) {
this->get_popup(*popupRole);
} else {
qWarning() << "Cannot attach popup" << popup << "to shell surface" << this
diff --git a/src/window/proxywindow.cpp b/src/window/proxywindow.cpp
index 62126bd..8a20dfa 100644
--- a/src/window/proxywindow.cpp
+++ b/src/window/proxywindow.cpp
@@ -57,6 +57,12 @@ ProxyWindowBase::ProxyWindowBase(QObject* parent)
ProxyWindowBase::~ProxyWindowBase() { this->deleteWindow(true); }
+ProxyWindowBase* ProxyWindowBase::forObject(QObject* obj) {
+ if (auto* proxy = qobject_cast(obj)) return proxy;
+ if (auto* iface = qobject_cast(obj)) return iface->proxyWindow();
+ return nullptr;
+}
+
void ProxyWindowBase::onReload(QObject* oldInstance) {
if (this->mVisible) this->window = this->retrieveWindow(oldInstance);
auto wasVisible = this->window != nullptr && this->window->isVisible();
diff --git a/src/window/proxywindow.hpp b/src/window/proxywindow.hpp
index aec821e..9ff66c4 100644
--- a/src/window/proxywindow.hpp
+++ b/src/window/proxywindow.hpp
@@ -66,6 +66,8 @@ public:
explicit ProxyWindowBase(QObject* parent = nullptr);
~ProxyWindowBase() override;
+ static ProxyWindowBase* forObject(QObject* obj);
+
ProxyWindowBase(ProxyWindowBase&) = delete;
ProxyWindowBase(ProxyWindowBase&&) = delete;
void operator=(ProxyWindowBase&) = delete;