summaryrefslogtreecommitdiff
path: root/lib/network
diff options
context:
space:
mode:
Diffstat (limited to 'lib/network')
-rw-r--r--lib/network/accesspoint.vala49
-rw-r--r--lib/network/config.vala.in6
-rw-r--r--lib/network/meson.build80
-rw-r--r--lib/network/network.vala206
-rw-r--r--lib/network/version1
-rw-r--r--lib/network/vpn.vala1
-rw-r--r--lib/network/wifi.vala182
-rw-r--r--lib/network/wired.vala73
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;
+ }
+}