This is an automated email from the git hooks/post-receive script.

git pushed a commit to branch network-manager
in repository enlightenment.

View the commit online.

commit 8a29b656dc9e232686b9cb54d1205490638937ce
Author: [email protected] <[email protected]>
AuthorDate: Sat Mar 7 11:26:40 2026 -0700

    fix: stabilize NetworkManager D-Bus lifecycle and UI behavior
    
    Replace one-shot D-Bus proxy pattern with persistent signal watchers
    for ActiveConnection and IP4Config tracking. The previous approach
    created/destroyed proxies on every state change, flooding NM with
    match rule churn and causing crashes.
    
    Key changes:
    - Persistent PropertiesChanged watchers on ActiveConnection and
      IP4Config objects eliminate repeated GetAll polling
    - SSID-based deduplication in popup (one entry per network, strongest
      signal shown)
    - Filter empty/hidden SSIDs from AP list
    - Map NM state values to ConnMan theme states for correct icon display
    - Add IP address label to popup
    - Use SSID-based matching for active network detection (handles
      multi-station roaming)
    - Fix ilist selection callback loop that caused connect/disconnect
      thrashing
    
    Co-Authored-By: Claude Opus 4.6 <[email protected]>
---
 src/modules/networkmanager/e_mod_main.c       | 157 +++++++++++-----
 src/modules/networkmanager/e_mod_main.h       |   1 +
 src/modules/networkmanager/e_networkmanager.c | 259 ++++++++++++++++++++------
 src/modules/networkmanager/e_networkmanager.h |  11 ++
 4 files changed, 324 insertions(+), 104 deletions(-)

diff --git a/src/modules/networkmanager/e_mod_main.c b/src/modules/networkmanager/e_mod_main.c
index 28873f4a6..3cc958f4b 100644
--- a/src/modules/networkmanager/e_mod_main.c
+++ b/src/modules/networkmanager/e_mod_main.c
@@ -32,7 +32,7 @@ void
 enm_popup_del(E_NM_Instance *inst)
 {
    E_FREE_FUNC(inst->popup, e_object_del);
-   inst->ui.popup.enabled = inst->ui.popup.list = NULL;
+   inst->ui.popup.enabled = inst->ui.popup.list = inst->ui.popup.ip_label = NULL;
 }
 
 static void
@@ -61,6 +61,8 @@ _enm_wireless_changed(void *data, Evas_Object *obj EINA_UNUSED,
    enm_wireless_enabled_set(ctxt->nm, !!ctxt->wireless_enabled);
 }
 
+static Eina_Bool _enm_ssid_is_active(struct NM_Manager *nm, const char *ssid);
+
 static void
 _enm_popup_selected_cb(void *data)
 {
@@ -80,7 +82,7 @@ _enm_popup_selected_cb(void *data)
    if (!ap) return;
 
    /* If this AP is currently active, disconnect instead */
-   if (nm->active_ap_path && (nm->active_ap_path == ap->path))
+   if (nm->active_ap_path && _enm_ssid_is_active(nm, ap->ssid))
      {
         INF("Disconnect from %s", ap->ssid ?: path);
         enm_ap_disconnect(nm);
@@ -118,7 +120,7 @@ _enm_ap_icon_new(struct NM_Manager *nm, struct NM_Access_Point *ap, Evas *evas)
                              "e/modules/connman/icon/wifi");
 
    /* Map active AP to ONLINE(5), otherwise IDLE(1) — ConnMan theme values */
-   state_val = (nm->active_ap_path && nm->active_ap_path == ap->path) ? 5 : 1;
+   state_val = (ap->ssid && _enm_ssid_is_active(nm, ap->ssid)) ? 5 : 1;
 
    msg = malloc(sizeof(*msg) + sizeof(int));
    if (msg)
@@ -163,6 +165,45 @@ _enm_ap_end_new(struct NM_Access_Point *ap, Evas *evas)
    return end;
 }
 
