fix(controlcenter): flush stale-cache races in toggle UI

- Drop rfkill from bluetooth toggle (redundant with D-Bus Powered,
  was causing stale cache on next read)
- Force toggle switch and icon to target state immediately after
  setting, don't wait for D-Bus callback
- Fix nightlight icon race: flip local is_active instead of
  re-querying async kill state
- Guard wifi on_network_ready against ethernet-first device loop
  (emits device-ready per device; wifi_device None on first)
- Share single NetworkClient between main panel and wifi popup
  (eliminates async-init race of two independent instances)
- Strip connected: prefix from wlan label initial display
- Add _pending_toggle pattern to wifi notify handler to
  suppress stale-cache revert; same for bluetooth switch lock
This commit is contained in:
sankalp committed 2026-07-17 11:23:42 +05:45
1 parent 14c00c4504
commit 4f36ada1c5
4 files changed
+78 -64

No files matched your search

+2 -12
View File
@@ -1,6 +1,6 @@
from enum import Enum, auto
from fabric.utils import Gdk, GLib, Gtk, exec_shell_command, logger
from fabric.utils import Gdk, GLib, Gtk, logger
from fabric.widgets.box import Box
from fabric.widgets.button import Button
from fabric.widgets.centerbox import CenterBox
@@ -46,17 +46,6 @@ def get_battery_theme_icon(percentage: float, charging: bool = False) -> str:
def set_bluetooth_enabled_with_fallback(client, enabled: bool):
if enabled:
command = "rfkill unblock bluetooth"
else:
command = "rfkill block bluetooth"
result = exec_shell_command(command)
if result is False:
logger.error(f"rfkill fallback failed: command '{command}' returned False")
elif isinstance(result, str) and result.strip():
logger.warning(f"rfkill command output: {result.strip()}")
try:
if hasattr(client, "set_enabled"):
client.set_enabled(enabled)
@@ -481,6 +470,7 @@ class BluetoothConnections(Box):
def handle_user_toggle(self, active: bool):
self._switch_lock = True
set_bluetooth_enabled_with_fallback(self.client, active)
self.toggle_button.set_active(active)
GLib.timeout_add(1500, self._unlock_switch)
def _unlock_switch(self):
+34 -23
View File
@@ -107,8 +107,14 @@ class ModusControlCenter(AppletWindow):
wlan = modus_service.wlan if modus_service.wlan else "No Connection"
bluetooth = modus_service.bluetooth if modus_service.bluetooth else "Off"
if wlan.startswith("connected:"):
parts = wlan.split(":")
wlan_display = parts[1] if len(parts) >= 2 else "Connected"
else:
wlan_display = wlan
self.wlan_label = Label(
label=wlan,
label=wlan_display,
name="wifi-widget-label",
max_chars_width=15,
h_align="start",
@@ -487,8 +493,10 @@ class ModusControlCenter(AppletWindow):
],
)
# Initialize managers directly like working version
self.wifi_man = WifiConnections(self)
# Initialize network service upfront so WifiConnections can use it
self.network_service = NetworkClient()
self.network_service.connect("device-ready", self.on_network_ready)
self.wifi_man = WifiConnections(self, network_service=self.network_service)
self.bluetooth_man = BluetoothConnections(self)
self.has_bluetooth_open = False
@@ -565,11 +573,8 @@ class ModusControlCenter(AppletWindow):
try:
logger.debug("Initializing control center resources...")
# Initialize network service lazily
if self.network_service is None:
self.network_service = NetworkClient()
# Connect network signal
self.network_service.connect("device-ready", self.on_network_ready)
# Connect network signal (service created earlier in __init__)
self.network_service.connect("device-ready", self.on_network_ready)
# Check initial states lazily (only when needed)
self._check_initial_states()
@@ -896,29 +901,35 @@ class ModusControlCenter(AppletWindow):
return
try:
if hasattr(self, "bluetooth_man") and hasattr(self.bluetooth_man, "client"):
current_state = self.bluetooth_man.client.enabled
set_bluetooth_enabled_with_fallback(
self.bluetooth_man.client, not current_state
client = self.bluetooth_man.client
target = not client.enabled
set_bluetooth_enabled_with_fallback(client, target)
client.notify("enabled")
self.bluetooth_svg.dynamic_file(
"applets/bluetooth.svg" if target else "applets/bluetooth-off.svg"
)
self.bluetooth_label.set_label("On" if target else "Off")
else:
logger.warning("Bluetooth client not available for toggling")
except Exception as e:
logger.warning(f"Failed to toggle bluetooth: {e}")
def on_network_ready(self, *_):
if self._network_ready_fired:
if self._network_ready_fired and self.wifi_service:
return
wifi = self.network_service.wifi_device
if not wifi:
return
self._network_ready_fired = True
self.wifi_service = self.network_service.wifi_device
if self.wifi_service:
self._signal_connections.append(
(
self.wifi_service,
self.wifi_service.connect(
"notify::wireless-enabled", self.update_wifi_icon
),
)
self.wifi_service = wifi
self._signal_connections.append(
(
self.wifi_service,
self.wifi_service.connect(
"notify::wireless-enabled", self.update_wifi_icon
),
)
)
def update_wifi_icon(self, *_):
try:
@@ -935,8 +946,8 @@ class ModusControlCenter(AppletWindow):
return
try:
if self.wifi_service:
self.wifi_service.toggle_wifi()
GLib.timeout_add(100, self.update_wifi_icon)
target = not self.wifi_service.enabled
self.wifi_service.enabled = target
else:
logger.warning("WiFi device not available for toggling")
except Exception as e:
+2 -1
View File
@@ -33,8 +33,9 @@ def create_night_light_widget(control_center):
)
def toggle_ui(*_):
nonlocal is_active
if toggle_night_light():
is_active = is_night_light_active()
is_active = not is_active
night_light_icon.dynamic_file(
"applets/redshift-status-on.svg"
if is_active
+40 -28
View File
@@ -260,7 +260,7 @@ class WifiNetworkSlot(Box):
class WifiConnections(Box):
def __init__(self, parent, show_back_button=True, **kwargs):
def __init__(self, parent, show_back_button=True, network_service=None, **kwargs):
super().__init__(
spacing=8,
orientation="vertical",
@@ -269,20 +269,24 @@ class WifiConnections(Box):
)
self.parent = parent
self.network_service = NetworkClient()
self.network_service = network_service or NetworkClient()
self.wifi_service = None
self.is_scanning = False # Track scanning state
self._update_in_progress = False # Prevent concurrent updates
self._destroyed = False # Track if widget is destroyed
self._signal_ids = [] # Track all service signal IDs for cleanup
self._network_ready_fired = False # Guard against duplicate device-ready
self._pending_toggle = None # Force toggle to target after stale revert
self._signal_ids.append(
(
self.network_service,
self.network_service.connect("device-ready", self.on_network_ready),
if network_service and network_service.wifi_device:
self.on_network_ready()
else:
self._signal_ids.append(
(
self.network_service,
self.network_service.connect("device-ready", self.on_network_ready),
)
)
)
# Create pull-to-refresh indicator
self.refresh_indicator = Label(
@@ -438,41 +442,49 @@ class WifiConnections(Box):
def on_network_ready(self, *_):
"""Called when network service is ready"""
if self._network_ready_fired:
if self._network_ready_fired and self.wifi_service:
return
wifi = self.network_service.wifi_device
if not wifi:
return
self._network_ready_fired = True
self.wifi_service = self.network_service.wifi_device
if self.wifi_service:
self.toggle_button.set_active(self.wifi_service.enabled)
self.wifi_service = wifi
self.toggle_button.set_active(self.wifi_service.enabled)
self._signal_ids.append(
(
self.wifi_service,
self.wifi_service.connect(
"notify::enabled", self.on_wifi_enabled_changed
),
)
self._signal_ids.append(
(
self.wifi_service,
self.wifi_service.connect(
"notify::enabled", self.on_wifi_enabled_changed
),
)
self._signal_ids.append(
(
self.wifi_service,
self.wifi_service.connect("changed", self.update_networks),
)
)
self._signal_ids.append(
(
self.wifi_service,
self.wifi_service.connect("changed", self.update_networks),
)
)
# Initial network update
self.update_networks()
# Initial network update
self.update_networks()
def on_toggle_changed(self, toggle_button, *_):
"""Handle WiFi toggle button changes"""
if self.wifi_service:
new_state = toggle_button.get_active()
self.wifi_service.enabled = new_state
target = toggle_button.get_active()
self._pending_toggle = target
self.wifi_service.enabled = target
self.toggle_button.set_active(target)
def on_wifi_enabled_changed(self, *_):
"""Handle WiFi enabled state changes"""
if self.wifi_service:
self.toggle_button.set_active(self.wifi_service.enabled)
if self._pending_toggle is not None:
self.toggle_button.set_active(self._pending_toggle)
self._pending_toggle = None
else:
self.toggle_button.set_active(self.wifi_service.enabled)
def open_network_settings(self, *_):
"""Open NetworkManager connection editor"""