Compare commits

...

8 commits

Author SHA1 Message Date
outfoxxed
0cb62920a7
hyprland/focus_grab: handle destruction of tracked windows 2026-03-18 02:39:22 -07:00
outfoxxed
3520c85d77
wayland: remove --require-defined linker argument
Not supported by lld
2026-03-17 19:42:47 -07:00
outfoxxed
3cf65af49f
docs: ask users not to submit v1 crash reports 2026-03-17 10:10:01 -07:00
-k
a51dcd0a01
wayland: use patched surfaceRole accessor on FreeBSD 2026-03-17 00:08:25 -07:00
-k
97b2688ad6
core/log: fix non-linux typo and import unistd on freebsd 2026-03-17 00:01:34 -07:00
-k
0a859d51f2
service/pam: include signal.h on freebsd 2026-03-17 00:01:24 -07:00
outfoxxed
1bd5b083cb
hyprland/ipc: add null checks and ws preinit to toplevel object init
Previously HyprlandToplevel::updateFromObject did not call
findWorkspaceByName with createIfMissing=true, leaving bWorkspace null
for a later insertToplevel call from HyprlandIpc::refreshToplevels.
2026-03-16 22:47:01 -07:00
outfoxxed
365bf16b1e
wayland: hook wl_proxy_get_listener avoiding QTBUG-145022 crash
Co-authored-by: Lemmy <studio@quadbyte.net>
2026-03-16 21:42:20 -07:00
22 changed files with 160 additions and 197 deletions

View file

@ -1,82 +1,17 @@
name: Crash Report (v1) name: Crash Report (v1)
description: Quickshell has crashed description: Quickshell has crashed (old)
labels: ["bug", "crash"] labels: ["unactionable"]
body: body:
- type: textarea - type: markdown
id: crashinfo
attributes: attributes:
label: General crash information value: |
description: | Thank you for taking the time to click the report button.
Paste the contents of the `info.txt` file in your crash folder here. At this point most of the worst issues in 0.2.1 and before have been fixed and we are
value: "<details> <summary>General information</summary> preparing for a new release. Please do not submit this report.
- type: checkboxes
id: donotcheck
``` attributes:
label: Read the text above. Do not submit the report.
<Paste the contents of the file here inside of the triple backticks> options:
- label: Yes I want this report to be deleted.
```
</details>"
validations:
required: true required: true
- type: textarea
id: userinfo
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 <path-to-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 <pid>` 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.

View file

@ -9,21 +9,21 @@ body:
description: | description: |
Any information likely to help debug the crash. What were you doing when the crash occurred, 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? what changes did you make, can you get it to happen again?
- type: textarea - type: upload
id: report id: report
attributes: attributes:
label: Report file label: Report file
description: Attach `report.txt` here. description: Attach `report.txt` here.
validations: validations:
required: true required: true
- type: textarea - type: upload
id: logs id: logs
attributes: attributes:
label: Log file label: Log file
description: | description: |
Attach `log.qslog.log` here. If it is too big to upload, compress it. 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 <path-to-log>`. You can preview the log if you'd like using `qs log <path-to-log> -r '*=true'`.
validations: validations:
required: true required: true
- type: textarea - type: textarea
@ -31,7 +31,7 @@ body:
attributes: attributes:
label: Configuration label: Configuration
description: | 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. Compress it into a zip, tar, etc.
This will help us reproduce the crash ourselves. This will help us reproduce the crash ourselves.

View file

@ -48,6 +48,7 @@ set shell id.
- Fixed volumes not initializing if a pipewire device was already loaded before its node. - 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 active toplevel not resetting after window closes.
- Fixed hyprland ipc window names and titles being reversed. - 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 missing signals for system tray item title and description updates.
- Fixed asynchronous loaders not working after reload. - Fixed asynchronous loaders not working after reload.
- Fixed asynchronous loaders not working before window creation. - Fixed asynchronous loaders not working before window creation.
@ -58,6 +59,8 @@ set shell id.
- Fixed ToplevelManager not clearing activeToplevel on deactivation. - Fixed ToplevelManager not clearing activeToplevel on deactivation.
- Desktop action order is now preserved. - Desktop action order is now preserved.
- Fixed partial socket reads in greetd and hyprland on slow machines. - 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 ## Packaging Changes

View file