+/* Find the best AP for a given SSID across all devices.
+ * Prefers the active AP if it matches, otherwise picks highest strength.
+ * Used to deduplicate multiple stations broadcasting the same network name. */
+static struct NM_Access_Point *
+_enm_best_ap_for_ssid(struct NM_Manager *nm, const char *ssid)
+{
+   struct NM_Device *dev;
+   struct NM_Access_Point *best = NULL;
+
+   EINA_INLIST_FOREACH(nm->devices, dev)
+     {
+        struct NM_Access_Point *ap;
+        if (dev->type != NM_DEVICE_TYPE_WIFI) continue;
+        EINA_INLIST_FOREACH(dev->access_points, ap)
+          {
+             if (!ap->ssid || !ap->ssid[0]) continue;
+             if (strcmp(ap->ssid, ssid)) continue;
+             /* Always prefer the active AP */
+             if (nm->active_ap_path && nm->active_ap_path == ap->path)
+               return ap;
+             if (!best || ap->strength > best->strength)
+               best = ap;
+          }
+     }
+   return best;
+}
+
+/* Check if the given SSID matches the currently connected network */
+static Eina_Bool
+_enm_ssid_is_active(struct NM_Manager *nm, const char *ssid)
+{
+   struct NM_Access_Point *active;
+
+   if (!nm->active_ap_path) return EINA_FALSE;
+   active = enm_manager_find_ap(nm, nm->active_ap_path);
+   if (!active || !active->ssid) return EINA_FALSE;
+   return !strcmp(active->ssid, ssid);
+}
+
 static void
 _enm_popup_update(struct NM_Manager *nm, E_NM_Instance *inst)
 {
@@ -170,13 +211,17 @@ _enm_popup_update(struct NM_Manager *nm, E_NM_Instance *inst)
    Evas_Object *enabled = inst->ui.popup.enabled;
    Evas *evas = evas_object_evas_get(list);
    struct NM_Device *dev;
-   const char *hidden = "«hidden»";
+   Eina_Hash *seen_ssids;
 
    EINA_SAFETY_ON_NULL_RETURN(nm);
 
    e_widget_ilist_freeze(list);
    e_widget_ilist_clear(list);
 
+   /* Deduplicate APs by SSID — show only the strongest station per SSID */
+   seen_ssids = eina_hash_string_superfast_new(NULL);
+
+
    EINA_INLIST_FOREACH(nm->devices, dev)
      {
         struct NM_Access_Point *ap;
@@ -185,24 +230,52 @@ _enm_popup_update(struct NM_Manager *nm, E_NM_Instance *inst)
 
         EINA_INLIST_FOREACH(dev->access_points, ap)
           {
-             Evas_Object *icon = _enm_ap_icon_new(nm, ap, evas);
-             Evas_Object *end = _enm_ap_end_new(ap, evas);
+             struct NM_Access_Point *best;
+             Evas_Object *icon, *end;
+
+             /* Skip hidden networks (empty or NULL SSID) */
+             if (!ap->ssid || !ap->ssid[0]) continue;
+
+             /* Skip if we already showed this SSID */
+             if (eina_hash_find(seen_ssids, ap->ssid)) continue;
+
+             /* Find the best AP for this SSID and only show that one */
+             best = _enm_best_ap_for_ssid(nm, ap->ssid);
+             if (!best) continue;
+
+             eina_hash_add(seen_ssids, ap->ssid, (void *)1);
+
+             icon = _enm_ap_icon_new(nm, best, evas);
+             end = _enm_ap_end_new(best, evas);
 
              e_widget_ilist_append_full(list, icon, end,
-                                        ap->ssid ?: hidden,
+                                        best->ssid,
                                         _enm_popup_selected_cb,
-                                        inst, ap->path);
+                                        inst, best->path);
           }
      }
 
+   eina_hash_free(seen_ssids);
+
    e_widget_ilist_thaw(list);
    e_widget_ilist_go(list);
 
+   /* Update IP label */
+   if (nm->ip_address)
+     {
+        char ipbuf[128];
+        snprintf(ipbuf, sizeof(ipbuf), "IP: %s", nm->ip_address);
+        e_widget_label_text_set(inst->ui.popup.ip_label, ipbuf);
+     }
+   else
+     e_widget_label_text_set(inst->ui.popup.ip_label, "");
+
    if (inst->ctxt)
      {
         inst->ctxt->wireless_enabled = nm->wireless_enabled ? 1 : 0;
         e_widget_check_checked_set(enabled, inst->ctxt->wireless_enabled);
      }
+
 }
 
 static void
