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:
parent
293a858799
commit
1b5dd32c0b
7 changed files with 239 additions and 12 deletions
|
|
@ -82,6 +82,9 @@ _rebuild_list(Popup *p)
|
|||
if (!p->list || !e_iwd || !e_iwd->manager) return;
|
||||
elm_box_clear(p->list);
|
||||
|
||||
/* When the radio is off, hide the (now-stale) network list entirely. */
|
||||
if (iwd_manager_state(e_iwd->manager) == IWD_STATE_OFF) return;
|
||||
|
||||
const Eina_Hash *h = iwd_manager_networks(e_iwd->manager);
|
||||
if (!h) return;
|
||||
|
||||
|
|
|
|||
103
src/iwd/iwd_adapter.c
Normal file
103
src/iwd/iwd_adapter.c
Normal 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;
|
||||
}
|
||||
24
src/iwd/iwd_adapter.h
Normal file
24
src/iwd/iwd_adapter.h
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
#ifndef IWD_ADAPTER_H
|
||||
#define IWD_ADAPTER_H
|
||||
|
||||
#include <Eina.h>
|
||||
#include <Eldbus.h>
|
||||
|
||||
typedef struct _Iwd_Adapter
|
||||
{
|
||||
char *path;
|
||||
Eina_Bool powered;
|
||||
Eldbus_Object *obj;
|
||||
Eldbus_Proxy *proxy;
|
||||
Eldbus_Proxy *_props_proxy_keepalive;
|
||||
Eldbus_Signal_Handler *sh_props;
|
||||
void *manager;
|
||||
} Iwd_Adapter;
|
||||
|
||||
Iwd_Adapter *iwd_adapter_new (Eldbus_Connection *conn, const char *path, void *manager);
|
||||
void iwd_adapter_free(Iwd_Adapter *a);
|
||||
|
||||
void iwd_adapter_apply_props(Iwd_Adapter *a, Eldbus_Message_Iter *props);
|
||||
void iwd_adapter_set_powered(Iwd_Adapter *a, Eina_Bool on);
|
||||
|
||||
#endif
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
#include "iwd_dbus.h"
|
||||
#include "iwd_props.h"
|
||||
#include "iwd_manager.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
|
@ -127,6 +128,8 @@ iwd_device_free(Iwd_Device *d)
|
|||
iwd_device_detach_station(d);
|
||||
if (d->sh_dev_props) eldbus_signal_handler_del(d->sh_dev_props);
|
||||
if (d->device_proxy) eldbus_proxy_unref(d->device_proxy);
|
||||
if (d->adapter_proxy) eldbus_proxy_unref(d->adapter_proxy);
|
||||
if (d->adapter_obj) eldbus_object_unref(d->adapter_obj);
|
||||
if (d->obj) eldbus_object_unref(d->obj);
|
||||
free(d->path);
|
||||
free(d->name);
|
||||
|
|
@ -136,23 +139,70 @@ iwd_device_free(Iwd_Device *d)
|
|||
free(d);
|
||||
}
|
||||
|
||||
static void
|
||||
_log_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_device_set_powered(Iwd_Device *d, Eina_Bool on)
|
||||
{
|
||||
if (!d || !d->device_proxy) return;
|
||||
eldbus_proxy_property_set(d->device_proxy, "Powered", "b", &on, NULL, NULL);
|
||||
if (!d) return;
|
||||
Eina_Bool v = on;
|
||||
|
||||
/* Toggle the radio at the Adapter level — that's what actually takes
|
||||
* the interface up/down on modern iwd. Device.Powered is a no-op on
|
||||
* many installs. Keep the adapter proxy alive on the device so the
|
||||
* pending property_set isn't canceled. */
|
||||
if (d->adapter_path && d->obj && !d->adapter_proxy)
|
||||
{
|
||||
Eldbus_Connection *conn = eldbus_object_connection_get(d->obj);
|
||||
d->adapter_obj = eldbus_object_get(conn, IWD_BUS_NAME, d->adapter_path);
|
||||
if (d->adapter_obj)
|
||||
d->adapter_proxy = eldbus_proxy_get(d->adapter_obj, IWD_IFACE_ADAPTER);
|
||||
}
|
||||
if (d->adapter_proxy)
|
||||
eldbus_proxy_property_set(d->adapter_proxy, "Powered", "b", &v, _log_reply,
|
||||
on ? "Adapter.Powered=true"
|
||||
: "Adapter.Powered=false");
|
||||
else
|
||||
fprintf(stderr, "e_iwd: set_powered: no Adapter for %s\n",
|
||||
d->path ? d->path : "?");
|
||||
|
||||
if (d->device_proxy)
|
||||
eldbus_proxy_property_set(d->device_proxy, "Powered", "b", &v, NULL, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
iwd_device_scan(Iwd_Device *d)
|
||||
{
|
||||
if (!d || !d->station_proxy) return;
|
||||
eldbus_proxy_call(d->station_proxy, "Scan", NULL, NULL, -1, "");
|
||||
if (!d)
|
||||
{
|
||||
fprintf(stderr, "e_iwd: scan: NULL device\n");
|
||||
return;
|
||||
}
|
||||
if (!d->station_proxy)
|
||||
{
|
||||
fprintf(stderr, "e_iwd: scan: no Station proxy on %s\n",
|
||||
d->path ? d->path : "?");
|
||||
return;
|
||||
}
|
||||
eldbus_proxy_call(d->station_proxy, "Scan", _log_reply, "Scan", -1, "");
|
||||
}
|
||||
|
||||
void
|
||||
iwd_device_disconnect(Iwd_Device *d)
|
||||
{
|
||||
if (!d || !d->station_proxy) return;
|
||||
eldbus_proxy_call(d->station_proxy, "Disconnect", NULL, NULL, -1, "");
|
||||
if (!d || !d->station_proxy)
|
||||
{
|
||||
fprintf(stderr, "e_iwd: disconnect: missing Station proxy\n");
|
||||
return;
|
||||
}
|
||||
eldbus_proxy_call(d->station_proxy, "Disconnect", _log_reply, "Disconnect", -1, "");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,8 @@ struct _Iwd_Device
|
|||
Eldbus_Object *obj;
|
||||
Eldbus_Proxy *device_proxy;
|
||||
Eldbus_Proxy *station_proxy;
|
||||
Eldbus_Object *adapter_obj;
|
||||
Eldbus_Proxy *adapter_proxy;
|
||||
Eldbus_Signal_Handler *sh_dev_props;
|
||||
Eldbus_Signal_Handler *sh_sta_props;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include "iwd_manager.h"
|
||||
#include "iwd_dbus.h"
|
||||
#include "iwd_adapter.h"
|
||||
#include "iwd_device.h"
|
||||
#include "iwd_network.h"
|
||||
#include <stdlib.h>
|
||||
|
|
@ -15,6 +16,7 @@ struct _Iwd_Manager
|
|||
{
|
||||
Iwd_Dbus *dbus;
|
||||
Iwd_Agent *agent;
|
||||
Eina_Hash *adapters; /* path → Iwd_Adapter * */
|
||||
Eina_Hash *devices; /* path → Iwd_Device * */
|
||||
Eina_Hash *networks; /* path → Iwd_Network * */
|
||||
Eina_List *listeners; /* Listener * */
|
||||
|
|
@ -86,12 +88,27 @@ static void
|
|||
_recompute_state(Iwd_Manager *m)
|
||||
{
|
||||
Iwd_State s = IWD_STATE_OFF;
|
||||
Eina_Bool any_powered = EINA_FALSE;
|
||||
|
||||
/* Adapter.Powered is the source of truth for radio state — Device.Powered
|
||||
* is a no-op on modern iwd, so don't let it lie to us. If we have no
|
||||
* tracked adapter at all, fall back to "any device exists". */
|
||||
if (eina_hash_population(m->adapters) > 0)
|
||||
{
|
||||
Eina_Iterator *ait = eina_hash_iterator_data_new(m->adapters);
|
||||
Iwd_Adapter *ap;
|
||||
EINA_ITERATOR_FOREACH(ait, ap)
|
||||
if (ap->powered) any_powered = EINA_TRUE;
|
||||
eina_iterator_free(ait);
|
||||
}
|
||||
else
|
||||
any_powered = (eina_hash_population(m->devices) > 0);
|
||||
|
||||
Eina_Iterator *it = eina_hash_iterator_data_new(m->devices);
|
||||
Iwd_Device *d;
|
||||
Eina_Bool any_powered = EINA_FALSE;
|
||||
EINA_ITERATOR_FOREACH(it, d)
|
||||
{
|
||||
if (d->powered) any_powered = EINA_TRUE;
|
||||
if (!any_powered) continue; /* radio is down: ignore stale station */
|
||||
if (!d->has_station) continue;
|
||||
if (d->scanning && s < IWD_STATE_SCANNING) s = IWD_STATE_SCANNING;
|
||||
if (d->station_state == IWD_STATION_CONNECTING && s < IWD_STATE_CONNECTING) s = IWD_STATE_CONNECTING;
|
||||
|
|
@ -110,7 +127,17 @@ _on_iface_added(void *data, const char *path, const char *iface, Eldbus_Message_
|
|||
Iwd_Manager *m = data;
|
||||
Eldbus_Connection *conn = iwd_dbus_conn(m->dbus);
|
||||
|
||||
if (!strcmp(iface, IWD_IFACE_DEVICE))
|
||||
if (!strcmp(iface, IWD_IFACE_ADAPTER))
|
||||
{
|
||||
Iwd_Adapter *a = eina_hash_find(m->adapters, path);
|
||||
if (!a)
|
||||
{
|
||||
a = iwd_adapter_new(conn, path, m);
|
||||
if (a) eina_hash_add(m->adapters, path, a);
|
||||
}
|
||||
if (a) iwd_adapter_apply_props(a, props);
|
||||
}
|
||||
else if (!strcmp(iface, IWD_IFACE_DEVICE))
|
||||
{
|
||||
Iwd_Device *d = eina_hash_find(m->devices, path);
|
||||
if (!d)
|
||||
|
|
@ -167,6 +194,10 @@ _on_iface_removed(void *data, const char *path, const char *iface)
|
|||
{
|
||||
eina_hash_del(m->networks, path, NULL);
|
||||
}
|
||||
else if (!strcmp(iface, IWD_IFACE_ADAPTER))
|
||||
{
|
||||
eina_hash_del(m->adapters, path, NULL);
|
||||
}
|
||||
iwd_manager_notify(m);
|
||||
}
|
||||
|
||||
|
|
@ -177,6 +208,7 @@ static void
|
|||
_on_name_vanished(void *data)
|
||||
{
|
||||
Iwd_Manager *m = data;
|
||||
eina_hash_free_buckets(m->adapters);
|
||||
eina_hash_free_buckets(m->devices);
|
||||
eina_hash_free_buckets(m->networks);
|
||||
m->state = IWD_STATE_OFF;
|
||||
|
|
@ -185,6 +217,7 @@ _on_name_vanished(void *data)
|
|||
|
||||
/* ----- lifecycle ------------------------------------------------------- */
|
||||
|
||||
static void _adapter_free_cb(void *d) { iwd_adapter_free(d); }
|
||||
static void _device_free_cb (void *d) { iwd_device_free(d); }
|
||||
static void _network_free_cb(void *d) { iwd_network_free(d); }
|
||||
|
||||
|
|
@ -193,6 +226,7 @@ iwd_manager_new(Eldbus_Connection *conn)
|
|||
{
|
||||
Iwd_Manager *m = calloc(1, sizeof(*m));
|
||||
if (!m) return NULL;
|
||||
m->adapters = eina_hash_string_superfast_new(_adapter_free_cb);
|
||||
m->devices = eina_hash_string_superfast_new(_device_free_cb);
|
||||
m->networks = eina_hash_string_superfast_new(_network_free_cb);
|
||||
m->state = IWD_STATE_OFF;
|
||||
|
|
@ -214,6 +248,7 @@ iwd_manager_free(Iwd_Manager *m)
|
|||
if (!m) return;
|
||||
iwd_agent_free(m->agent);
|
||||
iwd_dbus_free(m->dbus);
|
||||
eina_hash_free(m->adapters);
|
||||
eina_hash_free(m->devices);
|
||||
eina_hash_free(m->networks);
|
||||
Listener *li;
|
||||
|
|
@ -239,8 +274,17 @@ void
|
|||
iwd_manager_set_powered(Iwd_Manager *m, Eina_Bool on)
|
||||
{
|
||||
if (!m) return;
|
||||
Eina_Iterator *it = eina_hash_iterator_data_new(m->devices);
|
||||
Iwd_Device *d;
|
||||
EINA_ITERATOR_FOREACH(it, d) iwd_device_set_powered(d, on);
|
||||
|
||||
if (!on)
|
||||
{
|
||||
Eina_Iterator *dit = eina_hash_iterator_data_new(m->devices);
|
||||
Iwd_Device *d;
|
||||
EINA_ITERATOR_FOREACH(dit, d) iwd_device_disconnect(d);
|
||||
eina_iterator_free(dit);
|
||||
}
|
||||
|
||||
Eina_Iterator *it = eina_hash_iterator_data_new(m->adapters);
|
||||
Iwd_Adapter *a;
|
||||
EINA_ITERATOR_FOREACH(it, a) iwd_adapter_set_powered(a, on);
|
||||
eina_iterator_free(it);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ e_iwd_sources = [
|
|||
'e_mod_gadget.c',
|
||||
'e_mod_popup.c',
|
||||
'iwd/iwd_dbus.c',
|
||||
'iwd/iwd_adapter.c',
|
||||
'iwd/iwd_props.c',
|
||||
'iwd/iwd_agent.c',
|
||||
'iwd/iwd_manager.c',
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue