fix(hyprland/language): apply the CSS class on the main thread

onEvent() runs on the Hyprland IPC listener thread and mutated the label's
style context (add/remove class) directly, racing the GTK main thread's
drawing and corrupting the heap (double free / corrupted double-linked list).
Follow the Submap pattern: onEvent only stores the layout under the mutex and
emits the dispatcher; update() swaps the CSS class on the main thread, tracking
the previously applied class.

Fixes #4665
This commit is contained in:
Alex
2026-07-05 23:06:37 +02:00
parent 6c1a0fa185
commit 6294ed2520
2 changed files with 14 additions and 15 deletions
+1 -2
View File
@@ -30,8 +30,6 @@ class Language : public waybar::ALabel, public EventHandler {
std::string short_description; std::string short_description;
}; };
auto removeXkbLayoutCssClass() -> void;
auto addXkbLayoutCssClass() -> void;
static auto getLayout(const std::string&) -> Layout; static auto getLayout(const std::string&) -> Layout;
std::mutex mutex_; std::mutex mutex_;
@@ -39,6 +37,7 @@ class Language : public waybar::ALabel, public EventHandler {
util::JsonParser parser_; util::JsonParser parser_;
Layout layout_; Layout layout_;
std::string prev_short_name_; // applied CSS class; touched only in update() (#4665)
IPC& m_ipc; IPC& m_ipc;
}; };
+13 -13
View File
@@ -30,6 +30,18 @@ Language::~Language() {
auto Language::update() -> void { auto Language::update() -> void {
std::lock_guard<std::mutex> lg(mutex_); std::lock_guard<std::mutex> lg(mutex_);
// Swap the layout CSS class here (main thread). onEvent() runs on the IPC
// thread and must never touch GTK -- that race corrupts the heap (#4665).
if (prev_short_name_ != layout_.short_name) {
if (!prev_short_name_.empty()) {
label_.get_style_context()->remove_class(prev_short_name_);
}
if (!layout_.short_name.empty()) {
label_.get_style_context()->add_class(layout_.short_name);
}
prev_short_name_ = layout_.short_name;
}
spdlog::debug("hyprland language update with full name {}", layout_.full_name); spdlog::debug("hyprland language update with full name {}", layout_.full_name);
spdlog::debug("hyprland language update with short name {}", layout_.short_name); spdlog::debug("hyprland language update with short name {}", layout_.short_name);
spdlog::debug("hyprland language update with short description {}", layout_.short_description); spdlog::debug("hyprland language update with short description {}", layout_.short_description);
@@ -143,9 +155,8 @@ void Language::onEvent(const std::string& ev) {
layoutName = waybar::util::sanitize_string(layoutName); layoutName = waybar::util::sanitize_string(layoutName);
removeXkbLayoutCssClass(); // CSS class swap happens in update() on the main thread (#4665).
layout_ = getLayout(layoutName); layout_ = getLayout(layoutName);
addXkbLayoutCssClass();
spdlog::debug("hyprland language onevent with {}", layoutName); spdlog::debug("hyprland language onevent with {}", layoutName);
@@ -167,7 +178,6 @@ void Language::initLanguage() {
searcher = waybar::util::sanitize_string(searcher); searcher = waybar::util::sanitize_string(searcher);
layout_ = getLayout(searcher); layout_ = getLayout(searcher);
addXkbLayoutCssClass();
spdlog::debug("hyprland language initLanguage found {}", layout_.full_name); spdlog::debug("hyprland language initLanguage found {}", layout_.full_name);
@@ -177,16 +187,6 @@ void Language::initLanguage() {
} }
} }
auto Language::removeXkbLayoutCssClass() -> void {
label_.get_style_context()->remove_class(layout_.short_name);
spdlog::debug("hyprland language try to remove currently short_name css class {}",
layout_.short_name);
}
auto Language::addXkbLayoutCssClass() -> void {
label_.get_style_context()->add_class(layout_.short_name);
spdlog::debug("hyprland language add new short_name css class {}", layout_.short_name);
}
auto Language::getLayout(const std::string& fullName) -> Layout { auto Language::getLayout(const std::string& fullName) -> Layout {
auto* const context = rxkb_context_new(RXKB_CONTEXT_LOAD_EXOTIC_RULES); auto* const context = rxkb_context_new(RXKB_CONTEXT_LOAD_EXOTIC_RULES);
rxkb_context_parse_default_ruleset(context); rxkb_context_parse_default_ruleset(context);