From d3bfec13cc9a1880221db14b9d8e9f43fbaf3c02 Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 4 Jul 2026 03:13:16 +0200 Subject: [PATCH] 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. --- src/modules/keyboard_state.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/modules/keyboard_state.cpp b/src/modules/keyboard_state.cpp index 7e43c74c..458cf0af 100644 --- a/src/modules/keyboard_state.cpp +++ b/src/modules/keyboard_state.cpp @@ -268,10 +268,16 @@ waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Bar& std::lock_guard lock(devices_mutex_); auto it = libinput_devices_.find(dev_path); if (it != libinput_devices_.end()) { - spdlog::info("Keyboard {} has been removed.", dev_path); - libinput_path_remove_device(it->second); - libinput_device_unref(it->second); + struct libinput_device* device = it->second; + // Erase from the map first so that a second IN_DELETE event for the + // 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); + spdlog::info("Keyboard {} has been removed.", dev_path); + libinput_path_remove_device(device); + libinput_device_unref(device); } } i += sizeof(struct inotify_event) + event->len;