Phase 0: scaffold e_iwd Enlightenment module

Meson build, module entry points, and stub layout for the iwd backend
(D-Bus client, gadget, popup, config, UI widgets). Bodies are TODOs;
this compiles against EFL/E headers but performs no D-Bus work yet.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
nemunaire 2026-04-08 22:33:58 +07:00
commit b9eb5de878
29 changed files with 628 additions and 0 deletions

36
.gitignore vendored Normal file
View file

@ -0,0 +1,36 @@
# Build directories
.cache/
build/
builddir/
# Meson files
.mesonpy*
compile_commands.json
# Compiled files
*.o
*.so
*.a
*.la
*.lo
# Editor files
*~
*.swp
*.swo
.*.sw?
*.bak
.vscode/
.idea/
# System files
.DS_Store
Thumbs.db
# Generated files
config.h
*.edj
# Core dumps
core
core.*
vgcore.*

33
LICENSE Normal file
View file

@ -0,0 +1,33 @@
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies of the Software and its Copyright notices. In addition publicly
documented acknowledgment must be given that this software has been used if no
source code of this software is made available publicly. Making the source
available publicly means including the source for this software with the
distribution, or a method to get this software via some reasonable mechanism
(electronic transfer via a network or media) as well as making an offer to
supply the source on request. This Copyright notice serves as an offer to
supply the source on on request as well. Instead of this, supplying
acknowledgments of use of this software in either Copyright notices, Manuals,
Publicity and Marketing documents or any documentation provided with any
product containing this software. This License does not apply to any software
that links to the libraries provided by this software (statically or
dynamically), but only to the software provided.
Please see the COPYING-PLAIN for a plain-english explanation of this notice
and its intent.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

30
README.md Normal file
View file

