fix(keyboard-state): make device removal idempotent to stop libinput abort

On device unplug the inotify IN_DELETE handler removed the libinput
device and unref'd it before erasing the entry from libinput_devices_.
A repeated IN_DELETE event for the same path (observed as the
"has been removed" log line printed twice) could reach an
already-unlinked device and trigger a libinput list_remove assertion
abort.

Erase the map entry first (under devices_mutex_) so a second delete for
the same path is a no-op, then call libinput_path_remove_device() and
libinput_device_unref() exactly once per device pointer.

Fixes #5143, #4443, #4566.
This commit is contained in:
Alex
2026-07-04 03:14:13 +02:00
parent 4229dc8ac2
commit d3bfec13cc
+9 -3
View File
@@ -268,10 +268,16 @@ waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Bar&
std::lock_guard<std::mutex> lock(devices_mutex_); std::lock_guard<std::mutex> lock(devices_mutex_);
auto it = libinput_devices_.find(dev_path); auto it = libinput_devices_.find(dev_path);
if (it != libinput_devices_.end()) { if (it != libinput_devices_.end()) {
spdlog::info("Keyboard {} has been removed.", dev_path); struct libinput_device* device = it->second;
libinput_path_remove_device(it->second); // Erase from the map first so that a second IN_DELETE event for the
libinput_device_unref(it->second); // same path becomes a no-op. This keeps removal idempotent and
// ensures libinput_path_remove_device()/libinput_device_unref() are
// called exactly once per device, avoiding a libinput list_remove
// assertion abort on double removal.
libinput_devices_.erase(it); libinput_devices_.erase(it);
spdlog::info("Keyboard {} has been removed.", dev_path);
libinput_path_remove_device(device);
libinput_device_unref(device);
} }
} }
i += sizeof(struct inotify_event) + event->len; i += sizeof(struct inotify_event) + event->len;