@@ -254,6 +327,9 @@ _enm_popup_new(E_NM_Instance *inst)
    e_widget_size_min_set(inst->ui.popup.list, 60, 100);
    e_widget_list_object_append(list, inst->ui.popup.list, 1, 1, 0.5);
 
+   inst->ui.popup.ip_label = e_widget_label_add(evas, "");
+   e_widget_list_object_append(list, inst->ui.popup.ip_label, 1, 0, 0.5);
+
    ck = e_widget_check_add(evas, _("Wifi On"), &(ctxt->wireless_enabled));
    inst->ui.popup.enabled = ck;
    e_widget_list_object_append(list, ck, 1, 0, 0.5);
@@ -304,6 +380,25 @@ _enm_gadget_setup(E_NM_Instance *inst)
      edje_object_signal_emit(o, "e,available", "e");
 }
 
+/* Map NM state to ConnMan theme state values:
+ * OFFLINE=0, IDLE=1, ASSOCIATION=2, CONFIGURATION=3, READY=4, _ONLINE_=5 */
+static int
+_enm_state_to_connman(enum NM_State state)
+{
+   switch (state)
+     {
+      case NM_STATE_UNKNOWN:
+      case NM_STATE_ASLEEP:           return 0; /* OFFLINE */
+      case NM_STATE_DISCONNECTED:
+      case NM_STATE_DISCONNECTING:    return 1; /* IDLE */
+      case NM_STATE_CONNECTING:       return 2; /* ASSOCIATION */
+      case NM_STATE_CONNECTED_LOCAL:  return 4; /* READY */
+      case NM_STATE_CONNECTED_SITE:   return 4; /* READY */
+      case NM_STATE_CONNECTED_GLOBAL: return 5; /* ONLINE */
+      default:                        return 0;
+     }
+}
+
 static void
 _enm_mod_manager_update_inst(E_NM_Module_Context *ctxt EINA_UNUSED,
                               E_NM_Instance *inst,
@@ -315,19 +410,20 @@ _enm_mod_manager_update_inst(E_NM_Module_Context *ctxt EINA_UNUSED,
    struct NM_Access_Point *active_ap = NULL;
    const char *typestr = "wifi";
    char buf[256];
-   char tooltip[256];
    uint8_t strength;
+   int theme_state;
 
    /* Resolve active AP for real signal strength */
    if (nm && nm->active_ap_path)
      active_ap = enm_manager_find_ap(nm, nm->active_ap_path);
 
    strength = active_ap ? active_ap->strength : 0;
+   theme_state = _enm_state_to_connman(state);
 
    msg = malloc(sizeof(*msg) + sizeof(int));
    if (!msg) return;
    msg->count = 2;
-   msg->val[0] = state;
+   msg->val[0] = theme_state;
    msg->val[1] = strength;
 
    edje_object_message_send(o, EDJE_MESSAGE_INT_SET, 1, msg);
@@ -336,43 +432,10 @@ _enm_mod_manager_update_inst(E_NM_Module_Context *ctxt EINA_UNUSED,
    snprintf(buf, sizeof(buf), "e,changed,technology,%s", typestr);
    edje_object_signal_emit(o, buf, "e");
 
-   /* Build tooltip text for the gadget label */
-   if (!nm)
-     snprintf(tooltip, sizeof(tooltip), _("NetworkManager unavailable"));
-   else if (state == NM_STATE_CONNECTED_GLOBAL ||
-            state == NM_STATE_CONNECTED_SITE   ||
-            state == NM_STATE_CONNECTED_LOCAL)
-     {
-        const char *ssid = active_ap ? active_ap->ssid : NULL;
-        const char *ip   = nm->ip_address;
-
-        if (ssid && ip)
-          snprintf(tooltip, sizeof(tooltip), _("WiFi: %s — %s"), ssid, ip);
-        else if (ssid)
-          snprintf(tooltip, sizeof(tooltip), _("WiFi: %s"), ssid);
-        else if (ip)
-          snprintf(tooltip, sizeof(tooltip), _("Connected — %s"), ip);
-        else
-          snprintf(tooltip, sizeof(tooltip), _("Connected"));
-     }
-   else if (state == NM_STATE_CONNECTING)
-     {
-        const char *ssid = active_ap ? active_ap->ssid : NULL;
-        if (ssid)
-          snprintf(tooltip, sizeof(tooltip), _("Connecting: %s"), ssid);
-        else
-          snprintf(tooltip, sizeof(tooltip), _("Connecting…"));
-     }
-   else if (state == NM_STATE_DISCONNECTED ||
-            state == NM_STATE_DISCONNECTING)
-     snprintf(tooltip, sizeof(tooltip), _("Disconnected"));
-   else
-     snprintf(tooltip, sizeof(tooltip), _("NetworkManager: %s"),
-              enm_state_to_str(state));
-
-   edje_object_part_text_set(o, "e.text.label", tooltip);
-
-   DBG("state=%d strength=%u tooltip='%s'", state, strength, tooltip);
+   DBG("state=%d (theme=%d) strength=%u active_ap=%s ip=%s",
+       state, theme_state, strength,
+       nm ? (nm->active_ap_path ?: "(null)") : "no-nm",
+       nm ? (nm->ip_address ?: "(null)") : "no-nm");
 }
 
 void
diff --git a/src/modules/networkmanager/e_mod_main.h b/src/modules/networkmanager/e_mod_main.h
index 346144475..d8166e186 100644
--- a/src/modules/networkmanager/e_mod_main.h
+++ b/src/modules/networkmanager/e_mod_main.h
@@ -27,6 +27,7 @@ struct E_NM_Instance
         struct
           {
              Evas_Object *list;
+             Evas_Object *ip_label;
              Evas_Object *enabled; /* "Wifi On" checkbox */
           } popup;
      } ui;
diff --git a/src/modules/networkmanager/e_networkmanager.c b/src/modules/networkmanager/e_networkmanager.c
index 64b61b76d..4a6bc5dc7 100644
--- a/src/modules/networkmanager/e_networkmanager.c
+++ b/src/modules/networkmanager/e_networkmanager.c
@@ -516,9 +516,87 @@ _device_free(struct NM_Device *dev)
 }
 
 /* -------------------------------------------------------------------------- */
