manager: track Adapter objects + clear list on disable

Promote Adapter to a first-class manager object (Iwd_Adapter with
PropertiesChanged subscription). iwd_manager_set_powered now drives
the adapter directly, so Enable still works after Disable has torn
down the device hash. State recomputation also looks at any
powered adapter, and the popup hides the network list while
state == IWD_STATE_OFF.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
nemunaire 2026-04-09 10:58:47 +07:00
commit 1b5dd32c0b
7 changed files with 239 additions and 12 deletions

103
src/iwd/iwd_adapter.c Normal file
View file

@ -0,0 +1,103 @@
#include "iwd_adapter.h"
#include "iwd_dbus.h"
#include "iwd_props.h"
#include "iwd_manager.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static void
_prop_cb(void *data, const char *key, Eldbus_Message_Iter *v)
{
Iwd_Adapter *a = data;
if (!strcmp(key, "Powered")) a->powered = iwd_props_bool(v);
}
void
iwd_adapter_apply_props(Iwd_Adapter *a, Eldbus_Message_Iter *props)
{
iwd_props_for_each(props, _prop_cb, a);
}
static void
_on_props_changed(void *data, const Eldbus_Message *msg)
{
Iwd_Adapter *a = data;
const char *iface;
Eldbus_Message_Iter *changed, *invalidated;
if (!eldbus_message_arguments_get(msg, "sa{sv}as", &iface, &changed, &invalidated))
return;
if (strcmp(iface, IWD_IFACE_ADAPTER) != 0) return;
iwd_props_for_each(changed, _prop_cb, a);
if (a->manager) iwd_manager_notify(a->manager);
}
Iwd_Adapter *
iwd_adapter_new(Eldbus_Connection *conn, const char *path, void *manager)
{
Iwd_Adapter *a = calloc(1, sizeof(*a));
if (!a) return NULL;
a->path = path ? strdup(path) : NULL;
a->manager = manager;
a->obj = eldbus_object_get(conn, IWD_BUS_NAME, path);
if (a->obj)
{
a->proxy = eldbus_proxy_get(a->obj, IWD_IFACE_ADAPTER);
if (a->proxy)
a->sh_props = eldbus_proxy_properties_changed_callback_add(
a->proxy, _on_props_changed, a);
}
return a;
}
void
iwd_adapter_free(Iwd_Adapter *a)
{
if (!a) return;
if (a->sh_props) eldbus_signal_handler_del(a->sh_props);
if (a->_props_proxy_keepalive) eldbus_proxy_unref(a->_props_proxy_keepalive);
if (a->proxy) eldbus_proxy_unref(a->proxy);
if (a->obj) eldbus_object_unref(a->obj);
free(a->path);
free(a);
}
static void
_on_set_reply(void *data, const Eldbus_Message *msg, Eldbus_Pending *p EINA_UNUSED)
{
const char *what = data;
const char *en, *em;
if (eldbus_message_error_get(msg, &en, &em))
fprintf(stderr, "e_iwd: %s failed: %s: %s\n", what, en, em);
else
fprintf(stderr, "e_iwd: %s ok\n", what);
}
void
iwd_adapter_set_powered(Iwd_Adapter *a, Eina_Bool on)
{
if (!a || !a->obj) return;
/* Call org.freedesktop.DBus.Properties.Set explicitly so we control the
* variant marshaling exactly. eldbus_proxy_property_set silently swallows
* Adapter.Powered on this iwd version. */
Eldbus_Proxy *props = eldbus_proxy_get(a->obj, "org.freedesktop.DBus.Properties");
if (!props) return;
Eldbus_Message *msg = eldbus_proxy_method_call_new(props, "Set");
Eldbus_Message_Iter *iter = eldbus_message_iter_get(msg);
const char *iface = IWD_IFACE_ADAPTER;
const char *prop = "Powered";
eldbus_message_iter_basic_append(iter, 's', iface);
eldbus_message_iter_basic_append(iter, 's', prop);
Eldbus_Message_Iter *variant = eldbus_message_iter_container_new(iter, 'v', "b");
Eina_Bool v = on;
eldbus_message_iter_basic_append(variant, 'b', v);
eldbus_message_iter_container_close(iter, variant);
eldbus_proxy_send(props, msg, _on_set_reply,
on ? "Adapter.Powered=true" : "Adapter.Powered=false", -1);
/* Keep the props proxy alive on the adapter so the call isn't canceled. */
if (a->_props_proxy_keepalive) eldbus_proxy_unref(a->_props_proxy_keepalive);
a->_props_proxy_keepalive = props;
}