fix(hyprland/window): avoid stale state during IPC refresh
The window module re-entered the same shared_mutex while refreshing IPC state: update() took the lock and then called queryActiveWorkspace(), which tried to lock it again. That is undefined behavior for std::shared_mutex and could manifest as a deadlock. Remove the recursive lock path and reset the derived window state before each IPC refresh. That keeps solo/floating/swallowing/fullscreen classes from sticking around when the client lookup fails or a workspace becomes empty. Signed-off-by: Austin Horstman <khaneliman12@gmail.com>
This commit is contained in:
@@ -20,8 +20,8 @@ class Window : public waybar::AAppIconLabel, public EventHandler {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
struct Workspace {
|
struct Workspace {
|
||||||
int id;
|
int id = 0;
|
||||||
int windows;
|
int windows = 0;
|
||||||
std::string last_window;
|
std::string last_window;
|
||||||
std::string last_window_title;
|
std::string last_window_title;
|
||||||
|
|
||||||
@@ -29,14 +29,14 @@ class Window : public waybar::AAppIconLabel, public EventHandler {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct WindowData {
|
struct WindowData {
|
||||||
bool floating;
|
bool floating = false;
|
||||||
int monitor = -1;
|
int monitor = -1;
|
||||||
std::string class_name;
|
std::string class_name;
|
||||||
std::string initial_class_name;
|
std::string initial_class_name;
|
||||||
std::string title;
|
std::string title;
|
||||||
std::string initial_title;
|
std::string initial_title;
|
||||||
bool fullscreen;
|
bool fullscreen = false;
|
||||||
bool grouped;
|
bool grouped = false;
|
||||||
|
|
||||||
static auto parse(const Json::Value&) -> WindowData;
|
static auto parse(const Json::Value&) -> WindowData;
|
||||||
};
|
};
|
||||||
@@ -47,7 +47,7 @@ class Window : public waybar::AAppIconLabel, public EventHandler {
|
|||||||
void queryActiveWorkspace();
|
void queryActiveWorkspace();
|
||||||
void setClass(const std::string&, bool enable);
|
void setClass(const std::string&, bool enable);
|
||||||
|
|
||||||
bool separateOutputs_;
|
bool separateOutputs_ = false;
|
||||||
std::mutex mutex_;
|
std::mutex mutex_;
|
||||||
const Bar& bar_;
|
const Bar& bar_;
|
||||||
util::JsonParser parser_;
|
util::JsonParser parser_;
|
||||||
@@ -55,11 +55,11 @@ class Window : public waybar::AAppIconLabel, public EventHandler {
|
|||||||
Workspace workspace_;
|
Workspace workspace_;
|
||||||
std::string soloClass_;
|
std::string soloClass_;
|
||||||
std::string lastSoloClass_;
|
std::string lastSoloClass_;
|
||||||
bool solo_;
|
bool solo_ = false;
|
||||||
bool allFloating_;
|
bool allFloating_ = false;
|
||||||
bool swallowing_;
|
bool swallowing_ = false;
|
||||||
bool fullscreen_;
|
bool fullscreen_ = false;
|
||||||
bool focused_;
|
bool focused_ = false;
|
||||||
|
|
||||||
IPC& m_ipc;
|
IPC& m_ipc;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ Window::Window(const std::string& id, const Bar& bar, const Json::Value& config)
|
|||||||
|
|
||||||
windowIpcUniqueLock.unlock();
|
windowIpcUniqueLock.unlock();
|
||||||
|
|
||||||
queryActiveWorkspace();
|
|
||||||
update();
|
update();
|
||||||
dp.emit();
|
dp.emit();
|
||||||
}
|
}
|
||||||
@@ -177,66 +176,65 @@ auto Window::WindowData::parse(const Json::Value& value) -> Window::WindowData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Window::queryActiveWorkspace() {
|
void Window::queryActiveWorkspace() {
|
||||||
std::shared_lock<std::shared_mutex> windowIpcShareLock(windowIpcSmtx);
|
|
||||||
|
|
||||||
if (separateOutputs_) {
|
if (separateOutputs_) {
|
||||||
workspace_ = getActiveWorkspace(this->bar_.output->name);
|
workspace_ = getActiveWorkspace(this->bar_.output->name);
|
||||||
} else {
|
} else {
|
||||||
workspace_ = getActiveWorkspace();
|
workspace_ = getActiveWorkspace();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
focused_ = false;
|
||||||
|
windowData_ = WindowData{};
|
||||||
|
allFloating_ = false;
|
||||||
|
swallowing_ = false;
|
||||||
|
fullscreen_ = false;
|
||||||
|
solo_ = false;
|
||||||
|
soloClass_.clear();
|
||||||
|
|
||||||
|
if (workspace_.windows <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto clients = m_ipc.getSocket1JsonReply("clients");
|
||||||
|
if (!clients.isArray()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto activeWindow = std::ranges::find_if(clients, [&](const Json::Value& window) {
|
||||||
|
return window["address"] == workspace_.last_window;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (activeWindow == std::end(clients)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
focused_ = true;
|
focused_ = true;
|
||||||
if (workspace_.windows > 0) {
|
windowData_ = WindowData::parse(*activeWindow);
|
||||||
const auto clients = m_ipc.getSocket1JsonReply("clients");
|
updateAppIconName(windowData_.class_name, windowData_.initial_class_name);
|
||||||
if (clients.isArray()) {
|
std::vector<Json::Value> workspaceWindows;
|
||||||
auto activeWindow = std::ranges::find_if(clients, [&](const Json::Value& window) {
|
std::ranges::copy_if(
|
||||||
return window["address"] == workspace_.last_window;
|
clients, std::back_inserter(workspaceWindows), [&](const Json::Value& window) {
|
||||||
|
return window["workspace"]["id"] == workspace_.id && window["mapped"].asBool();
|
||||||
});
|
});
|
||||||
|
swallowing_ = std::ranges::any_of(workspaceWindows, [&](const Json::Value& window) {
|
||||||
|
return !window["swallowing"].isNull() && window["swallowing"].asString() != "0x0";
|
||||||
|
});
|
||||||
|
std::vector<Json::Value> visibleWindows;
|
||||||
|
std::ranges::copy_if(workspaceWindows, std::back_inserter(visibleWindows),
|
||||||
|
[&](const Json::Value& window) { return !window["hidden"].asBool(); });
|
||||||
|
solo_ = 1 == std::count_if(
|
||||||
|
visibleWindows.begin(), visibleWindows.end(),
|
||||||
|
[&](const Json::Value& window) { return !window["floating"].asBool(); });
|
||||||
|
allFloating_ = std::ranges::all_of(
|
||||||
|
visibleWindows, [&](const Json::Value& window) { return window["floating"].asBool(); });
|
||||||
|
fullscreen_ = windowData_.fullscreen;
|
||||||
|
|
||||||
if (activeWindow == std::end(clients)) {
|
// Fullscreen windows look like they are solo
|
||||||
focused_ = false;
|
if (fullscreen_) {
|
||||||
return;
|
solo_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
windowData_ = WindowData::parse(*activeWindow);
|
if (solo_) {
|
||||||
updateAppIconName(windowData_.class_name, windowData_.initial_class_name);
|
soloClass_ = windowData_.class_name;
|
||||||
std::vector<Json::Value> workspaceWindows;
|
|
||||||
std::ranges::copy_if(
|
|
||||||
clients, std::back_inserter(workspaceWindows), [&](const Json::Value& window) {
|
|
||||||
return window["workspace"]["id"] == workspace_.id && window["mapped"].asBool();
|
|
||||||
});
|
|
||||||
swallowing_ = std::ranges::any_of(workspaceWindows, [&](const Json::Value& window) {
|
|
||||||
return !window["swallowing"].isNull() && window["swallowing"].asString() != "0x0";
|
|
||||||
});
|
|
||||||
std::vector<Json::Value> visibleWindows;
|
|
||||||
std::ranges::copy_if(workspaceWindows, std::back_inserter(visibleWindows),
|
|
||||||
[&](const Json::Value& window) { return !window["hidden"].asBool(); });
|
|
||||||
solo_ = 1 == std::count_if(
|
|
||||||
visibleWindows.begin(), visibleWindows.end(),
|
|
||||||
[&](const Json::Value& window) { return !window["floating"].asBool(); });
|
|
||||||
allFloating_ = std::ranges::all_of(
|
|
||||||
visibleWindows, [&](const Json::Value& window) { return window["floating"].asBool(); });
|
|
||||||
fullscreen_ = windowData_.fullscreen;
|
|
||||||
|
|
||||||
// Fullscreen windows look like they are solo
|
|
||||||
if (fullscreen_) {
|
|
||||||
solo_ = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (solo_) {
|
|
||||||
soloClass_ = windowData_.class_name;
|
|
||||||
} else {
|
|
||||||
soloClass_ = "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
focused_ = false;
|
|
||||||
windowData_ = WindowData{};
|
|
||||||
allFloating_ = false;
|
|
||||||
swallowing_ = false;
|
|
||||||
fullscreen_ = false;
|
|
||||||
solo_ = false;
|
|
||||||
soloClass_ = "";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user