-/* IP tracking                                                                 */
+/* IP tracking (persistent watcher)                                            */
 /* -------------------------------------------------------------------------- */
 
+static void
+_manager_ip4_watch_free(struct NM_Manager *nm)
+{
+   if (nm->pending.ip4config)
+     {
+        eldbus_pending_cancel(nm->pending.ip4config);
+        nm->pending.ip4config = NULL;
+     }
+   if (nm->ip4_proxy)
+     {
+        eldbus_proxy_unref(nm->ip4_proxy);
+        eldbus_object_unref(nm->ip4_obj);
+        nm->ip4_proxy = NULL;
+        nm->ip4_obj = NULL;
+     }
+   eina_stringshare_del(nm->ip4_path);
+   nm->ip4_path = NULL;
+}
+
+/* Parse AddressData from a variant containing aa{sv} */
+static void
+_ip4_parse_address_data(struct NM_Manager *nm, Eldbus_Message_Iter *var)
+{
+   Eldbus_Message_Iter *addr_array, *addr_dict, *entry;
+   const char *entry_key;
+   Eldbus_Message_Iter *entry_var;
+
+   if (!eldbus_message_iter_arguments_get(var, "aa{sv}", &addr_array))
+     return;
+
+   /* Take the first address */
+   if (!eldbus_message_iter_get_and_next(addr_array, 'a', &addr_dict))
+     return;
+
+   while (eldbus_message_iter_get_and_next(addr_dict, 'e', &entry))
+     {
+        if (!eldbus_message_iter_arguments_get(entry, "sv",
+                                               &entry_key, &entry_var))
+          continue;
+        if (!strcmp(entry_key, "address"))
+          {
+             const char *addr;
+             if (eldbus_message_iter_arguments_get(entry_var, "s", &addr))
+               {
+                  free(nm->ip_address);
+                  nm->ip_address = strdup(addr);
+                  DBG("IP address: %s", nm->ip_address);
+               }
+          }
+     }
+}
+
+static void
+_ip4_prop_changed(void *data, const Eldbus_Message *msg)
+{
+   struct NM_Manager *nm = data;
+   Eldbus_Message_Iter *changed_props, *invalidated;
+   const char *iface;
+   Eldbus_Message_Iter *dict, *var;
+   const char *key;
+
+   if (!eldbus_message_arguments_get(msg, "sa{sv}as",
+                                     &iface, &changed_props, &invalidated))
+     return;
+
+   while (eldbus_message_iter_get_and_next(changed_props, 'e', &dict))
+     {
+        if (!eldbus_message_iter_arguments_get(dict, "sv", &key, &var))
+          continue;
+
+        if (!strcmp(key, "AddressData"))
+          {
+             _ip4_parse_address_data(nm, var);
+             enm_mod_manager_update(nm);
+          }
+     }
+}
+
 static void
 _ip4config_get_props_cb(void *data, const Eldbus_Message *msg,
                         Eldbus_Pending *pending EINA_UNUSED)
