diff options
Diffstat (limited to 'lib/network')
-rw-r--r-- | lib/network/accesspoint.vala | 49 | ||||
-rw-r--r-- | lib/network/config.vala.in | 6 | ||||
-rw-r--r-- | lib/network/meson.build | 80 | ||||
-rw-r--r-- | lib/network/network.vala | 206 | ||||
-rw-r--r-- | lib/network/version | 1 | ||||
-rw-r--r-- | lib/network/vpn.vala | 1 | ||||
-rw-r--r-- | lib/network/wifi.vala | 182 | ||||
-rw-r--r-- | lib/network/wired.vala | 73 |
8 files changed, 598 insertions, 0 deletions
diff --git a/lib/network/accesspoint.vala b/lib/network/accesspoint.vala new file mode 100644 index 0000000..3c51018 --- /dev/null +++ b/lib/network/accesspoint.vala @@ -0,0 +1,49 @@ +public class AstalNetwork.AccessPoint : Object { + private Wifi wifi; + private NM.AccessPoint ap; + + public uint bandwidth { get { return ap.bandwidth; } } + public string bssid { owned get { return ap.bssid; } } + public uint frequency { get { return ap.frequency; } } + public int last_seen { get { return ap.last_seen; } } + public uint max_bitrate { get { return ap.max_bitrate; } } + public uint8 strength { get { return ap.strength; } } + public string icon_name { get; private set; } + public NM.80211Mode mode { get { return ap.mode; } } + public NM.80211ApFlags flags { get { return ap.flags; } } + public NM.80211ApSecurityFlags rsn_flags { get { return ap.rsn_flags; } } + public NM.80211ApSecurityFlags wpa_flags { get { return ap.wpa_flags; } } + + public string? ssid { + owned get { + if (ap.ssid == null) + return null; + + return (string)NM.Utils.ssid_to_utf8(ap.ssid.get_data()); + } + } + + internal AccessPoint(Wifi wifi, NM.AccessPoint ap) { + this.wifi = wifi; + this.ap = ap; + ap.notify.connect((pspec) => { + if (get_class().find_property(pspec.name) != null) + notify_property(pspec.name); + if (pspec.name == "strength") + icon_name = _icon(); + }); + icon_name = _icon(); + } + + private string _icon() { + if (strength >= 80) return Wifi.ICON_EXCELLENT; + if (strength >= 60) return Wifi.ICON_GOOD; + if (strength >= 40) return Wifi.ICON_OK; + if (strength >= 20) return Wifi.ICON_WEAK; + return Wifi.ICON_NONE; + } + + // TODO: connect to ap + // public signal void auth(); + // public void try_connect(string? password) { } +} diff --git a/lib/network/config.vala.in b/lib/network/config.vala.in new file mode 100644 index 0000000..dbec0f3 --- /dev/null +++ b/lib/network/config.vala.in @@ -0,0 +1,6 @@ +namespace AstalNetwork { + public const int MAJOR_VERSION = @MAJOR_VERSION@; + public const int MINOR_VERSION = @MINOR_VERSION@; + public const int MICRO_VERSION = @MICRO_VERSION@; + public const string VERSION = "@VERSION@"; +} diff --git a/lib/network/meson.build b/lib/network/meson.build new file mode 100644 index 0000000..17ea358 --- /dev/null +++ b/lib/network/meson.build @@ -0,0 +1,80 @@ +project( + 'astal-network', + 'vala', + 'c', + version: run_command('cat', join_paths(meson.project_source_root(), 'version')).stdout().strip(), + meson_version: '>= 0.62.0', + default_options: [ + 'warning_level=2', + 'werror=false', + 'c_std=gnu11', + ], +) + +version_split = meson.project_version().split('.') +api_version = version_split[0] + '.' + version_split[1] +gir = 'AstalNetwork-' + api_version + '.gir' +typelib = 'AstalNetwork-' + api_version + '.typelib' + +config = configure_file( + input: 'config.vala.in', + output: 'config.vala', + configuration: { + 'VERSION': meson.project_version(), + 'MAJOR_VERSION': version_split[0], + 'MINOR_VERSION': version_split[1], + 'MICRO_VERSION': version_split[2], + }, +) + +deps = [ + dependency('glib-2.0'), + dependency('gobject-2.0'), + dependency('libnm'), +] + +sources = [ + config, + 'network.vala', + 'wifi.vala', + 'wired.vala', + 'wired.vala', + 'accesspoint.vala', +] + +lib = library( + meson.project_name(), + sources, + dependencies: deps, + vala_header: meson.project_name() + '.h', + vala_vapi: meson.project_name() + '-' + api_version + '.vapi', + vala_gir: gir, + version: meson.project_version(), + install: true, + install_dir: [true, true, true, true], +) + +import('pkgconfig').generate( + lib, + name: meson.project_name(), + filebase: meson.project_name() + '-' + api_version, + version: meson.project_version(), + subdirs: meson.project_name(), + requires: deps, + install_dir: get_option('libdir') / 'pkgconfig', +) + +custom_target( + typelib, + command: [ + find_program('g-ir-compiler'), + '--output', '@OUTPUT@', + '--shared-library', get_option('prefix') / get_option('libdir') / '@PLAINNAME@', + meson.current_build_dir() / gir, + ], + input: lib, + output: typelib, + depends: lib, + install: true, + install_dir: get_option('libdir') / 'girepository-1.0', +) diff --git a/lib/network/network.vala b/lib/network/network.vala new file mode 100644 index 0000000..7c8e466 --- /dev/null +++ b/lib/network/network.vala @@ -0,0 +1,206 @@ +namespace AstalNetwork { + public Network get_default() { + return Network.get_default(); + } +} + +public class AstalNetwork.Network : Object { + private static Network instance; + public static Network get_default() { + if (instance == null) + instance = new Network(); + + return instance; + } + + public NM.Client client { get; private set; } + + public Wifi? wifi { get; private set; } + public Wired? wired { get; private set; } + public Primary primary { get; private set; } + + public Connectivity connectivity { + get { return (Connectivity)client.connectivity; } + } + + public State state { + get { return (State)client.state; } + } + + construct { + try { + client = new NM.Client(); + var wifi_device = (NM.DeviceWifi)get_device(NM.DeviceType.WIFI); + if (wifi_device != null) + wifi = new Wifi(wifi_device); + + var ethernet = (NM.DeviceEthernet)get_device(NM.DeviceType.ETHERNET); + if (ethernet != null) + wired = new Wired(ethernet); + + sync(); + client.notify["primary-connection"].connect(sync); + client.notify["activating-connection"].connect(sync); + + client.notify["state"].connect(() => notify_property("state")); + client.notify["connectivity"].connect(() => notify_property("connectivity")); + } catch (Error err) { + critical(err.message); + } + } + + private NM.Device get_device(NM.DeviceType t) { + var valid = new GenericArray<NM.Device>(); + foreach (var device in client.get_devices()) { + if (device.device_type == t) + valid.add(device); + } + + foreach (var device in valid) { + if (device.active_connection != null) + return device; + } + + return valid.get(0); + } + + private void sync() { + var ac = client.get_primary_connection(); + + if (ac == null) + ac = client.get_activating_connection(); + + if (ac != null) + primary = Primary.from_connection_type(ac.type); + else + primary = Primary.UNKNOWN; + } +} + +public enum AstalNetwork.Primary { + UNKNOWN, + WIRED, + WIFI; + + public string to_string() { + switch (this) { + case WIFI: return "wifi"; + case WIRED: return "wired"; + default: return "unknown"; + } + } + + public static Primary from_connection_type(string type) { + switch (type) { + case "802-11-wireless": return Primary.WIFI; + case "802-3-ethernet": return Primary.WIRED; + default: return Primary.UNKNOWN; + } + } +} + +// alias for NM.State +public enum AstalNetwork.State { + UNKNOWN, + ASLEEP, + DISCONNECTED, + DISCONNECTING, + CONNECTING, + CONNECTED_LOCAL, + CONNECTED_SITE, + CONNECTED_GLOBAL; + + public string to_string() { + switch (this) { + case ASLEEP: return "asleep"; + case DISCONNECTED: return "disconnected"; + case DISCONNECTING: return "disconnecting"; + case CONNECTING: return "connecting"; + case CONNECTED_LOCAL: return "connected_local"; + case CONNECTED_SITE: return "connected_site"; + case CONNECTED_GLOBAL: return "connected_global"; + default: return "unknown"; + } + } +} + + +// alias for NM.ConnectivityState +public enum AstalNetwork.Connectivity { + UNKNOWN, + NONE, + PORTAL, + LIMITED, + FULL; + + public string to_string() { + switch (this) { + case NONE: return "none"; + case PORTAL: return "portal"; + case LIMITED: return "limited"; + case FULL: return "full"; + default: return "unknown"; + } + } +} + +// alias for NM.DeviceState +public enum AstalNetwork.DeviceState { + UNKNOWN, + UNMANAGED, + UNAVAILABLE, + DISCONNECTED, + PREPARE, + CONFIG, + NEED_AUTH, + IP_CONFIG, + IP_CHECK, + SECONDARIES, + ACTIVATED, + DEACTIVATING, + FAILED; + + public string to_string() { + switch (this) { + case UNMANAGED: return "unmanaged"; + case UNAVAILABLE: return "unavailable"; + case DISCONNECTED: return "disconnected"; + case PREPARE: return "prepare"; + case CONFIG: return "config"; + case NEED_AUTH: return "need_auth"; + case IP_CONFIG: return "ip_config"; + case IP_CHECK: return "ip_check"; + case SECONDARIES: return "secondaries"; + case ACTIVATED: return "activated"; + case DEACTIVATING: return "deactivating"; + case FAILED: return "failed"; + default: return "unknown"; + } + + } +} + +public enum AstalNetwork.Internet { + CONNECTED, + CONNECTING, + DISCONNECTED; + + public static Internet from_device(NM.Device device) { + if (device == null || device.active_connection == null) + return DISCONNECTED; + + switch (device.active_connection.state) { + case NM.ActiveConnectionState.ACTIVATED: return CONNECTED; + case NM.ActiveConnectionState.ACTIVATING: return CONNECTING; + default: return DISCONNECTED; + } + } + + public string to_string() { + switch (this) { + case CONNECTED: return "connected"; + case CONNECTING: return "connecting"; + default: return "disconnected"; + } + } +} diff --git a/lib/network/version b/lib/network/version new file mode 100644 index 0000000..6e8bf73 --- /dev/null +++ b/lib/network/version @@ -0,0 +1 @@ +0.1.0 diff --git a/lib/network/vpn.vala b/lib/network/vpn.vala new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/lib/network/vpn.vala @@ -0,0 +1 @@ + diff --git a/lib/network/wifi.vala b/lib/network/wifi.vala new file mode 100644 index 0000000..c9e9881 --- /dev/null +++ b/lib/network/wifi.vala @@ -0,0 +1,182 @@ +public class AstalNetwork.Wifi : Object { + internal const string ICON_EXCELLENT = "network-wireless-signal-excellent-symbolic"; + internal const string ICON_OK = "network-wireless-signal-ok-symbolic"; + internal const string ICON_GOOD = "network-wireless-signal-good-symbolic"; + internal const string ICON_WEAK = "network-wireless-signal-weak-symbolic"; + internal const string ICON_NONE = "network-wireless-signal-none-symbolic"; + internal const string ICON_ACQUIRING = "network-wireless-acquiring-symbolic"; + internal const string ICON_CONNECTED = "network-wireless-connected-symbolic"; + internal const string ICON_DISABLED = "network-wireless-disabled-symbolic"; + internal const string ICON_OFFLINE = "network-wireless-offline-symbolic"; + internal const string ICON_NO_ROUTE = "network-wireless-no-route-symbolic"; + internal const string ICON_HOTSPOT = "network-wireless-hotspot-symbolic"; + + private HashTable<string, AccessPoint> _access_points = + new HashTable<string, AccessPoint>(str_hash, str_equal); + + public NM.DeviceWifi device { get; construct set; } + + public NM.ActiveConnection? active_connection { get; private set; } + private ulong connection_handler = 0; + + public AccessPoint? active_access_point { get; private set; } + private ulong ap_handler = 0; + + public List<weak AccessPoint> access_points { + owned get { return _access_points.get_values(); } + } + + public bool enabled { + get { return device.client.wireless_enabled; } + set { device.client.wireless_enabled = value; } + } + + public Internet internet { get; private set; } + public uint bandwidth { get; private set; } + public string ssid { get; private set; } + public uint8 strength { get; private set; } + public uint frequency { get; private set; } + public DeviceState state { get; private set; } + public string icon_name { get; private set; } + public bool is_hotspot { get; private set; } + public bool scanning { get; private set; } + + internal Wifi(NM.DeviceWifi device) { + this.device = device; + + foreach (var ap in device.access_points) + _access_points.set(ap.bssid, new AccessPoint(this, ap)); + + device.access_point_added.connect((access_point) => { + var ap = (NM.AccessPoint)access_point; + _access_points.set(ap.bssid, new AccessPoint(this, ap)); + notify_property("access-points"); + }); + + device.access_point_removed.connect((access_point) => { + var ap = (NM.AccessPoint)access_point; + _access_points.remove(ap.bssid); + notify_property("access-points"); + }); + + on_active_connection(); + device.notify["active-connection"].connect(on_active_connection); + + on_active_access_point(); + device.notify["active-access-point"].connect(on_active_access_point); + + state = (DeviceState)device.state; + device.client.notify["wireless-enabled"].connect(() => notify_property("enabled")); + device.state_changed.connect((n, o, r) => { + state_changed(n, o, r); + state = (DeviceState)n; + }); + + device.notify.connect(() => { icon_name = _icon(); }); + device.client.notify.connect(() => { icon_name = _icon(); }); + icon_name = _icon(); + } + + public signal void state_changed( + DeviceState new_state, + DeviceState old_state, + NM.DeviceStateReason reaseon + ); + + public void scan() { + scanning = true; + var last_scan = device.last_scan; + device.request_scan_async.begin(null, (_, res) => { + try { + device.request_scan_async.end(res); + Timeout.add(1000, () => { + if (device.last_scan == last_scan) + return Source.CONTINUE; + + scanning = false; + return Source.REMOVE; + }, Priority.DEFAULT); + } catch (Error err) { + critical(err.message); + } + }); + } + + private void on_active_connection() { + if (connection_handler > 0 && active_connection != null) { + active_connection.disconnect(connection_handler); + connection_handler = 0; + active_connection = null; + } + + active_connection = device.active_connection; + is_hotspot = _hotspot(); + if (active_connection != null) { + connection_handler = active_connection.notify["state"].connect(() => { + internet = Internet.from_device(device); + }); + } + } + + private void on_active_access_point_notify() { + bandwidth = active_access_point.bandwidth; + frequency = active_access_point.frequency; + strength = active_access_point.strength; + ssid = active_access_point.ssid; + } + + private void on_active_access_point() { + if (ap_handler > 0 && active_access_point != null) { + active_access_point.disconnect(ap_handler); + ap_handler = 0; + active_access_point = null; + } + + var ap = device.active_access_point; + if (ap != null) { + active_access_point = _access_points.get(ap.bssid); + on_active_access_point_notify(); + ap_handler = active_access_point.notify.connect(on_active_access_point_notify); + } + } + + private string _icon() { + if (!enabled) return ICON_DISABLED; + + var full = device.client.connectivity == NM.ConnectivityState.FULL; + + if (internet == Internet.CONNECTED) { + if (is_hotspot) return ICON_HOTSPOT; + if (!full) return ICON_NO_ROUTE; + if (active_access_point == null) return ICON_CONNECTED; + + if (strength >= 80) return ICON_EXCELLENT; + if (strength >= 60) return ICON_GOOD; + if (strength >= 40) return ICON_OK; + if (strength >= 20) return ICON_WEAK; + + return ICON_NONE; + } + + if (internet == Internet.CONNECTING) { + return ICON_ACQUIRING; + } + + return ICON_OFFLINE; + } + + private bool _hotspot() { + if (device.active_connection == null) + return false; + + var conn = device.active_connection.connection; + if (conn == null) + return false; + + var ip4config = conn.get_setting_ip4_config(); + if (ip4config == null) + return false; + + return ip4config.method == NM.SettingIP4Config.METHOD_SHARED; + } +} diff --git a/lib/network/wired.vala b/lib/network/wired.vala new file mode 100644 index 0000000..68cf460 --- /dev/null +++ b/lib/network/wired.vala @@ -0,0 +1,73 @@ +public class AstalNetwork.Wired : Object { + private const string ICON_CONNECTED = "network-wired-symbolic"; + private const string ICON_DISCONNECTED = "network-wired-disconnected-symbolic"; + private const string ICON_ACQUIRING = "network-wired-acquiring-symbolic"; + private const string ICON_NO_ROUTE = "network-wired-no-route-symbolic"; + + public NM.DeviceEthernet device { get; construct set; } + + public NM.ActiveConnection connection; + private ulong connection_handler = 0; + + internal Wired(NM.DeviceEthernet device) { + this.device = device; + + speed = device.speed; + state = (DeviceState)device.state; + icon_name = _icon(); + + device.notify.connect((pspec) => { + if (pspec.name == "speed") { + speed = device.speed; + } + if (pspec.name == "state") { + state = (DeviceState)device.state; + } + if (pspec.name == "active-connection") { + on_active_connection(); + } + icon_name = _icon(); + }); + + device.client.notify.connect(() => { icon_name = _icon(); }); + + on_active_connection(); + icon_name = _icon(); + } + + private void on_active_connection() { + if (connection_handler > 0 && connection != null) { + connection.disconnect(connection_handler); + connection_handler = 0; + connection = null; + } + + connection = device.active_connection; + if (connection != null) { + connection_handler = connection.notify["state"].connect(() => { + internet = Internet.from_device(device); + }); + } + } + + public uint speed { get; private set; } + public Internet internet { get; private set; } + public DeviceState state { get; private set; } + public string icon_name { get; private set; } + + private string _icon() { + var full = device.client.connectivity == NM.ConnectivityState.FULL; + + if (internet == Internet.CONNECTING) { + return ICON_ACQUIRING; + } + + if (internet == Internet.CONNECTED) { + if (!full) return ICON_NO_ROUTE; + + return ICON_CONNECTED; + } + + return ICON_DISCONNECTED; + } +} |