@ -0,0 +1,30 @@
# e_iwd
Enlightenment module for Wi-Fi management via [iwd](https://iwd.wiki.kernel.org/),
a native replacement for the ConnMan-based econnman gadget.
See `CLAUDE.md` for the full PRD and implementation plan.
## Status
Phase 0 — scaffolding only. Nothing connects to D-Bus yet.
## Build
meson setup build
ninja -C build
sudo ninja -C build install
Requires: `enlightenment`, `elementary`, `eldbus` (pkg-config).
## Layout
src/
e_mod_main.c module entry points
e_mod_gadget.c shelf gadget
e_mod_popup.c popup UI
e_mod_config.c persistent settings
iwd/ D-Bus client to net.connman.iwd
ui/ reusable EFL widgets
data/
module.desktop

2
data/meson.build Normal file
View file

@ -0,0 +1,2 @@
install_data('module.desktop', install_dir : module_dir)
# TODO: build and install e-module-iwd.edj theme

6
data/module.desktop Normal file
View file

@ -0,0 +1,6 @@
[Desktop Entry]
Type=Link
Name=iwd
Icon=e-module-iwd
Comment=Wi-Fi management via iwd
X-Enlightenment-ModuleType=utils

21
meson.build Normal file
View file

@ -0,0 +1,21 @@
project('e_iwd', 'c',
version : '0.1.0',
license : 'MIT',
default_options : ['c_std=gnu99', 'warning_level=2'])
cc = meson.get_compiler('c')
eldbus = dependency('eldbus')
elementary = dependency('elementary')
enlightenment = dependency('enlightenment')
module_arch = enlightenment.get_variable(pkgconfig: 'module_arch',
default_value: 'linux-gnu-@0@'.format(host_machine.cpu()))
module_dir = join_paths(get_option('libdir'), 'enlightenment', 'modules', 'iwd')
add_project_arguments('-DPACKAGE="e_iwd"',
'-DPACKAGE_VERSION="@0@"'.format(meson.project_version()),
language : 'c')
subdir('src')
subdir('data')

26
src/e_mod_config.c Normal file
View file

@ -0,0 +1,26 @@
#include "e_mod_main.h"
#include "e_mod_config.h"
E_Iwd_Config *e_iwd_config = NULL;
void
e_iwd_config_load(void)
{
/* TODO: register E_Config_DD and load saved config */
e_iwd_config = E_NEW(E_Iwd_Config, 1);
e_iwd_config->auto_connect = 1;
e_iwd_config->show_hidden = 0;
e_iwd_config->refresh_interval = 5;
}
void
e_iwd_config_save(void)
{
/* TODO: e_config_domain_save */
}
void
e_iwd_config_dialog_show(void)
{
/* TODO: build E_Config_Dialog */
}

20
src/e_mod_config.h Normal file
View file

@ -0,0 +1,20 @@
#ifndef E_MOD_CONFIG_H
#define E_MOD_CONFIG_H
typedef struct _E_Iwd_Config E_Iwd_Config;
struct _E_Iwd_Config
{
int auto_connect;
int show_hidden;
int refresh_interval;
char *preferred_adapter;
};
extern E_Iwd_Config *e_iwd_config;
void e_iwd_config_load(void);
void e_iwd_config_save(void);
void e_iwd_config_dialog_show(void);
#endif

23
src/e_mod_gadget.c Normal file
View file

@ -0,0 +1,23 @@
#include "e_mod_main.h"
#include "e_mod_gadget.h"
#include "e_mod_popup.h"
/* TODO: register with E gadget system, draw status icon, handle clicks */
void
e_iwd_gadget_init(void)
{
/* TODO: e_gadget_type_add("iwd", _create_cb, NULL); */
}
void
e_iwd_gadget_shutdown(void)
{
/* TODO: e_gadget_type_del("iwd"); */
}
void
e_iwd_gadget_update(void)
{
/* TODO: refresh icon/tooltip from current iwd_manager state */
}

8
src/e_mod_gadget.h Normal file
View file

@ -0,0 +1,8 @@
#ifndef E_MOD_GADGET_H
#define E_MOD_GADGET_H
void e_iwd_gadget_init(void);
void e_iwd_gadget_shutdown(void);
void e_iwd_gadget_update(void);
#endif

56
src/e_mod_main.c Normal file
View file

@ -0,0 +1,56 @@
#include "e_mod_main.h"
#include "iwd/iwd_manager.h"
#include "e_mod_gadget.h"
#include "e_mod_config.h"
E_Iwd_Module *e_iwd = NULL;
EAPI E_Module_Api e_modapi = { E_MODULE_API_VERSION, "iwd" };
EAPI void *
e_modapi_init(E_Module *m)
{
e_iwd = E_NEW(E_Iwd_Module, 1);
e_iwd->module = m;
if (!eldbus_init())
{
E_FREE(e_iwd);
return NULL;
}
e_iwd->conn = eldbus_connection_get(ELDBUS_CONNECTION_TYPE_SYSTEM);
if (!e_iwd->conn)
{
eldbus_shutdown();
E_FREE(e_iwd);
return NULL;
}
e_iwd_config_load();
e_iwd->manager = iwd_manager_new(e_iwd->conn);
e_iwd_gadget_init();
return m;
}
EAPI int
e_modapi_shutdown(E_Module *m EINA_UNUSED)
{
if (!e_iwd) return 1;
e_iwd_gadget_shutdown();
if (e_iwd->manager) iwd_manager_free(e_iwd->manager);
e_iwd_config_save();
if (e_iwd->conn) eldbus_connection_unref(e_iwd->conn);
eldbus_shutdown();
E_FREE(e_iwd);
return 1;
}
EAPI int
e_modapi_save(E_Module *m EINA_UNUSED)
{
e_iwd_config_save();
return 1;
}

27
src/e_mod_main.h Normal file
View file

@ -0,0 +1,27 @@
#ifndef E_MOD_MAIN_H
#define E_MOD_MAIN_H
#include <e.h>
#include <Eldbus.h>
#include <Elementary.h>
typedef struct _E_Iwd_Module E_Iwd_Module;
struct _E_Iwd_Module
{
E_Module *module;
Eldbus_Connection *conn;
void *manager; /* Iwd_Manager * */
void *gadget; /* gadget instance */
void *config; /* E_Config_Dialog data */
};
extern E_Iwd_Module *e_iwd;
EAPI extern E_Module_Api e_modapi;
EAPI void *e_modapi_init (E_Module *m);
EAPI int e_modapi_shutdown (E_Module *m);
EAPI int e_modapi_save (E_Module *m);
#endif

11
src/e_mod_popup.c Normal file
View file

@ -0,0 +1,11 @@
#include "e_mod_main.h"
#include "e_mod_popup.h"
#include "ui/wifi_list.h"
#include "ui/wifi_status.h"
/* TODO: build the popup window with current connection panel,
* network list, and action buttons. */
void e_iwd_popup_toggle(Evas_Object *anchor EINA_UNUSED) { }
void e_iwd_popup_close(void) { }
void e_iwd_popup_refresh(void) { }

10
src/e_mod_popup.h Normal file
View file

@ -0,0 +1,10 @@
#ifndef E_MOD_POPUP_H
#define E_MOD_POPUP_H
#include <Evas.h>
void e_iwd_popup_toggle(Evas_Object *anchor);
void e_iwd_popup_close(void);
void e_iwd_popup_refresh(void);
#endif

32
src/iwd/iwd_dbus.c Normal file
View file

@ -0,0 +1,32 @@
#include "iwd_dbus.h"
#include <Eina.h>
struct _Iwd_Dbus
{
Eldbus_Connection *conn;
Eldbus_Object *root;
Eldbus_Proxy *object_manager;
Eldbus_Signal_Handler *name_owner_sh;
};
/* TODO:
* - watch IWD_BUS_NAME owner (NameOwnerChanged) for restart handling
* - call org.freedesktop.DBus.ObjectManager.GetManagedObjects on "/"
* - listen for InterfacesAdded / InterfacesRemoved
* - dispatch new objects to iwd_manager
*/
Iwd_Dbus *
iwd_dbus_new(Eldbus_Connection *conn)
{
Iwd_Dbus *d = calloc(1, sizeof(*d));
d->conn = conn;
return d;
}
void
iwd_dbus_free(Iwd_Dbus *d)
{
if (!d) return;
free(d);
}

20
src/iwd/iwd_dbus.h Normal file
View file

@ -0,0 +1,20 @@
#ifndef IWD_DBUS_H
#define IWD_DBUS_H
#include <Eldbus.h>
#define IWD_BUS_NAME "net.connman.iwd"
#define IWD_IFACE_ADAPTER "net.connman.iwd.Adapter"
#define IWD_IFACE_DEVICE "net.connman.iwd.Device"
#define IWD_IFACE_STATION "net.connman.iwd.Station"
#define IWD_IFACE_NETWORK "net.connman.iwd.Network"
#define IWD_IFACE_KNOWN_NETWORK "net.connman.iwd.KnownNetwork"
#define IWD_IFACE_AGENT_MANAGER "net.connman.iwd.AgentManager"
#define IWD_IFACE_AGENT "net.connman.iwd.Agent"
typedef struct _Iwd_Dbus Iwd_Dbus;
Iwd_Dbus *iwd_dbus_new (Eldbus_Connection *conn);
void iwd_dbus_free(Iwd_Dbus *d);
#endif

26
src/iwd/iwd_device.c Normal file
View file

@ -0,0 +1,26 @@
#include "iwd_device.h"
Iwd_Device *
iwd_device_new(Eldbus_Connection *conn EINA_UNUSED, const char *path)
{
Iwd_Device *d = calloc(1, sizeof(*d));
if (path) d->path = strdup(path);
/* TODO: build proxies for Device + Station; subscribe to PropertiesChanged */
return d;
}
void
iwd_device_free(Iwd_Device *d)
{
if (!d) return;
free(d->path);
free(d->name);
free(d->address);
free(d);
}
void
iwd_device_set_powered(Iwd_Device *d EINA_UNUSED, Eina_Bool on EINA_UNUSED)
{
/* TODO: Set("Powered") on Device interface */
}

22
src/iwd/iwd_device.h Normal file
View file

@ -0,0 +1,22 @@
#ifndef IWD_DEVICE_H
#define IWD_DEVICE_H
#include <Eina.h>
#include <Eldbus.h>
typedef struct _Iwd_Device Iwd_Device;
struct _Iwd_Device
{
char *path; /* D-Bus object path */
char *name;
char *address;
Eina_Bool powered;
Eldbus_Proxy *station; /* may be NULL */
};
Iwd_Device *iwd_device_new (Eldbus_Connection *conn, const char *path);
void iwd_device_free(Iwd_Device *d);
void iwd_device_set_powered(Iwd_Device *d, Eina_Bool on);
#endif

44
src/iwd/iwd_manager.c Normal file
View file

@ -0,0 +1,44 @@
#include "iwd_manager.h"
#include "iwd_dbus.h"
struct _Iwd_Manager
{
Iwd_Dbus *dbus;
Eina_List *devices; /* Iwd_Device * */
Eina_List *listeners;
Iwd_State state;
};
/* TODO:
* - Build the device/station/network tree from iwd_dbus events
* - Aggregate state from active station
* - Notify listeners on any change
*/
Iwd_Manager *
iwd_manager_new(Eldbus_Connection *conn)
{
Iwd_Manager *m = calloc(1, sizeof(*m));
m->dbus = iwd_dbus_new(conn);
m->state = IWD_STATE_OFF;
return m;
}
void
iwd_manager_free(Iwd_Manager *m)
{
if (!m) return;
iwd_dbus_free(m->dbus);
eina_list_free(m->devices);
eina_list_free(m->listeners);
free(m);
}
Iwd_State iwd_manager_state(const Iwd_Manager *m) { return m ? m->state : IWD_STATE_OFF; }
const Eina_List *iwd_manager_devices(const Iwd_Manager *m) { return m ? m->devices : NULL; }
void iwd_manager_scan_request(Iwd_Manager *m EINA_UNUSED) { /* TODO */ }
void iwd_manager_set_powered (Iwd_Manager *m EINA_UNUSED, Eina_Bool on EINA_UNUSED) { /* TODO */ }
void iwd_manager_listener_add(Iwd_Manager *m EINA_UNUSED, Iwd_Manager_Cb cb EINA_UNUSED, void *data EINA_UNUSED) { /* TODO */ }
void iwd_manager_listener_del(Iwd_Manager *m EINA_UNUSED, Iwd_Manager_Cb cb EINA_UNUSED, void *data EINA_UNUSED) { /* TODO */ }

32
src/iwd/iwd_manager.h Normal file
View file

@ -0,0 +1,32 @@
#ifndef IWD_MANAGER_H
#define IWD_MANAGER_H
#include <Eldbus.h>
#include <Eina.h>
typedef enum {
IWD_STATE_OFF,
IWD_STATE_IDLE,
IWD_STATE_SCANNING,
IWD_STATE_CONNECTING,
IWD_STATE_CONNECTED,
IWD_STATE_ERROR,
} Iwd_State;
typedef struct _Iwd_Manager Iwd_Manager;
Iwd_Manager *iwd_manager_new (Eldbus_Connection *conn);
void iwd_manager_free(Iwd_Manager *m);
Iwd_State iwd_manager_state (const Iwd_Manager *m);
const Eina_List *iwd_manager_devices (const Iwd_Manager *m);
void iwd_manager_scan_request (Iwd_Manager *m);
void iwd_manager_set_powered (Iwd_Manager *m, Eina_Bool on);
/* Event callback signature for UI layer */
typedef void (*Iwd_Manager_Cb)(void *data, Iwd_Manager *m);
void iwd_manager_listener_add (Iwd_Manager *m, Iwd_Manager_Cb cb, void *data);
void iwd_manager_listener_del (Iwd_Manager *m, Iwd_Manager_Cb cb, void *data);
#endif

22
src/iwd/iwd_network.c Normal file
View file

@ -0,0 +1,22 @@
#include "iwd_network.h"
Iwd_Network *
iwd_network_new(Eldbus_Connection *conn EINA_UNUSED, const char *path)
{
Iwd_Network *n = calloc(1, sizeof(*n));
if (path) n->path = strdup(path);
/* TODO: read Name, Type, KnownNetwork properties */
return n;
}
void
iwd_network_free(Iwd_Network *n)
{
if (!n) return;
free(n->path);
free(n->ssid);
free(n);
}
void iwd_network_connect(Iwd_Network *n EINA_UNUSED) { /* TODO: Network.Connect() */ }
void iwd_network_forget (Iwd_Network *n EINA_UNUSED) { /* TODO: KnownNetwork.Forget() */ }

32
src/iwd/iwd_network.h Normal file
View file

@ -0,0 +1,32 @@
#ifndef IWD_NETWORK_H
#define IWD_NETWORK_H
#include <Eina.h>
#include <Eldbus.h>
typedef enum {
IWD_SEC_OPEN,
IWD_SEC_PSK,
IWD_SEC_8021X,
IWD_SEC_WEP,
} Iwd_Security;
typedef struct _Iwd_Network Iwd_Network;
struct _Iwd_Network
{
char *path;
char *ssid;
Iwd_Security security;
int signal; /* 0..100 */
Eina_Bool known;
Eina_Bool connected;
};
Iwd_Network *iwd_network_new (Eldbus_Connection *conn, const char *path);
void iwd_network_free(Iwd_Network *n);
void iwd_network_connect (Iwd_Network *n);
void iwd_network_forget (Iwd_Network *n);
#endif

23
src/meson.build Normal file
View file

@ -0,0 +1,23 @@
e_iwd_sources = [
'e_mod_main.c',
'e_mod_config.c',
'e_mod_gadget.c',
'e_mod_popup.c',
'iwd/iwd_dbus.c',
'iwd/iwd_manager.c',
'iwd/iwd_device.c',
'iwd/iwd_network.c',
'ui/wifi_list.c',
'ui/wifi_auth.c',
'ui/wifi_status.c',
]
shared_module('module',
e_iwd_sources,
name_prefix : '',
name_suffix : 'so',
dependencies : [eldbus, elementary, enlightenment],
include_directories : include_directories('.', 'iwd', 'ui'),
install : true,
install_dir : join_paths(module_dir, module_arch),
)

12
src/ui/wifi_auth.c Normal file
View file

@ -0,0 +1,12 @@
#include "wifi_auth.h"
/* TODO: modal dialog with password entry + "remember" checkbox.
* Bound to the iwd Agent's RequestPassphrase reply. */
void
wifi_auth_prompt(Evas_Object *parent EINA_UNUSED,
const char *ssid EINA_UNUSED,
Wifi_Auth_Cb cb EINA_UNUSED,
void *data EINA_UNUSED)
{
}

11
src/ui/wifi_auth.h Normal file
View file

@ -0,0 +1,11 @@
#ifndef WIFI_AUTH_H
#define WIFI_AUTH_H
#include <Elementary.h>
typedef void (*Wifi_Auth_Cb)(void *data, const char *passphrase, Eina_Bool remember);
void wifi_auth_prompt(Evas_Object *parent, const char *ssid,
Wifi_Auth_Cb cb, void *data);
#endif

13
src/ui/wifi_list.c Normal file
View file

@ -0,0 +1,13 @@
#include "wifi_list.h"
/* TODO: Genlist of networks, sorted (known first, then signal desc),
* with security icon, signal bars, and click connect/auth flow. */
Evas_Object *
wifi_list_add(Evas_Object *parent)
{
Evas_Object *gl = elm_genlist_add(parent);
return gl;
}
void wifi_list_refresh(Evas_Object *list EINA_UNUSED) { /* TODO */ }

9
src/ui/wifi_list.h Normal file
View file

@ -0,0 +1,9 @@
#ifndef WIFI_LIST_H
#define WIFI_LIST_H
#include <Elementary.h>
Evas_Object *wifi_list_add(Evas_Object *parent);
void wifi_list_refresh(Evas_Object *list);
#endif

12
src/ui/wifi_status.c Normal file
View file

@ -0,0 +1,12 @@
#include "wifi_status.h"
/* TODO: current connection summary widget (SSID, signal, IP, Disconnect). */
Evas_Object *
wifi_status_add(Evas_Object *parent)
{
Evas_Object *box = elm_box_add(parent);
return box;
}
void wifi_status_refresh(Evas_Object *o EINA_UNUSED) { /* TODO */ }

9
src/ui/wifi_status.h Normal file
View file

@ -0,0 +1,9 @@
#ifndef WIFI_STATUS_H
#define WIFI_STATUS_H
#include <Elementary.h>
Evas_Object *wifi_status_add(Evas_Object *parent);
void wifi_status_refresh(Evas_Object *o);
#endif