@@ -547,67 +625,105 @@ _ip4config_get_props_cb(void *data, const Eldbus_Message *msg,
           continue;
 
         if (!strcmp(key, "AddressData"))
-          {
-             Eldbus_Message_Iter *addr_array, *addr_dict, *entry;
-             const char *entry_key;
-             Eldbus_Message_Iter *entry_var;
-
-             if (!eldbus_message_iter_arguments_get(var, "aa{sv}", &addr_array))
-               continue;
-
-             /* Take the first address */
-             if (!eldbus_message_iter_get_and_next(addr_array, 'a', &addr_dict))
-               continue;
-
-             while (eldbus_message_iter_get_and_next(addr_dict, 'e', &entry))
-               {
-                  if (!eldbus_message_iter_arguments_get(entry, "sv",
-                                                         &entry_key,
-                                                         &entry_var))
-                    continue;
-                  if (!strcmp(entry_key, "address"))
-                    {
-                       const char *addr;
-                       if (eldbus_message_iter_arguments_get(entry_var, "s",
-                                                             &addr))
-                         {
-                            free(nm->ip_address);
-                            nm->ip_address = strdup(addr);
-                            DBG("IP address: %s", nm->ip_address);
-                         }
-                    }
-               }
-          }
+          _ip4_parse_address_data(nm, var);
      }
+
+   /* Proxy stays alive — updates arrive via _ip4_prop_changed signal */
+   enm_mod_manager_update(nm);
 }
 
 static void
-_manager_fetch_ip(struct NM_Manager *nm, const char *ip4config_path)
+_manager_watch_ip4(struct NM_Manager *nm, const char *ip4config_path)
 {
    Eldbus_Object *obj;
    Eldbus_Proxy *props;
 
    if (!ip4config_path || !strcmp(ip4config_path, "/")) return;
 
-   if (nm->pending.ip4config)
-     eldbus_pending_cancel(nm->pending.ip4config);
+   /* Skip if already watching this exact path */
+   if (nm->ip4_path && !strcmp(nm->ip4_path, ip4config_path))
+     return;
+
+   /* Tear down previous watcher */
+   _manager_ip4_watch_free(nm);
 
    obj = eldbus_object_get(conn, NM_BUS_NAME, ip4config_path);
    props = eldbus_proxy_get(obj, NM_IFACE_PROPS);
 
+   nm->ip4_proxy = props;
+   nm->ip4_obj = obj;
+   nm->ip4_path = eina_stringshare_add(ip4config_path);
+
+   /* Subscribe to property changes — keeps proxy alive for signals */
+   eldbus_proxy_signal_handler_add(props, "PropertiesChanged",
+                                   _ip4_prop_changed, nm);
+
+   /* Initial fetch */
    nm->pending.ip4config = eldbus_proxy_call(props, "GetAll",
                                              _ip4config_get_props_cb, nm,
                                              -1, "s", NM_IFACE_IP4);
-
-   /* Unref proxy/obj; the pending keeps them alive until callback fires */
-   eldbus_proxy_unref(props);
-   eldbus_object_unref(obj);
 }
 
 /* -------------------------------------------------------------------------- */
-/* Active connection tracking                                                  */
+/* Active connection tracking (persistent watcher)                             */
 /* -------------------------------------------------------------------------- */
 