@ -31,6 +31,9 @@
#include <sys/sendfile.h> #include <sys/sendfile.h>
#include <sys/types.h> #include <sys/types.h>
#endif #endif
#ifdef __FreeBSD__
#include <unistd.h>
#endif
#include "instanceinfo.hpp" #include "instanceinfo.hpp"
#include "logcat.hpp" #include "logcat.hpp"
@ -67,7 +70,7 @@ bool copyFileData(int sourceFd, int destFd, qint64 size) {
return true; return true;
#else #else
std::array<char, 64 * 1024> buffer = {}; std::array<char, 64 * 1024> buffer = {};
auto remaining = totalTarget; auto remaining = usize;
while (remaining > 0) { while (remaining > 0) {
auto chunk = std::min(remaining, buffer.size()); auto chunk = std::min(remaining, buffer.size());

View file

@ -18,7 +18,6 @@
#include <qwindow.h> #include <qwindow.h>
#include "../window/proxywindow.hpp" #include "../window/proxywindow.hpp"
#include "../window/windowinterface.hpp"
#include "iconprovider.hpp" #include "iconprovider.hpp"
#include "model.hpp" #include "model.hpp"
#include "platformmenu_p.hpp" #include "platformmenu_p.hpp"
@ -91,10 +90,8 @@ bool PlatformMenuEntry::display(QObject* parentWindow, int relativeX, int relati
} else if (parentWindow == nullptr) { } else if (parentWindow == nullptr) {
qCritical() << "Cannot display PlatformMenuEntry with null parent window."; qCritical() << "Cannot display PlatformMenuEntry with null parent window.";
return false; return false;
} else if (auto* proxy = qobject_cast<ProxyWindowBase*>(parentWindow)) { } else if (auto* proxy = ProxyWindowBase::forObject(parentWindow)) {
window = proxy->backingWindow(); window = proxy->backingWindow();
} else if (auto* interface = qobject_cast<WindowInterface*>(parentWindow)) {
window = interface->proxyWindow()->backingWindow();
} else { } else {
qCritical() << "PlatformMenuEntry.display() must be called with a window."; qCritical() << "PlatformMenuEntry.display() must be called with a window.";
return false; return false;

View file

@ -11,7 +11,6 @@
#include <qwindow.h> #include <qwindow.h>
#include "../window/proxywindow.hpp" #include "../window/proxywindow.hpp"
#include "../window/windowinterface.hpp"
#include "types.hpp" #include "types.hpp"
bool PopupAnchorState::operator==(const PopupAnchorState& other) const { bool PopupAnchorState::operator==(const PopupAnchorState& other) const {
@ -40,10 +39,8 @@ void PopupAnchor::setWindowInternal(QObject* window) {
} }
if (window) { if (window) {
if (auto* proxy = qobject_cast<ProxyWindowBase*>(window)) { if (auto* proxy = ProxyWindowBase::forObject(window)) {
this->bProxyWindow = proxy; this->bProxyWindow = proxy;
} else if (auto* interface = qobject_cast<WindowInterface*>(window)) {
this->bProxyWindow = interface->proxyWindow();
} else { } else {
qWarning() << "Tried to set popup anchor window to" << window qWarning() << "Tried to set popup anchor window to" << window
<< "which is not a quickshell window."; << "which is not a quickshell window.";

View file

@ -8,6 +8,9 @@
#include <qtmetamacros.h> #include <qtmetamacros.h>
#include <sys/signal.h> #include <sys/signal.h>
#include <sys/wait.h> #include <sys/wait.h>
#ifdef __FreeBSD__
#include <signal.h>
#endif
#include "../../core/logcat.hpp" #include "../../core/logcat.hpp"
#include "ipc.hpp" #include "ipc.hpp"

View file

@ -73,6 +73,7 @@ endfunction()
# ----- # -----
qt_add_library(quickshell-wayland STATIC qt_add_library(quickshell-wayland STATIC
wl_proxy_safe_deref.cpp
platformmenu.cpp platformmenu.cpp
popupanchor.cpp popupanchor.cpp
xdgshell.cpp xdgshell.cpp
@ -80,6 +81,10 @@ qt_add_library(quickshell-wayland STATIC
output_tracking.cpp 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 # required to make sure the constructor is linked
add_library(quickshell-wayland-init OBJECT init.cpp) add_library(quickshell-wayland-init OBJECT init.cpp)

View file

@ -9,7 +9,6 @@
#include <qwindow.h> #include <qwindow.h>
#include "../../../window/proxywindow.hpp" #include "../../../window/proxywindow.hpp"
#include "../../../window/windowinterface.hpp"
#include "grab.hpp" #include "grab.hpp"
#include "manager.hpp" #include "manager.hpp"
@ -38,8 +37,51 @@ QObjectList HyprlandFocusGrab::windows() const { return this->windowObjects; }
void HyprlandFocusGrab::setWindows(QObjectList windows) { void HyprlandFocusGrab::setWindows(QObjectList windows) {
if (windows == this->windowObjects) return; 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->windowObjects = std::move(windows);
this->syncWindows();
emit this->windowsChanged(); emit this->windowsChanged();
} }
@ -75,59 +117,18 @@ void HyprlandFocusGrab::tryActivate() {
QObject::connect(this->grab, &FocusGrab::cleared, this, &HyprlandFocusGrab::onGrabCleared); QObject::connect(this->grab, &FocusGrab::cleared, this, &HyprlandFocusGrab::onGrabCleared);
this->grab->startTransaction(); this->grab->startTransaction();
for (auto* proxy: this->trackedProxies) { for (auto* obj: this->windowObjects) {
if (proxy->backingWindow() != nullptr) { auto* proxy = ProxyWindowBase::forObject(obj);
if (proxy && proxy->backingWindow()) {
this->grab->addWindow(proxy->backingWindow()); this->grab->addWindow(proxy->backingWindow());
} }
} }
this->grab->completeTransaction(); this->grab->completeTransaction();
} }
void HyprlandFocusGrab::syncWindows() { void HyprlandFocusGrab::onObjectDestroyed(QObject* object) {
auto newProxy = QList<ProxyWindowBase*>(); this->windowObjects.removeOne(object);
for (auto* windowObject: this->windowObjects) { emit this->windowsChanged();
auto* proxyWindow = qobject_cast<ProxyWindowBase*>(windowObject);
if (proxyWindow == nullptr) {
if (auto* iface = qobject_cast<WindowInterface*>(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();
} }
} // namespace qs::hyprland } // namespace qs::hyprland

View file

@ -96,15 +96,13 @@ private slots:
void onGrabActivated(); void onGrabActivated();
void onGrabCleared(); void onGrabCleared();
void onProxyConnected(); void onProxyConnected();
void onObjectDestroyed(QObject* object);
private: private:
void tryActivate(); void tryActivate();
void syncWindows();
bool targetActive = false; bool targetActive = false;
QObjectList windowObjects; QObjectList windowObjects;
QList<ProxyWindowBase*> trackedProxies;
QList<QWindow*> trackedWindows;
focus_grab::FocusGrab* grab = nullptr; focus_grab::FocusGrab* grab = nullptr;
}; };

View file

@ -729,7 +729,7 @@ void HyprlandIpc::refreshToplevels() {
} }
auto* workspace = toplevel->bindableWorkspace().value(); auto* workspace = toplevel->bindableWorkspace().value();
workspace->insertToplevel(toplevel); if (workspace) workspace->insertToplevel(toplevel);
} }
}); });
} }

View file

@ -72,20 +72,16 @@ void HyprlandToplevel::updateFromObject(const QVariantMap& object) {
Qt::beginPropertyUpdateGroup(); Qt::beginPropertyUpdateGroup();
bool ok = false; bool ok = false;
auto address = addressStr.toULongLong(&ok, 16); auto address = addressStr.toULongLong(&ok, 16);
if (!ok || !address) { if (ok && address) this->setAddress(address);
return;
}
this->setAddress(address);
this->bTitle = title; this->bTitle = title;
auto workspaceMap = object.value("workspace").toMap(); auto workspaceMap = object.value("workspace").toMap();
auto workspaceName = workspaceMap.value("name").toString(); auto workspaceName = workspaceMap.value("name").toString();
auto* workspace = this->ipc->findWorkspaceByName(workspaceName, false); auto* workspace = this->ipc->findWorkspaceByName(workspaceName, true);
if (!workspace) return; if (workspace) this->setWorkspace(workspace);
this->setWorkspace(workspace);
this->bLastIpcObject = object; this->bLastIpcObject = object;
Qt::endPropertyUpdateGroup(); Qt::endPropertyUpdateGroup();
} }

View file

@ -14,7 +14,6 @@
#include "../../../core/region.hpp" #include "../../../core/region.hpp"
#include "../../../window/proxywindow.hpp" #include "../../../window/proxywindow.hpp"
#include "../../../window/windowinterface.hpp"
#include "manager.hpp" #include "manager.hpp"
#include "surface.hpp" #include "surface.hpp"
@ -23,13 +22,7 @@ using QtWaylandClient::QWaylandWindow;
namespace qs::hyprland::surface { namespace qs::hyprland::surface {
HyprlandWindow* HyprlandWindow::qmlAttachedProperties(QObject* object) { HyprlandWindow* HyprlandWindow::qmlAttachedProperties(QObject* object) {
auto* proxyWindow = qobject_cast<ProxyWindowBase*>(object); auto* proxyWindow = ProxyWindowBase::forObject(object);
if (!proxyWindow) {
if (auto* iface = qobject_cast<WindowInterface*>(object)) {
proxyWindow = iface->proxyWindow();
}
}
if (!proxyWindow) return nullptr; if (!proxyWindow) return nullptr;
return new HyprlandWindow(proxyWindow); return new HyprlandWindow(proxyWindow);

View file

@ -6,7 +6,6 @@
#include <qtmetamacros.h> #include <qtmetamacros.h>
#include "../../window/proxywindow.hpp" #include "../../window/proxywindow.hpp"
#include "../../window/windowinterface.hpp"
#include "proto.hpp" #include "proto.hpp"
namespace qs::wayland::idle_inhibit { namespace qs::wayland::idle_inhibit {
@ -25,27 +24,13 @@ QObject* IdleInhibitor::window() const { return this->bWindowObject; }
void IdleInhibitor::setWindow(QObject* window) { void IdleInhibitor::setWindow(QObject* window) {
if (window == this->bWindowObject) return; if (window == this->bWindowObject) return;
auto* proxyWindow = qobject_cast<ProxyWindowBase*>(window); auto* proxyWindow = ProxyWindowBase::forObject(window);
if (proxyWindow == nullptr) {
if (auto* iface = qobject_cast<WindowInterface*>(window)) {
proxyWindow = iface->proxyWindow();
}
}
this->bWindowObject = proxyWindow ? window : nullptr; this->bWindowObject = proxyWindow ? window : nullptr;
} }
void IdleInhibitor::boundWindowChanged() { void IdleInhibitor::boundWindowChanged() {
auto* window = this->bBoundWindow.value(); auto* window = this->bBoundWindow.value();
auto* proxyWindow = qobject_cast<ProxyWindowBase*>(window); auto* proxyWindow = ProxyWindowBase::forObject(window);
if (proxyWindow == nullptr) {
if (auto* iface = qobject_cast<WindowInterface*>(window)) {
proxyWindow = iface->proxyWindow();
}
}
if (proxyWindow == this->proxyWindow) return; if (proxyWindow == this->proxyWindow) return;
if (this->mWaylandWindow) { if (this->mWaylandWindow) {

View file

@ -10,6 +10,7 @@
#include "wlr_layershell/wlr_layershell.hpp" #include "wlr_layershell/wlr_layershell.hpp"
#endif #endif
void installWlProxySafeDeref(); // NOLINT(misc-use-internal-linkage)
void installPlatformMenuHook(); // NOLINT(misc-use-internal-linkage) void installPlatformMenuHook(); // NOLINT(misc-use-internal-linkage)
void installPopupPositioner(); // NOLINT(misc-use-internal-linkage) void installPopupPositioner(); // NOLINT(misc-use-internal-linkage)
@ -33,6 +34,7 @@ class WaylandPlugin: public QsEnginePlugin {
} }
void init() override { void init() override {
installWlProxySafeDeref();
installPlatformMenuHook(); installPlatformMenuHook();
installPopupPositioner(); installPopupPositioner();
} }

View file

@ -9,7 +9,6 @@
#include <qwindow.h> #include <qwindow.h>
#include "../../window/proxywindow.hpp" #include "../../window/proxywindow.hpp"
#include "../../window/windowinterface.hpp"
#include "proto.hpp" #include "proto.hpp"
namespace qs::wayland::shortcuts_inhibit { namespace qs::wayland::shortcuts_inhibit {
@ -48,14 +47,7 @@ ShortcutInhibitor::~ShortcutInhibitor() {
void ShortcutInhibitor::onBoundWindowChanged() { void ShortcutInhibitor::onBoundWindowChanged() {
auto* window = this->bBoundWindow.value(); auto* window = this->bBoundWindow.value();
auto* proxyWindow = qobject_cast<ProxyWindowBase*>(window); auto* proxyWindow = ProxyWindowBase::forObject(window);
if (!proxyWindow) {
if (auto* iface = qobject_cast<WindowInterface*>(window)) {
proxyWindow = iface->proxyWindow();
}
}
if (proxyWindow == this->proxyWindow) return; if (proxyWindow == this->proxyWindow) return;
if (this->proxyWindow) { if (this->proxyWindow) {

View file

@ -9,7 +9,6 @@
#include "../../core/qmlscreen.hpp" #include "../../core/qmlscreen.hpp"
#include "../../core/util.hpp" #include "../../core/util.hpp"
#include "../../window/proxywindow.hpp" #include "../../window/proxywindow.hpp"
#include "../../window/windowinterface.hpp"
#include "../output_tracking.hpp" #include "../output_tracking.hpp"
#include "handle.hpp" #include "handle.hpp"
#include "manager.hpp" #include "manager.hpp"
@ -73,13 +72,7 @@ void Toplevel::fullscreenOn(QuickshellScreenInfo* screen) {
} }
void Toplevel::setRectangle(QObject* window, QRect rect) { void Toplevel::setRectangle(QObject* window, QRect rect) {
auto* proxyWindow = qobject_cast<ProxyWindowBase*>(window); auto* proxyWindow = ProxyWindowBase::forObject(window);
if (proxyWindow == nullptr) {
if (auto* iface = qobject_cast<WindowInterface*>(window)) {
proxyWindow = iface->proxyWindow();
}
}
if (proxyWindow != this->rectWindow) { if (proxyWindow != this->rectWindow) {
if (this->rectWindow != nullptr) { if (this->rectWindow != nullptr) {

View file

@ -8,9 +8,9 @@
#include <qobjectdefs.h> #include <qobjectdefs.h>
#include <qproperty.h> #include <qproperty.h>
#include "../../windowmanager/screenprojection.hpp"
#include "../../windowmanager/windowmanager.hpp" #include "../../windowmanager/windowmanager.hpp"
#include "../../windowmanager/windowset.hpp" #include "../../windowmanager/windowset.hpp"
#include "../../windowmanager/screenprojection.hpp"
#include "ext_workspace.hpp" #include "ext_workspace.hpp"
namespace qs::wm::wayland { namespace qs::wm::wayland {

View file

@ -0,0 +1,41 @@
#include <dlfcn.h>
#include <qlogging.h>
#include <qloggingcategory.h>
#include <wayland-client-core.h>
#include <wayland-util.h>
#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<wl_proxy_get_listener_t>(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

View file

@ -17,6 +17,7 @@
#include <qvariant.h> #include <qvariant.h>
#include <qwayland-wlr-layer-shell-unstable-v1.h> #include <qwayland-wlr-layer-shell-unstable-v1.h>
#include <qwindow.h> #include <qwindow.h>
#include <wayland-xdg-shell-client-protocol.h>
#include "../../window/panelinterface.hpp" #include "../../window/panelinterface.hpp"
#include "shell_integration.hpp" #include "shell_integration.hpp"
@ -247,9 +248,19 @@ void LayerSurface::commit() {
} }
void LayerSurface::attachPopup(QtWaylandClient::QWaylandShellSurface* popup) { void LayerSurface::attachPopup(QtWaylandClient::QWaylandShellSurface* popup) {
std::any role = popup->surfaceRole(); #ifdef __FreeBSD__
// FreeBSD uses an alternate RTTI matching strategy by default which does
if (auto* popupRole = std::any_cast<::xdg_popup*>(&role)) { // NOLINT // 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); this->get_popup(*popupRole);
} else { } else {
qWarning() << "Cannot attach popup" << popup << "to shell surface" << this qWarning() << "Cannot attach popup" << popup << "to shell surface" << this

View file

@ -57,6 +57,12 @@ ProxyWindowBase::ProxyWindowBase(QObject* parent)
ProxyWindowBase::~ProxyWindowBase() { this->deleteWindow(true); } ProxyWindowBase::~ProxyWindowBase() { this->deleteWindow(true); }
ProxyWindowBase* ProxyWindowBase::forObject(QObject* obj) {
if (auto* proxy = qobject_cast<ProxyWindowBase*>(obj)) return proxy;
if (auto* iface = qobject_cast<WindowInterface*>(obj)) return iface->proxyWindow();
return nullptr;
}
void ProxyWindowBase::onReload(QObject* oldInstance) { void ProxyWindowBase::onReload(QObject* oldInstance) {
if (this->mVisible) this->window = this->retrieveWindow(oldInstance); if (this->mVisible) this->window = this->retrieveWindow(oldInstance);
auto wasVisible = this->window != nullptr && this->window->isVisible(); auto wasVisible = this->window != nullptr && this->window->isVisible();

View file

@ -66,6 +66,8 @@ public:
explicit ProxyWindowBase(QObject* parent = nullptr); explicit ProxyWindowBase(QObject* parent = nullptr);
~ProxyWindowBase() override; ~ProxyWindowBase() override;
static ProxyWindowBase* forObject(QObject* obj);
ProxyWindowBase(ProxyWindowBase&) = delete; ProxyWindowBase(ProxyWindowBase&) = delete;
ProxyWindowBase(ProxyWindowBase&&) = delete; ProxyWindowBase(ProxyWindowBase&&) = delete;
void operator=(ProxyWindowBase&) = delete; void operator=(ProxyWindowBase&) = delete;