fix: backward-compat + bug fixes and man-page build fix for 0.16.0
Post-0.15.0 review of the 0.15.0..HEAD range surfaced regressions and
bugs. This restores backward compatibility for existing configs/CSS,
fixes confirmed defects, and repairs the scdoc man-page build break on
master. Pango-markup tooltips are intentional and were kept.
Backward-compat restorations:
- AModule: honor legacy numeric Gdk::CursorType cursor values (int overload)
- memory: correct GiB divisor (was ~2.3% low); round bare {} placeholders
- wireplumber: scale max-volume into the linear domain so the cap works again
- idle_inhibitor: gate right/middle-click deactivate & scroll on dynamic-timeouts;
accept both dynamic-timeout(s); widen timeout to double (no fractional truncation)
- custom: keep #custom-<name>.<class> CSS selectors working (classes on box_)
- image: don't wordexp-split a single path; fall back to the literal path
- niri/window: restore hide-when-empty (new show-empty opt-in); escape tooltip
- wlr/taskbar: plain-text tooltip when markup is disabled
Bug fixes:
- tray: fix use-after-free in onAdd; guard the watcher retry timeout
- hyprland: clamp max-windows iterator (OOB); drop duplicate language tooltip block
- niri/window: supply {col}/{max_col} args in the empty branch (fmt::format_error)
- mpris: escape {dynamic}/{player} tooltip; fix dangling player; albumArtist source
- mango: fix use-after-free race (dispatch under callback_mutex_)
- mpd: contain throwing checkErrors in noexcept idle paths (no std::terminate/UAF)
- keyboard_state: always render every lock label, with guarded defaults
- bluetooth: bound GATT ReadValue timeout, opt-in + services-resolved gating,
preserve authoritative Battery1 percentage
- wireplumber: fix WpDevice reference leak / NULL handling
- battery, clock, dwl, wayfire, graph, custom_graph, transform, river: assorted
crash/logic fixes
Man page / build:
- niri-workspaces: fix scdoc "indented by an amount greater than 1"
(workspace-taskbar sub-options were mis-indented; breaks man-page build)
- document new show-empty (niri/window); correct network {txBitrate}/{rxBitrate}
Not compiled locally (no gtkmm on this host); C++ build relies on CI.
Man pages validated with scdoc 1.11.4.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -87,14 +87,29 @@ auto isChildPath(const std::string& child, const std::string& parent) -> bool {
|
||||
return child.starts_with(parent);
|
||||
}
|
||||
|
||||
// Returns true only if some configured format/tooltip string actually references the peripheral
|
||||
// battery placeholder. The GATT battery scan issues over-the-air BLE reads, so it must stay
|
||||
// opt-in: users who don't display {device_battery_percentage_peripheral} pay zero cost.
|
||||
auto configUsesPeripheralBattery(const Json::Value& config) -> bool {
|
||||
for (const auto& key : config.getMemberNames()) {
|
||||
if (config[key].isString() &&
|
||||
config[key].asString().find("device_battery_percentage_peripheral") != std::string::npos) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
auto readBatteryCharacteristicValue(GDBusProxy* proxy_char) -> std::optional<unsigned char> {
|
||||
GVariantBuilder builder;
|
||||
g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}"));
|
||||
|
||||
GError* error = nullptr;
|
||||
// Use a small finite timeout (2s) instead of the default (-1 == 25s) so a sleeping or
|
||||
// dropped BLE peripheral cannot stall the Waybar main thread for ~25s on a synchronous read.
|
||||
GVariant* gvar =
|
||||
g_dbus_proxy_call_sync(proxy_char, "ReadValue", g_variant_new("(a{sv})", &builder),
|
||||
G_DBUS_CALL_FLAGS_NONE, -1, nullptr, &error);
|
||||
G_DBUS_CALL_FLAGS_NONE, 2000, nullptr, &error);
|
||||
if (error != nullptr) {
|
||||
g_error_free(error);
|
||||
return std::nullopt;
|
||||
@@ -538,7 +553,9 @@ auto waybar::modules::Bluetooth::processBatteryServiceCharacteristics(
|
||||
|
||||
if (hasUserDescriptionDescriptor(objects, char_path, user_description_uuid)) {
|
||||
peripheral_battery = battery_value.value();
|
||||
} else {
|
||||
} else if (!central_battery.has_value()) {
|
||||
// Only fill the central sink once so multiple non-described 0x2a19 characteristics don't
|
||||
// clobber each other (order-dependent last-writer-wins) or an already-set Battery1 value.
|
||||
central_battery = battery_value.value();
|
||||
}
|
||||
}
|
||||
@@ -564,8 +581,20 @@ auto waybar::modules::Bluetooth::getDeviceProperties(GDBusObject* object, Device
|
||||
g_object_unref(proxy_device);
|
||||
|
||||
device_info.battery_percentage = getDeviceBatteryPercentage(object);
|
||||
getDeviceGattBatteryLevels(object, device_info.battery_percentage,
|
||||
device_info.battery_percentage_peripheral);
|
||||
// Only perform the (synchronous, over-the-air) GATT battery scan when the peripheral battery
|
||||
// placeholder is actually used and the device's services are resolved. This keeps the scan off
|
||||
// the frequent property-changed hot path (RSSI/TxPower updates leave ServicesResolved false or
|
||||
// unchanged) and opt-in for split-keyboard style peripherals.
|
||||
if (device_info.services_resolved && configUsesPeripheralBattery(config_)) {
|
||||
// Read the GATT central level into a separate local; only fall back to it when the
|
||||
// authoritative org.bluez.Battery1 percentage is absent, so the GATT scan can never overwrite
|
||||
// the Battery1 value used by {device_battery_percentage}.
|
||||
std::optional<unsigned char> gatt_central;
|
||||
getDeviceGattBatteryLevels(object, gatt_central, device_info.battery_percentage_peripheral);
|
||||
if (!device_info.battery_percentage.has_value()) {
|
||||
device_info.battery_percentage = gatt_central;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user