+static void
+_manager_active_conn_watch_free(struct NM_Manager *nm)
+{
+   if (nm->pending.active_conn)
+     {
+        eldbus_pending_cancel(nm->pending.active_conn);
+        nm->pending.active_conn = NULL;
+     }
+   if (nm->active_conn_proxy)
+     {
+        eldbus_proxy_unref(nm->active_conn_proxy);
+        eldbus_object_unref(nm->active_conn_obj);
+        nm->active_conn_proxy = NULL;
+        nm->active_conn_obj = NULL;
+     }
+}
+
+static void
+_active_conn_prop_changed(void *data, const Eldbus_Message *msg)
+{
+   struct NM_Manager *nm = data;
+   Eldbus_Message_Iter *changed_props, *invalidated;
+   const char *iface;
+   Eldbus_Message_Iter *dict, *var;
+   const char *key;
+
+   if (!eldbus_message_arguments_get(msg, "sa{sv}as",
+                                     &iface, &changed_props, &invalidated))
+     return;
+
+   while (eldbus_message_iter_get_and_next(changed_props, 'e', &dict))
+     {
+        if (!eldbus_message_iter_arguments_get(dict, "sv", &key, &var))
+          continue;
+
+        if (!strcmp(key, "Ip4Config"))
+          {
+             const char *ip4path;
+             if (eldbus_message_iter_arguments_get(var, "o", &ip4path))
+               _manager_watch_ip4(nm, ip4path);
+          }
+        else if (!strcmp(key, "SpecificObject"))
+          {
+             const char *ap_path;
+             if (eldbus_message_iter_arguments_get(var, "o", &ap_path))
+               {
+                  DBG("ActiveConn SpecificObject changed: %s", ap_path);
+                  eina_stringshare_del(nm->active_ap_path);
+                  nm->active_ap_path = eina_stringshare_add(ap_path);
+                  enm_mod_manager_update(nm);
+                  enm_mod_aps_changed(nm);
+               }
+          }
+     }
+}
+
 static void
 _active_conn_get_props_cb(void *data, const Eldbus_Message *msg,
                           Eldbus_Pending *pending EINA_UNUSED)
@@ -625,7 +741,10 @@ _active_conn_get_props_cb(void *data, const Eldbus_Message *msg,
      }
 
    if (!eldbus_message_arguments_get(msg, "a{sv}", &array))
-     return;
+     {
+        WRN("ActiveConnection GetAll: cannot parse a{sv}");
+        return;
+     }
 
    while (eldbus_message_iter_get_and_next(array, 'e', &dict))
      {
@@ -639,45 +758,62 @@ _active_conn_get_props_cb(void *data, const Eldbus_Message *msg,
           {
              const char *ip4path;
              if (eldbus_message_iter_arguments_get(var, "o", &ip4path))
-               _manager_fetch_ip(nm, ip4path);
+               _manager_watch_ip4(nm, ip4path);
           }
         else if (!strcmp(key, "SpecificObject"))
           {
-             /* AP path for the active WiFi connection */
              const char *ap_path;
              if (eldbus_message_iter_arguments_get(var, "o", &ap_path))
                {
+                  DBG("ActiveConn SpecificObject=%s", ap_path);
                   eina_stringshare_del(nm->active_ap_path);
                   nm->active_ap_path = eina_stringshare_add(ap_path);
                }
           }
      }
+
+   /* Proxy stays alive — updates arrive via _active_conn_prop_changed */
+   DBG("ActiveConn done: active_ap=%s", nm->active_ap_path ?: "(null)");
+   enm_mod_manager_update(nm);
+   enm_mod_aps_changed(nm);
 }
 
 static void
-_manager_check_active_connections(struct NM_Manager *nm,
-                                   const char *active_conn_path)
+_manager_watch_active_conn(struct NM_Manager *nm,
+                            const char *active_conn_path)
 {
    Eldbus_Object *obj;
    Eldbus_Proxy *props;
 
+   DBG("_manager_watch_active_conn path=%s", active_conn_path ?: "(null)");
    if (!active_conn_path || !strcmp(active_conn_path, "/")) return;
 
+   /* Skip if already watching this exact connection */
+   if (nm->active_connection_path &&
+       !strcmp(nm->active_connection_path, active_conn_path))
+     return;
+
    eina_stringshare_del(nm->active_connection_path);
    nm->active_connection_path = eina_stringshare_add(active_conn_path);
 
-   if (nm->pending.active_conn)
-     eldbus_pending_cancel(nm->pending.active_conn);
+   /* Tear down previous watchers (active conn + ip4) */
+   _manager_active_conn_watch_free(nm);
+   _manager_ip4_watch_free(nm);
 
    obj = eldbus_object_get(conn, NM_BUS_NAME, active_conn_path);
    props = eldbus_proxy_get(obj, NM_IFACE_PROPS);
 
+   nm->active_conn_proxy = props;
+   nm->active_conn_obj = obj;
+
+   /* Subscribe to property changes — keeps proxy alive for signals */
+   eldbus_proxy_signal_handler_add(props, "PropertiesChanged",
+                                   _active_conn_prop_changed, nm);
+
+   /* Initial fetch */
    nm->pending.active_conn = eldbus_proxy_call(props, "GetAll",
                                                _active_conn_get_props_cb, nm,
                                                -1, "s", NM_IFACE_ACONN);
-
-   eldbus_proxy_unref(props);
-   eldbus_object_unref(obj);
 }
 
 /* -------------------------------------------------------------------------- */
@@ -736,9 +872,12 @@ _manager_prop_changed(void *data, const Eldbus_Message *msg)
 
              /* Use first active connection */
              if (eldbus_message_iter_get_and_next(conn_array, 'o', &aconn_path))
-               _manager_check_active_connections(nm, aconn_path);
+               _manager_watch_active_conn(nm, aconn_path);
              else
                {
+                  /* No active connections — tear down watchers */
+                  _manager_active_conn_watch_free(nm);
+                  _manager_ip4_watch_free(nm);
                   eina_stringshare_del(nm->active_ap_path);
                   nm->active_ap_path = NULL;
                   eina_stringshare_del(nm->active_connection_path);
@@ -799,12 +938,19 @@ _manager_get_props_cb(void *data, const Eldbus_Message *msg,
              const char *aconn_path;
 
              if (!eldbus_message_iter_arguments_get(var, "ao", &conn_array))
-               continue;
+               {
+                  WRN("ActiveConnections: cannot parse ao from variant");
+                  continue;
+               }
 
              if (eldbus_message_iter_get_and_next(conn_array, 'o', &aconn_path))
-               _manager_check_active_connections(nm, aconn_path);
+               {
+                  DBG("ActiveConnections: first path=%s", aconn_path);
+                  _manager_watch_active_conn(nm, aconn_path);
+               }
              else
                {
+                  DBG("ActiveConnections: empty array");
                   eina_stringshare_del(nm->active_ap_path);
                   nm->active_ap_path = NULL;
                   eina_stringshare_del(nm->active_connection_path);
@@ -947,10 +1093,9 @@ _manager_free(struct NM_Manager *nm)
      eldbus_pending_cancel(nm->pending.get_props);
    if (nm->pending.get_devices)
      eldbus_pending_cancel(nm->pending.get_devices);
-   if (nm->pending.active_conn)
-     eldbus_pending_cancel(nm->pending.active_conn);
-   if (nm->pending.ip4config)
-     eldbus_pending_cancel(nm->pending.ip4config);
+
+   _manager_active_conn_watch_free(nm);
+   _manager_ip4_watch_free(nm);
 
    while (nm->devices)
      {
diff --git a/src/modules/networkmanager/e_networkmanager.h b/src/modules/networkmanager/e_networkmanager.h
index 38d6e0e02..1f8e6ac90 100644
--- a/src/modules/networkmanager/e_networkmanager.h
+++ b/src/modules/networkmanager/e_networkmanager.h
@@ -106,6 +106,17 @@ struct NM_Manager
         Eldbus_Pending *active_conn;
         Eldbus_Pending *ip4config;
      } pending;
+
+   /* Persistent proxy/obj for watching active connection properties.
+    * Created when active connection changes, freed when it changes again
+    * or on manager shutdown.  Signal-driven via PropertiesChanged. */
+   Eldbus_Proxy  *active_conn_proxy;
+   Eldbus_Object *active_conn_obj;
+
+   /* Persistent proxy/obj for watching IP4Config properties. */
+   Eldbus_Proxy  *ip4_proxy;
+   Eldbus_Object *ip4_obj;
+   const char    *ip4_path;
 };
 
 /* Ecore Events */

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.

Reply via email to