diff --git a/src/e_mod_popup.c b/src/e_mod_popup.c index 94b777a..c55a9f3 100644 --- a/src/e_mod_popup.c +++ b/src/e_mod_popup.c @@ -148,6 +148,7 @@ _on_net_clicked(void *data, Evas_Object *obj EINA_UNUSED, void *ev EINA_UNUSED) { Iwd_Network *n = data; if (!n) return; + if (e_iwd && e_iwd->manager) iwd_manager_clear_error(e_iwd->manager); iwd_network_connect(n); } @@ -234,7 +235,17 @@ _refresh(Popup *p) if (!p || !e_iwd || !e_iwd->manager) return; Iwd_State s = iwd_manager_state(e_iwd->manager); if (p->status_lbl) - elm_object_text_set(p->status_lbl, _state_label(s)); + { + const char *err = iwd_manager_last_error(e_iwd->manager); + if (err) + { + char buf[320]; + snprintf(buf, sizeof(buf), "%s — %s", _state_label(s), err); + elm_object_text_set(p->status_lbl, buf); + } + else + elm_object_text_set(p->status_lbl, _state_label(s)); + } if (p->btn_toggle) elm_object_text_set(p->btn_toggle, s == IWD_STATE_OFF ? "Enable" : "Disable"); if (p->btn_scan) @@ -260,18 +271,23 @@ _on_manager_change(void *data, Iwd_Manager *m EINA_UNUSED) static void _on_rescan (void *d EINA_UNUSED, Evas_Object *o EINA_UNUSED, void *e EINA_UNUSED) { - if (e_iwd && e_iwd->manager) iwd_manager_scan_request(e_iwd->manager); + if (!e_iwd || !e_iwd->manager) return; + iwd_manager_clear_error(e_iwd->manager); + iwd_manager_scan_request(e_iwd->manager); } static void _on_toggle(void *d EINA_UNUSED, Evas_Object *o EINA_UNUSED, void *e EINA_UNUSED) { if (!e_iwd || !e_iwd->manager) return; + iwd_manager_clear_error(e_iwd->manager); Eina_Bool off = (iwd_manager_state(e_iwd->manager) == IWD_STATE_OFF); iwd_manager_set_powered(e_iwd->manager, off); } static void _on_disconnect(void *d EINA_UNUSED, Evas_Object *o EINA_UNUSED, void *e EINA_UNUSED) { Iwd_Device *dev = _active_device(); - if (dev) iwd_device_disconnect(dev); + if (!dev) return; + if (e_iwd && e_iwd->manager) iwd_manager_clear_error(e_iwd->manager); + iwd_device_disconnect(dev); } static void diff --git a/src/iwd/iwd_adapter.c b/src/iwd/iwd_adapter.c index 4747dad..8998d39 100644 --- a/src/iwd/iwd_adapter.c +++ b/src/iwd/iwd_adapter.c @@ -61,6 +61,16 @@ iwd_adapter_free(Iwd_Adapter *a) 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) { @@ -83,7 +93,7 @@ iwd_adapter_set_powered(Iwd_Adapter *a, Eina_Bool on) eldbus_message_iter_basic_append(variant, 'b', v); eldbus_message_iter_container_close(iter, variant); - eldbus_proxy_send(props, msg, NULL, NULL, -1); + 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; diff --git a/src/iwd/iwd_agent.c b/src/iwd/iwd_agent.c index f78c558..e7cc974 100644 --- a/src/iwd/iwd_agent.c +++ b/src/iwd/iwd_agent.c @@ -1,5 +1,7 @@ #include "iwd_agent.h" #include "iwd_dbus.h" +#include "iwd_manager.h" +#include #include #include @@ -135,11 +137,18 @@ iwd_agent_cancel(Iwd_Agent_Request *req) /* ----- Registration with iwd ------------------------------------------ */ static void -_on_register(void *data EINA_UNUSED, const Eldbus_Message *msg, +_on_register(void *data, const Eldbus_Message *msg, Eldbus_Pending *p EINA_UNUSED) { + /* `data` is the manager — same pointer the trampoline carries. */ + Iwd_Manager *m = data; const char *en, *em; - if (eldbus_message_error_get(msg, &en, &em)) + if (!eldbus_message_error_get(msg, &en, &em)) return; + if (m) + iwd_manager_report_error(m, + "Wi-Fi agent registration refused (another agent running?): %s", + em ? em : en); + else fprintf(stderr, "e_iwd: agent register failed: %s: %s\n", en, em); } @@ -147,7 +156,7 @@ void iwd_agent_register(Iwd_Agent *a) { if (!a || !a->am_proxy) return; - eldbus_proxy_call(a->am_proxy, "RegisterAgent", _on_register, NULL, -1, + eldbus_proxy_call(a->am_proxy, "RegisterAgent", _on_register, a->data, -1, "o", IWD_AGENT_PATH); } diff --git a/src/iwd/iwd_device.c b/src/iwd/iwd_device.c index 1c05c52..b8942c9 100644 --- a/src/iwd/iwd_device.c +++ b/src/iwd/iwd_device.c @@ -3,7 +3,6 @@ #include "iwd_props.h" #include "iwd_manager.h" #include "iwd_network.h" -#include #include #include @@ -186,35 +185,72 @@ _refresh_signals(Iwd_Device *d) _on_ordered_networks, d, -1, ""); } +static void +_on_scan_reply(void *data, const Eldbus_Message *msg, Eldbus_Pending *p EINA_UNUSED) +{ + Iwd_Device *d = data; + const char *en, *em; + if (!eldbus_message_error_get(msg, &en, &em)) return; + /* "AlreadyExists" / "InProgress" is the normal race when two scan + * triggers fire close together — don't spam the user with that. */ + if (en && (strstr(en, "InProgress") || strstr(en, "Busy") || + strstr(en, "AlreadyExists"))) + return; + if (d->manager) + iwd_manager_report_error(d->manager, "Scan failed: %s", em ? em : en); +} + void iwd_device_scan(Iwd_Device *d) { if (!d || !d->station_proxy) return; - eldbus_proxy_call(d->station_proxy, "Scan", NULL, NULL, -1, ""); + eldbus_proxy_call(d->station_proxy, "Scan", _on_scan_reply, d, -1, ""); +} + +static void +_on_disconnect_reply(void *data, const Eldbus_Message *msg, Eldbus_Pending *p EINA_UNUSED) +{ + Iwd_Device *d = data; + const char *en, *em; + if (eldbus_message_error_get(msg, &en, &em) && d->manager) + iwd_manager_report_error(d->manager, + "Disconnect failed: %s", em ? em : en); } void iwd_device_disconnect(Iwd_Device *d) { if (!d || !d->station_proxy) return; - eldbus_proxy_call(d->station_proxy, "Disconnect", NULL, NULL, -1, ""); + eldbus_proxy_call(d->station_proxy, "Disconnect", _on_disconnect_reply, d, -1, ""); } +typedef struct +{ + Iwd_Device *d; + char *ssid; +} _Connect_Hidden_Ctx; + static void _on_connect_hidden_reply(void *data, const Eldbus_Message *msg, Eldbus_Pending *p EINA_UNUSED) { + _Connect_Hidden_Ctx *ctx = data; const char *en, *em; - char *ssid = data; - if (eldbus_message_error_get(msg, &en, &em)) - fprintf(stderr, "e_iwd: ConnectHiddenNetwork('%s') failed: %s: %s\n", - ssid ? ssid : "?", en, em); - free(ssid); + if (eldbus_message_error_get(msg, &en, &em) && ctx->d->manager) + iwd_manager_report_error(ctx->d->manager, + "Connect to hidden '%s' failed: %s", + ctx->ssid ? ctx->ssid : "?", em ? em : en); + free(ctx->ssid); + free(ctx); } void iwd_device_connect_hidden(Iwd_Device *d, const char *ssid) { if (!d || !d->station_proxy || !ssid || !*ssid) return; + _Connect_Hidden_Ctx *ctx = calloc(1, sizeof(*ctx)); + if (!ctx) return; + ctx->d = d; + ctx->ssid = strdup(ssid); eldbus_proxy_call(d->station_proxy, "ConnectHiddenNetwork", - _on_connect_hidden_reply, strdup(ssid), -1, "s", ssid); + _on_connect_hidden_reply, ctx, -1, "s", ssid); } diff --git a/src/iwd/iwd_manager.c b/src/iwd/iwd_manager.c index 49b9576..941e907 100644 --- a/src/iwd/iwd_manager.c +++ b/src/iwd/iwd_manager.c @@ -4,6 +4,8 @@ #include "iwd_device.h" #include "iwd_network.h" #include +#include +#include #include #include @@ -23,6 +25,7 @@ struct _Iwd_Manager Eina_List *listeners; /* Listener * */ Iwd_State state; Ecore_Job *notify_job; + char *last_error; Iwd_Agent_Passphrase_Cb pass_cb; void *pass_data; @@ -102,6 +105,33 @@ iwd_manager_notify(Iwd_Manager *m) m->notify_job = ecore_job_add(_notify_job_cb, m); } +const char * +iwd_manager_last_error(const Iwd_Manager *m) { return m ? m->last_error : NULL; } + +void +iwd_manager_clear_error(Iwd_Manager *m) +{ + if (!m || !m->last_error) return; + free(m->last_error); + m->last_error = NULL; +} + +void +iwd_manager_report_error(Iwd_Manager *m, const char *fmt, ...) +{ + if (!m || !fmt) return; + char buf[256]; + va_list ap; + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + /* stderr keeps the dev-visible trail; the stashed copy drives the UI. */ + fprintf(stderr, "e_iwd: %s\n", buf); + free(m->last_error); + m->last_error = strdup(buf); + iwd_manager_notify(m); +} + /* ----- state aggregation ---------------------------------------------- */ static void @@ -281,6 +311,7 @@ iwd_manager_free(Iwd_Manager *m) eina_hash_free(m->networks); Listener *li; EINA_LIST_FREE(m->listeners, li) free(li); + free(m->last_error); free(m); } diff --git a/src/iwd/iwd_manager.h b/src/iwd/iwd_manager.h index 900fad4..f23d3b5 100644 --- a/src/iwd/iwd_manager.h +++ b/src/iwd/iwd_manager.h @@ -36,6 +36,15 @@ void iwd_manager_listener_del (Iwd_Manager *m, Iwd_Manager_Cb cb, void *data); /* Internal: invoked by sub-objects when their state changes. */ void iwd_manager_notify (Iwd_Manager *m); +/* Latest user-facing error string, or NULL. Owned by the manager. + * Cleared on next successful state change or by iwd_manager_clear_error. */ +const char *iwd_manager_last_error (const Iwd_Manager *m); +void iwd_manager_clear_error(Iwd_Manager *m); + +/* Stash a one-shot error message and notify listeners. Used by D-Bus reply + * callbacks when iwd refuses a Connect/Forget/Set(Powered)/etc. */ +void iwd_manager_report_error(Iwd_Manager *m, const char *fmt, ...); + /* The UI installs its passphrase prompt here. The handler must * eventually call iwd_agent_reply()/iwd_agent_cancel() with the request. */ void iwd_manager_set_passphrase_handler(Iwd_Manager *m, diff --git a/src/iwd/iwd_network.c b/src/iwd/iwd_network.c index 18a84d5..29da8cd 100644 --- a/src/iwd/iwd_network.c +++ b/src/iwd/iwd_network.c @@ -2,7 +2,6 @@ #include "iwd_dbus.h" #include "iwd_props.h" #include "iwd_manager.h" -#include #include #include @@ -80,15 +79,36 @@ iwd_network_free(Iwd_Network *n) free(n); } +typedef struct +{ + Iwd_Network *n; + char *ssid; +} _Net_Reply_Ctx; + static void _on_connect_reply(void *data, const Eldbus_Message *msg, Eldbus_Pending *p EINA_UNUSED) { + _Net_Reply_Ctx *ctx = data; const char *en, *em; - const char *ssid = data; - if (eldbus_message_error_get(msg, &en, &em)) - fprintf(stderr, "e_iwd: connect to '%s' failed: %s: %s\n", - ssid ? ssid : "?", en, em); - free(data); + if (eldbus_message_error_get(msg, &en, &em) && ctx->n->manager) + iwd_manager_report_error(ctx->n->manager, + "Connect to '%s' failed: %s", + ctx->ssid ? ctx->ssid : "?", em ? em : en); + free(ctx->ssid); + free(ctx); +} + +static void +_on_forget_reply(void *data, const Eldbus_Message *msg, Eldbus_Pending *p EINA_UNUSED) +{ + _Net_Reply_Ctx *ctx = data; + const char *en, *em; + if (eldbus_message_error_get(msg, &en, &em) && ctx->n->manager) + iwd_manager_report_error(ctx->n->manager, + "Forget '%s' failed: %s", + ctx->ssid ? ctx->ssid : "?", em ? em : en); + free(ctx->ssid); + free(ctx); } int @@ -104,6 +124,16 @@ iwd_network_signal_tier(const Iwd_Network *n) return 1; } +static _Net_Reply_Ctx * +_reply_ctx_new(Iwd_Network *n) +{ + _Net_Reply_Ctx *ctx = calloc(1, sizeof(*ctx)); + if (!ctx) return NULL; + ctx->n = n; + ctx->ssid = n->ssid ? strdup(n->ssid) : NULL; + return ctx; +} + void iwd_network_connect(Iwd_Network *n) { @@ -111,7 +141,7 @@ iwd_network_connect(Iwd_Network *n) /* Network.Connect() takes no args; iwd will dial the registered Agent * for a passphrase if needed. */ eldbus_proxy_call(n->proxy, "Connect", _on_connect_reply, - n->ssid ? strdup(n->ssid) : NULL, -1, ""); + _reply_ctx_new(n), -1, ""); } void @@ -124,7 +154,7 @@ iwd_network_forget(Iwd_Network *n) Eldbus_Proxy *kp = eldbus_proxy_get(kobj, IWD_IFACE_KNOWN_NETWORK); if (kp) { - eldbus_proxy_call(kp, "Forget", NULL, NULL, -1, ""); + eldbus_proxy_call(kp, "Forget", _on_forget_reply, _reply_ctx_new(n), -1, ""); eldbus_proxy_unref(kp); } eldbus_object_unref(kobj);