eiwd/src/iwd/iwd_adapter.c
Pierre-Olivier Mercier 0418e8bab9 manager: surface D-Bus call errors to the user
Connect / Forget / Set(Powered) / Scan / Disconnect / RegisterAgent /
ConnectHidden previously discarded reply errors with NULL callbacks, so
"Connecting…" could hang forever after a refused call (rfkill, busy
adapter, another agent already registered, bad credentials on a known
network). The user had no way to see the failure.

Add iwd_manager_{report,last,clear}_error and wire reply callbacks in
adapter / device / network / agent. The popup status line now appends
the latest error to the state label, and user actions (rescan, toggle,
connect, disconnect) clear it.

Scan errors that mean "already in flight" are filtered out — they're
the normal race when two scan triggers fire close together.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 11:29:59 +07:00

100 lines
3.2 KiB
C

#include "iwd_adapter.h"
#include "iwd_dbus.h"
#include "iwd_props.h"
#include "iwd_manager.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_powered_reply(void *data, const Eldbus_Message *msg, Eldbus_Pending *p EINA_UNUSED)
{
Iwd_Adapter *a = data;
const char *en, *em;
if (eldbus_message_error_get(msg, &en, &em) && a->manager)
iwd_manager_report_error(a->manager,
"Set Adapter.Powered failed: %s", em ? em : en);
}
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_powered_reply, a, -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;
}