summaryrefslogtreecommitdiff
path: root/csrc/services/network-service.c
diff options
context:
space:
mode:
Diffstat (limited to 'csrc/services/network-service.c')
-rw-r--r--csrc/services/network-service.c1577
1 files changed, 1577 insertions, 0 deletions
diff --git a/csrc/services/network-service.c b/csrc/services/network-service.c
new file mode 100644
index 0000000..b2d1334
--- /dev/null
+++ b/csrc/services/network-service.c
@@ -0,0 +1,1577 @@
+#include "services/network-service.h"
+#include <NetworkManager.h>
+#include <gtk/gtk.h>
+#include <stdint.h>
+
+typedef struct _ApNetworkMembership ApNetworkMembership;
+
+#define SCAN_REFRESH_INTERVAL 10000
+
+//
+// FoobarNetwork:
+//
+// Represents a Wi-Fi network, identified by its SSID. Instances contain a set of NMAccessPoints and the strength is
+// updated to match that of the best available AP.
+//
+
+struct _FoobarNetwork
+{
+ GObject parent_instance;
+ GBytes* ssid;
+ gchar* name;
+ gint strength;
+ gboolean is_active;
+ GHashTable* aps;
+};
+
+enum
+{
+ NETWORK_PROP_NAME = 1,
+ NETWORK_PROP_STRENGTH,
+ NETWORK_PROP_IS_ACTIVE,
+ N_NETWORK_PROPS,
+};
+
+static GParamSpec* network_props[N_NETWORK_PROPS] = { 0 };
+
+static void foobar_network_class_init ( FoobarNetworkClass* klass );
+static void foobar_network_init ( FoobarNetwork* self );
+static void foobar_network_get_property ( GObject* object,
+ guint prop_id,
+ GValue* value,
+ GParamSpec* pspec );
+static void foobar_network_finalize ( GObject* object );
+static FoobarNetwork* foobar_network_new ( void );
+static void foobar_network_set_ssid ( FoobarNetwork* self,
+ GBytes* value );
+static void foobar_network_set_active ( FoobarNetwork* self,
+ gboolean value );
+static void foobar_network_add_ap ( FoobarNetwork* self,
+ NMAccessPoint* ap );
+static void foobar_network_remove_ap ( FoobarNetwork* self,
+ NMAccessPoint* ap );
+static void foobar_network_update_strength ( FoobarNetwork* self );
+static void foobar_network_handle_strength_change( GObject* ap,
+ GParamSpec* pspec,
+ gpointer userdata );
+
+G_DEFINE_FINAL_TYPE( FoobarNetwork, foobar_network, G_TYPE_OBJECT )
+
+//
+// FoobarNetworkAdapterState:
+//
+// The current connection/connectivity state of a network adapter.
+//
+
+G_DEFINE_ENUM_TYPE(
+ FoobarNetworkAdapterState,
+ foobar_network_adapter_state,
+ G_DEFINE_ENUM_VALUE( FOOBAR_NETWORK_ADAPTER_STATE_DISCONNECTED, "disconnected" ),
+ G_DEFINE_ENUM_VALUE( FOOBAR_NETWORK_ADAPTER_STATE_CONNECTING, "connecting" ),
+ G_DEFINE_ENUM_VALUE( FOOBAR_NETWORK_ADAPTER_STATE_CONNECTED, "connected" ),
+ G_DEFINE_ENUM_VALUE( FOOBAR_NETWORK_ADAPTER_STATE_LIMITED, "limited" ) )
+
+//
+// FoobarNetworkAdapter:
+//
+// Base class for Wi-Fi/ethernet network adapters.
+//
+
+typedef struct _FoobarNetworkAdapterPrivate
+{
+ NMDevice* device;
+ NMActiveConnection* connection;
+ FoobarNetworkAdapterState state;
+ gulong connectivity_handler_id;
+ gulong connection_handler_id;
+ gulong connection_state_handler_id;
+} FoobarNetworkAdapterPrivate;
+
+enum
+{
+ ADAPTER_PROP_STATE = 1,
+ N_ADAPTER_PROPS,
+};
+
+static GParamSpec* adapter_props[N_ADAPTER_PROPS] = { 0 };
+
+static void foobar_network_adapter_class_init ( FoobarNetworkAdapterClass* klass );
+static void foobar_network_adapter_init ( FoobarNetworkAdapter* self );
+static void foobar_network_adapter_get_property ( GObject* object,
+ guint prop_id,
+ GValue* value,
+ GParamSpec* pspec );
+static void foobar_network_adapter_finalize ( GObject* object );
+static void foobar_network_adapter_set_device ( FoobarNetworkAdapter* self,
+ NMDevice* value );
+static void foobar_network_adapter_update_state ( FoobarNetworkAdapter* self );
+static void foobar_network_adapter_handle_state_change ( GObject* sender,
+ GParamSpec* pspec,
+ gpointer userdata );
+static void foobar_network_adapter_handle_connection_change( GObject* sender,
+ GParamSpec* pspec,
+ gpointer userdata );
+
+G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE( FoobarNetworkAdapter, foobar_network_adapter, G_TYPE_OBJECT )
+
+//
+// FoobarNetworkAdapterWired:
+//
+// Represents an ethernet network adapter.
+//
+
+struct _FoobarNetworkAdapterWired
+{
+ FoobarNetworkAdapter parent_instance;
+ NMDeviceEthernet* device;
+ gulong speed_handler_id;
+};
+
+static void foobar_network_adapter_wired_class_init ( FoobarNetworkAdapterWiredClass* klass );
+static void foobar_network_adapter_wired_init ( FoobarNetworkAdapterWired* self );
+static void foobar_network_adapter_wired_get_property ( GObject* object,
+ guint prop_id,
+ GValue* value,
+ GParamSpec* pspec );
+static void foobar_network_adapter_wired_finalize ( GObject* object );
+static FoobarNetworkAdapterWired* foobar_network_adapter_wired_new ( NMDeviceEthernet* device );
+static void foobar_network_adapter_wired_handle_speed_change( GObject* device,
+ GParamSpec* pspec,
+ gpointer userdata );
+
+enum
+{
+ WIRED_PROP_SPEED = 1,
+ N_WIRED_PROPS,
+};
+
+static GParamSpec* wired_props[N_WIRED_PROPS] = { 0 };
+
+G_DEFINE_FINAL_TYPE( FoobarNetworkAdapterWired, foobar_network_adapter_wired, FOOBAR_TYPE_NETWORK_ADAPTER )
+
+//
+// FoobarNetworkAdapterWifi:
+//
+// Represents a Wi-Fi network adapter.
+//
+
+struct _FoobarNetworkAdapterWifi
+{
+ FoobarNetworkAdapter parent_instance;
+ GHashTable* ap_networks;
+ GHashTable* named_networks;
+ GListStore* networks;
+ GtkFilterListModel* filtered_networks;
+ GtkSortListModel* sorted_networks;
+ FoobarNetwork* active;
+ gboolean is_scanning;
+ NMClient* client;
+ NMDeviceWifi* device;
+ gulong enabled_handler_id;
+ gulong active_handler_id;
+ gulong added_handler_id;
+ gulong removed_handler_id;
+ guint refresh_source_id;
+};
+
+enum
+{
+ WIFI_PROP_NETWORKS = 1,
+ WIFI_PROP_ACTIVE,
+ WIFI_PROP_IS_ENABLED,
+ WIFI_PROP_IS_SCANNING,
+ N_WIFI_PROPS,
+};
+
+static GParamSpec* wifi_props[N_WIFI_PROPS] = { 0 };
+
+static void foobar_network_adapter_wifi_class_init ( FoobarNetworkAdapterWifiClass* klass );
+static void foobar_network_adapter_wifi_init ( FoobarNetworkAdapterWifi* self );
+static void foobar_network_adapter_wifi_get_property ( GObject* object,
+ guint prop_id,
+ GValue* value,
+ GParamSpec* pspec );
+static void foobar_network_adapter_wifi_set_property ( GObject* object,
+ guint prop_id,
+ GValue const* value,
+ GParamSpec* pspec );
+static void foobar_network_adapter_wifi_finalize ( GObject* object );
+static FoobarNetworkAdapterWifi* foobar_network_adapter_wifi_new ( NMClient* client,
+ NMDeviceWifi* device );
+static void foobar_network_adapter_wifi_add_ap_to_network ( FoobarNetworkAdapterWifi* self,
+ NMAccessPoint* ap );
+static void foobar_network_adapter_wifi_update_active ( FoobarNetworkAdapterWifi* self );
+static void foobar_network_adapter_wifi_handle_ssid_change ( GObject* ap,
+ GParamSpec* pspec,
+ gpointer userdata );
+static void foobar_network_adapter_wifi_handle_enabled_change( GObject* client,
+ GParamSpec* pspec,
+ gpointer userdata );
+static void foobar_network_adapter_wifi_handle_active_change ( GObject* device,
+ GParamSpec* pspec,
+ gpointer userdata );
+static void foobar_network_adapter_wifi_handle_added ( NMDeviceWifi* device,
+ NMAccessPoint* ap,
+ gpointer userdata );
+static void foobar_network_adapter_wifi_handle_removed ( NMDeviceWifi* device,
+ NMAccessPoint* ap,
+ gpointer userdata );
+static gboolean foobar_network_adapter_wifi_refresh_list ( gpointer userdata );
+static gboolean foobar_network_adapter_wifi_filter_func ( gpointer item,
+ gpointer userdata );
+static gint foobar_network_adapter_wifi_sort_func ( gconstpointer item_a,
+ gconstpointer item_b,
+ gpointer userdata );
+
+G_DEFINE_FINAL_TYPE( FoobarNetworkAdapterWifi, foobar_network_adapter_wifi, FOOBAR_TYPE_NETWORK_ADAPTER )
+
+//
+// ApNetworkMembership:
+//
+// Structure representing an AP's membership in a network. The AP is removed when the structure is destroyed.
+//
+
+struct _ApNetworkMembership
+{
+ NMAccessPoint* ap;
+ FoobarNetwork* network;
+};
+
+static ApNetworkMembership* ap_network_membership_new ( NMAccessPoint* ap,
+ FoobarNetwork* network );
+static void ap_network_membership_destroy( ApNetworkMembership* self );
+
+//
+// FoobarNetworkService:
+//
+// Service monitoring the network state of the computer. This is implemented using the NetworkManager client library.
+//
+
+struct _FoobarNetworkService
+{
+ GObject parent_instance;
+ NMClient* client;
+ FoobarNetworkAdapterWired* wired;
+ FoobarNetworkAdapterWifi* wifi;
+ FoobarNetworkAdapter* active;
+ gulong primary_handler_id;
+ gulong activating_handler_id;
+};
+
+enum
+{
+ PROP_WIRED = 1,
+ PROP_WIFI,
+ PROP_ACTIVE,
+ N_PROPS,
+};
+
+static GParamSpec* props[N_PROPS] = { 0 };
+
+static void foobar_network_service_class_init ( FoobarNetworkServiceClass* klass );
+static void foobar_network_service_init ( FoobarNetworkService* self );
+static void foobar_network_service_get_property ( GObject* object,
+ guint prop_id,
+ GValue* value,
+ GParamSpec* pspec );
+static void foobar_network_service_finalize ( GObject* object );
+static void foobar_network_service_handle_connection_change( GObject* client,
+ GParamSpec* pspec,
+ gpointer userdata );
+static void foobar_network_service_update_active ( FoobarNetworkService* self );
+
+G_DEFINE_FINAL_TYPE( FoobarNetworkService, foobar_network_service, G_TYPE_OBJECT )
+
+// ---------------------------------------------------------------------------------------------------------------------
+// Wi-Fi Network
+// ---------------------------------------------------------------------------------------------------------------------
+
+//
+// Static initialization for networks.
+//
+void foobar_network_class_init( FoobarNetworkClass* klass )
+{
+ GObjectClass* object_klass = G_OBJECT_CLASS( klass );
+ object_klass->get_property = foobar_network_get_property;
+ object_klass->finalize = foobar_network_finalize;
+
+ network_props[NETWORK_PROP_NAME] = g_param_spec_string(
+ "name",
+ "Name",
+ "The advertised SSID of the network.",
+ NULL,
+ G_PARAM_READABLE );
+ network_props[NETWORK_PROP_STRENGTH] = g_param_spec_int(
+ "strength",
+ "Strength",
+ "The strength of the network in percent.",
+ 0,
+ 100,
+ 0,
+ G_PARAM_READABLE );
+ network_props[NETWORK_PROP_IS_ACTIVE] = g_param_spec_boolean(
+ "is-active",
+ "Is Active",
+ "Indicates whether the network is currently used by the client.",
+ FALSE,
+ G_PARAM_READABLE );
+ g_object_class_install_properties( object_klass, N_NETWORK_PROPS, network_props );
+}
+
+//
+// Instance initialization for networks.
+//
+void foobar_network_init( FoobarNetwork* self )
+{
+ self->aps = g_hash_table_new_full( g_direct_hash, g_direct_equal, NULL, NULL );
+}
+
+//
+// Property getter implementation, mapping a property id to a method.
+//
+void foobar_network_get_property(
+ GObject* object,
+ guint prop_id,
+ GValue* value,
+ GParamSpec* pspec )
+{
+ FoobarNetwork* self = (FoobarNetwork*)object;
+
+ switch ( prop_id )
+ {
+ case NETWORK_PROP_NAME:
+ g_value_set_string( value, foobar_network_get_name( self ) );
+ break;
+ case NETWORK_PROP_STRENGTH:
+ g_value_set_int( value, foobar_network_get_strength( self ) );
+ break;
+ case NETWORK_PROP_IS_ACTIVE:
+ g_value_set_boolean( value, foobar_network_is_active( self ) );
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID( object, prop_id, pspec );
+ break;
+ }
+}
+
+//
+// Instance cleanup for networks.
+//
+void foobar_network_finalize( GObject* object )
+{
+ FoobarNetwork* self = (FoobarNetwork*)object;
+
+ GHashTableIter iter;
+ gpointer ap;
+ g_hash_table_iter_init( &iter, self->aps );
+ while ( g_hash_table_iter_next( &iter, &ap, NULL ) )
+ {
+ g_signal_handlers_disconnect_by_data( ap, self );
+ }
+
+ g_clear_pointer( &self->ssid, g_bytes_unref );
+ g_clear_pointer( &self->name, g_free );
+ g_clear_pointer( &self->aps, g_hash_table_unref );
+
+ G_OBJECT_CLASS( foobar_network_parent_class )->finalize( object );
+}
+
+//
+// Create a new network instance.
+//
+FoobarNetwork* foobar_network_new( void )
+{
+ return g_object_new( FOOBAR_TYPE_NETWORK, NULL );
+}
+
+//
+// Get the advertized SSID of the network.
+//
+gchar const* foobar_network_get_name( FoobarNetwork* self )
+{
+ g_return_val_if_fail( FOOBAR_IS_NETWORK( self ), NULL );
+ return self->name;
+}
+
+//
+// Get the current strength percentage value for the network.
+//
+gint foobar_network_get_strength( FoobarNetwork* self )
+{
+ g_return_val_if_fail( FOOBAR_IS_NETWORK( self ), 0 );
+ return self->strength;
+}
+
+//
+// Check whether the network is currently used by the client.
+//
+gboolean foobar_network_is_active( FoobarNetwork* self )
+{
+ g_return_val_if_fail( FOOBAR_IS_NETWORK( self ), FALSE );
+ return self->is_active;
+}
+
+//
+// Update the advertized SSID of the network.
+//
+// This also updates the string representation (which may be a lossy conversion).
+//
+void foobar_network_set_ssid(
+ FoobarNetwork* self,
+ GBytes* value )
+{
+ g_return_if_fail( FOOBAR_IS_NETWORK( self ) );
+
+ if ( self->ssid != value )
+ {
+ g_clear_pointer( &self->ssid, g_bytes_unref );
+ g_clear_pointer( &self->name, g_free );
+
+ if ( value )
+ {
+ self->ssid = g_bytes_ref( value );
+ gsize ssid_len;
+ guint8 const* ssid_data = g_bytes_get_data( self->ssid, &ssid_len );
+ self->name = nm_utils_ssid_to_utf8( ssid_data, ssid_len );
+ }
+
+ g_object_notify_by_pspec( G_OBJECT( self ), network_props[NETWORK_PROP_NAME] );
+ }
+}
+
+//
+// Update the "active" flag of the network. This does not actually activate the network.
+//
+void foobar_network_set_active(
+ FoobarNetwork* self,
+ gboolean value )
+{
+ g_return_if_fail( FOOBAR_IS_NETWORK( self ) );
+
+ value = !!value;
+ if ( self->is_active != value )
+ {
+ self->is_active = value;
+ g_object_notify_by_pspec( G_OBJECT( self ), network_props[NETWORK_PROP_NAME] );
+ }
+}
+
+//
+// Include the specified AP when computing the network's strength.
+//
+void foobar_network_add_ap(
+ FoobarNetwork* self,
+ NMAccessPoint* ap )
+{
+ if ( g_hash_table_add( self->aps, g_object_ref( ap ) ) )
+ {
+ g_signal_connect( ap, "notify::strength", G_CALLBACK( foobar_network_handle_strength_change ), self );
+ foobar_network_update_strength( self );
+ }
+}
+
+//
+// Stop including the specified AP when computing the network's strength.
+//
+void foobar_network_remove_ap(
+ FoobarNetwork* self,
+ NMAccessPoint* ap )
+{
+ if ( g_hash_table_remove( self->aps, ap ) )
+ {
+ g_signal_handlers_disconnect_by_data( ap, self );
+ foobar_network_update_strength( self );
+ }
+}
+
+//
+// Update the network's "strength" property to match that of the best AP for it.
+//
+void foobar_network_update_strength( FoobarNetwork* self )
+{
+ gint strength = 0;
+ GHashTableIter iter;
+ gpointer ap;
+ g_hash_table_iter_init( &iter, self->aps );
+ while ( g_hash_table_iter_next( &iter, &ap, NULL ) )
+ {
+ gint ap_strength = nm_access_point_get_strength( ap );
+ strength = MAX( strength, ap_strength );
+ }
+
+ strength = CLAMP( strength, 0, 100 );
+ if ( self->strength != strength )
+ {
+ self->strength = strength;
+ g_object_notify_by_pspec( G_OBJECT( self ), network_props[NETWORK_PROP_STRENGTH] );
+ }
+}
+
+//
+// Called when the strength of an access point for this network has changed.
+//
+void foobar_network_handle_strength_change(
+ GObject* ap,
+ GParamSpec* pspec,
+ gpointer userdata )
+{
+ (void)ap;
+ (void)pspec;
+ FoobarNetwork* self = (FoobarNetwork*)userdata;
+
+ foobar_network_update_strength( self );
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+// Network Adapter
+// ---------------------------------------------------------------------------------------------------------------------
+
+//
+// Static initialization for network adapters.
+//
+void foobar_network_adapter_class_init( FoobarNetworkAdapterClass* klass )
+{
+ GObjectClass* object_klass = G_OBJECT_CLASS( klass );
+ object_klass->get_property = foobar_network_adapter_get_property;
+ object_klass->finalize = foobar_network_adapter_finalize;
+
+ adapter_props[ADAPTER_PROP_STATE] = g_param_spec_enum(
+ "state",
+ "State",
+ "The current connection state of the network.",
+ FOOBAR_TYPE_NETWORK_ADAPTER_STATE,
+ FOOBAR_NETWORK_ADAPTER_STATE_DISCONNECTED,
+ G_PARAM_READABLE );
+ g_object_class_install_properties( object_klass, N_ADAPTER_PROPS, adapter_props );
+}
+
+//
+// Instance initialization for network adapters.
+//
+void foobar_network_adapter_init( FoobarNetworkAdapter* self )
+{
+ (void)self;
+}
+
+//
+// Property getter implementation, mapping a property id to a method.
+//
+void foobar_network_adapter_get_property(
+ GObject* object,
+ guint prop_id,
+ GValue* value,
+ GParamSpec* pspec )
+{
+ FoobarNetworkAdapter* self = (FoobarNetworkAdapter*)object;
+
+ switch ( prop_id )
+ {
+ case ADAPTER_PROP_STATE:
+ g_value_set_enum( value, foobar_network_adapter_get_state( self ) );
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID( object, prop_id, pspec );
+ break;
+ }
+}
+
+//
+// Instance cleanup for network adapters.
+//
+void foobar_network_adapter_finalize( GObject* object )
+{
+ FoobarNetworkAdapter* self = (FoobarNetworkAdapter*)object;
+
+ foobar_network_adapter_set_device( self, NULL );
+
+ G_OBJECT_CLASS( foobar_network_adapter_parent_class )->finalize( object );
+}
+
+//
+// Get the current connection/connectivity state of the adapter.
+//
+FoobarNetworkAdapterState foobar_network_adapter_get_state( FoobarNetworkAdapter* self )
+{
+ g_return_val_if_fail( FOOBAR_IS_NETWORK_ADAPTER( self ), FOOBAR_NETWORK_ADAPTER_STATE_DISCONNECTED );
+
+ FoobarNetworkAdapterPrivate* priv = foobar_network_adapter_get_instance_private( self );
+ return priv->state;
+}
+
+//
+// Update the backing network device which is used to derive the adapter's state.
+//
+void foobar_network_adapter_set_device(
+ FoobarNetworkAdapter* self,
+ NMDevice* value )
+{
+ FoobarNetworkAdapterPrivate* priv = foobar_network_adapter_get_instance_private( self );
+ if ( priv->device != value )
+ {
+ g_clear_signal_handler( &priv->connection_state_handler_id, priv->connection );
+ g_clear_signal_handler( &priv->connectivity_handler_id, priv->device );
+ g_clear_signal_handler( &priv->connection_handler_id, priv->device );
+ g_clear_object( &priv->connection );
+ g_clear_object( &priv->device );
+
+ priv->device = value;
+ if ( priv->device )
+ {
+ g_object_ref( priv->device );
+ priv->connectivity_handler_id = g_signal_connect(
+ priv->device,
+ "notify::ip4-connectivity",
+ G_CALLBACK( foobar_network_adapter_handle_state_change ), self );
+ priv->connection_handler_id = g_signal_connect(
+ priv->device,
+ "notify::active-connection",
+ G_CALLBACK( foobar_network_adapter_handle_connection_change ), self );
+ priv->connection = nm_device_get_active_connection( priv->device );
+ if ( priv->connection )
+ {
+ g_object_ref( priv->connection );
+ priv->connection_state_handler_id = g_signal_connect(
+ priv->connection,
+ "notify::state",
+ G_CALLBACK( foobar_network_adapter_handle_state_change ),
+ self );
+ }
+ }
+
+ foobar_network_adapter_update_state( self );
+ }
+}
+
+//
+// Derive the connection/connectivity state for the network from the backing device.
+//
+void foobar_network_adapter_update_state( FoobarNetworkAdapter* self )
+{
+ FoobarNetworkAdapterPrivate* priv = foobar_network_adapter_get_instance_private( self );
+
+ FoobarNetworkAdapterState state = FOOBAR_NETWORK_ADAPTER_STATE_DISCONNECTED;
+ if ( priv->device )
+ {
+ NMActiveConnection* connection = nm_device_get_active_connection( priv->device );
+ if ( connection )
+ {
+ switch ( nm_active_connection_get_state( connection ) )
+ {
+ case NM_ACTIVE_CONNECTION_STATE_ACTIVATED:
+ state = ( nm_device_get_connectivity( priv->device, AF_INET ) == NM_CONNECTIVITY_FULL )
+ ? FOOBAR_NETWORK_ADAPTER_STATE_CONNECTED
+ : FOOBAR_NETWORK_ADAPTER_STATE_LIMITED;
+ break;
+ case NM_ACTIVE_CONNECTION_STATE_ACTIVATING:
+ state = FOOBAR_NETWORK_ADAPTER_STATE_CONNECTING;
+ break;
+ case NM_ACTIVE_CONNECTION_STATE_DEACTIVATING:
+ case NM_ACTIVE_CONNECTION_STATE_DEACTIVATED:
+ case NM_ACTIVE_CONNECTION_STATE_UNKNOWN:
+ default:
+ state = FOOBAR_NETWORK_ADAPTER_STATE_DISCONNECTED;
+ break;
+ }
+ }
+ }
+
+ if ( priv->state != state )
+ {
+ priv->state = state;
+ g_object_notify_by_pspec( G_OBJECT( self ), adapter_props[ADAPTER_PROP_STATE] );
+ }
+}
+
+//
+// Called when the "ip4-connectivity" property of the NetworkManager device or the "state" property of the active
+// connection has changed.
+//
+static void foobar_network_adapter_handle_state_change(
+ GObject* sender,
+ GParamSpec* pspec,
+ gpointer userdata )
+{
+ (void)sender;
+ (void)pspec;
+ FoobarNetworkAdapter* self = (FoobarNetworkAdapter*)userdata;
+
+ foobar_network_adapter_update_state( self );
+}
+
+//
+// Called when the "active-connection" property of the NetworkManager device has changed.
+//
+// This updates the state of the adapter and sets the connection up for state change listening.
+//
+static void foobar_network_adapter_handle_connection_change(
+ GObject* sender,
+ GParamSpec* pspec,
+ gpointer userdata )
+{
+ (void)sender;
+ (void)pspec;
+ FoobarNetworkAdapter* self = (FoobarNetworkAdapter*)userdata;
+ FoobarNetworkAdapterPrivate* priv = foobar_network_adapter_get_instance_private( self );
+
+ g_clear_signal_handler( &priv->connection_state_handler_id, priv->connection );
+ g_clear_object( &priv->connection );
+
+ priv->connection = nm_device_get_active_connection( priv->device );
+ if ( priv->connection )
+ {
+ g_object_ref( priv->connection );
+ priv->connection_state_handler_id = g_signal_connect(
+ priv->connection,
+ "notify::state",
+ G_CALLBACK( foobar_network_adapter_handle_state_change ),
+ self );
+ }
+
+ foobar_network_adapter_update_state( self );
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+// Wired Network Adapter
+// ---------------------------------------------------------------------------------------------------------------------
+
+//
+// Static initialization for wired network adapters.
+//
+void foobar_network_adapter_wired_class_init( FoobarNetworkAdapterWiredClass* klass )
+{
+ GObjectClass* object_klass = G_OBJECT_CLASS( klass );
+ object_klass->get_property = foobar_network_adapter_wired_get_property;
+ object_klass->finalize = foobar_network_adapter_wired_finalize;
+
+ wired_props[WIRED_PROP_SPEED] = g_param_spec_int64(
+ "speed",
+ "Speed",
+ "The supported speed of the adapter in Mbit/s.",
+ 0,
+ INT64_MAX,
+ 0,
+ G_PARAM_READABLE );
+ g_object_class_install_properties( object_klass, N_WIRED_PROPS, wired_props );
+}
+
+//
+// Instance initialization for wired network adapters.
+//
+void foobar_network_adapter_wired_init( FoobarNetworkAdapterWired* self )
+{
+ (void)self;
+}
+
+//
+// Property getter implementation, mapping a property id to a method.
+//
+void foobar_network_adapter_wired_get_property(
+ GObject* object,
+ guint prop_id,
+ GValue* value,
+ GParamSpec* pspec )
+{
+ FoobarNetworkAdapterWired* self = (FoobarNetworkAdapterWired*)object;
+
+ switch ( prop_id )
+ {
+ case WIRED_PROP_SPEED:
+ g_value_set_int64( value, foobar_network_adapter_wired_get_speed( self ) );
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID( object, prop_id, pspec );
+ break;
+ }
+}
+
+//
+// Instance cleanup for wired network adapters.
+//
+void foobar_network_adapter_wired_finalize( GObject* object )
+{
+ FoobarNetworkAdapterWired* self = (FoobarNetworkAdapterWired*)object;
+
+ g_clear_signal_handler( &self->speed_handler_id, self->device );
+ g_clear_object( &self->device );
+
+ G_OBJECT_CLASS( foobar_network_adapter_wired_parent_class )->finalize( object );
+}
+
+//
+// Create a new wired network adapter wrapping the provided NetworkManager device.
+//
+FoobarNetworkAdapterWired* foobar_network_adapter_wired_new( NMDeviceEthernet* device )
+{
+ FoobarNetworkAdapterWired* self = g_object_new( FOOBAR_TYPE_NETWORK_ADAPTER_WIRED, NULL );
+ foobar_network_adapter_set_device( FOOBAR_NETWORK_ADAPTER( self ), NM_DEVICE( device ) );
+
+ self->device = g_object_ref( device );
+ self->speed_handler_id = g_signal_connect(
+ self->device,
+ "notify::speed",
+ G_CALLBACK( foobar_network_adapter_wired_handle_speed_change ),
+ self );
+ return self;
+}
+
+//
+// Get the supported speed of the adapter in Mbit/s.
+//
+gint64 foobar_network_adapter_wired_get_speed( FoobarNetworkAdapterWired* self )
+{
+ g_return_val_if_fail( FOOBAR_IS_NETWORK_ADAPTER_WIRED( self ), 0 );
+ return nm_device_ethernet_get_speed( self->device );
+}
+
+//
+// Called when the "speed" property of the NetworkManager device changes.
+//
+void foobar_network_adapter_wired_handle_speed_change(
+ GObject* device,
+ GParamSpec* pspec,
+ gpointer userdata )
+{
+ (void)device;
+ (void)pspec;
+ FoobarNetworkAdapterWired* self = (FoobarNetworkAdapterWired*)userdata;
+
+ g_object_notify_by_pspec( G_OBJECT( self ), wired_props[WIRED_PROP_SPEED] );
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+// Wireless Network Adapter
+// ---------------------------------------------------------------------------------------------------------------------
+
+//
+// Static initialization for wireless network adapters.
+//
+void foobar_network_adapter_wifi_class_init( FoobarNetworkAdapterWifiClass* klass )
+{
+ GObjectClass* object_klass = G_OBJECT_CLASS( klass );
+ object_klass->get_property = foobar_network_adapter_wifi_get_property;
+ object_klass->set_property = foobar_network_adapter_wifi_set_property;
+ object_klass->finalize = foobar_network_adapter_wifi_finalize;
+
+ wifi_props[WIFI_PROP_NETWORKS] = g_param_spec_object(
+ "networks",
+ "Networks",
+ "Sorted list of available networks.",
+ G_TYPE_LIST_MODEL,
+ G_PARAM_READABLE );
+ wifi_props[WIFI_PROP_ACTIVE] = g_param_spec_object(
+ "active",
+ "Active",
+ "The currently active network (if any).",
+ FOOBAR_TYPE_NETWORK,
+ G_PARAM_READABLE );
+ wifi_props[WIFI_PROP_IS_ENABLED] = g_param_spec_boolean(
+ "is-enabled",
+ "Is Enabled",
+ "Indicates whether the wifi adapter is currently enabled.",
+ FALSE,
+ G_PARAM_READWRITE );
+ wifi_props[WIFI_PROP_IS_SCANNING] = g_param_spec_boolean(
+ "is-scanning",
+ "Is Scanning",
+ "Indicates whether the wifi adapter is currently scanning for available networks.",
+ FALSE,
+ G_PARAM_READWRITE );
+ g_object_class_install_properties( object_klass, N_WIFI_PROPS, wifi_props );
+}
+
+//
+// Instance initialization for wireless network adapters.
+//
+void foobar_network_adapter_wifi_init( FoobarNetworkAdapterWifi* self )
+{
+ self->ap_networks = g_hash_table_new_full(
+ g_direct_hash,
+ g_direct_equal,
+ g_object_unref,
+ (GDestroyNotify)ap_network_membership_destroy );
+ self->named_networks = g_hash_table_new_full(
+ g_bytes_hash,
+ g_bytes_equal,
+ (GDestroyNotify)g_bytes_unref,
+ g_object_unref );
+ self->networks = g_list_store_new( FOOBAR_TYPE_NETWORK );
+
+ GtkCustomFilter* filter = gtk_custom_filter_new( foobar_network_adapter_wifi_filter_func, NULL, NULL );
+ self->filtered_networks = gtk_filter_list_model_new(
+ G_LIST_MODEL( g_object_ref( self->networks ) ),
+ GTK_FILTER( filter ) );
+
+ GtkCustomSorter* sorter = gtk_custom_sorter_new( foobar_network_adapter_wifi_sort_func, NULL, NULL );
+ self->sorted_networks = gtk_sort_list_model_new(
+ G_LIST_MODEL( g_object_ref( self->filtered_networks ) ),
+ GTK_SORTER( sorter ) );
+}
+
+//
+// Property getter implementation, mapping a property id to a method.
+//
+void foobar_network_adapter_wifi_get_property(
+ GObject* object,
+ guint prop_id,
+ GValue* value,
+ GParamSpec* pspec )
+{
+ FoobarNetworkAdapterWifi* self = (FoobarNetworkAdapterWifi*)object;
+
+ switch ( prop_id )
+ {
+ case WIFI_PROP_NETWORKS:
+ g_value_set_object( value, foobar_network_adapter_wifi_get_networks( self ) );
+ break;
+ case WIFI_PROP_ACTIVE:
+ g_value_set_object( value, foobar_network_adapter_wifi_get_active( self ) );
+ break;
+ case WIFI_PROP_IS_ENABLED:
+ g_value_set_boolean( value, foobar_network_adapter_wifi_is_enabled( self ) );
+ break;
+ case WIFI_PROP_IS_SCANNING:
+ g_value_set_boolean( value, foobar_network_adapter_wifi_is_scanning( self ) );
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID( object, prop_id, pspec );
+ break;
+ }
+}
+
+//
+// Property setter implementation, mapping a property id to a method.
+//
+void foobar_network_adapter_wifi_set_property(
+ GObject* object,
+ guint prop_id,
+ GValue const* value,
+ GParamSpec* pspec )
+{
+ FoobarNetworkAdapterWifi* self = (FoobarNetworkAdapterWifi*)object;
+
+ switch ( prop_id )
+ {
+ case WIFI_PROP_IS_ENABLED:
+ foobar_network_adapter_wifi_set_enabled( self, g_value_get_boolean( value ) );
+ break;
+ case WIFI_PROP_IS_SCANNING:
+ foobar_network_adapter_wifi_set_scanning( self, g_value_get_boolean( value ) );
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID( object, prop_id, pspec );
+ break;
+ }
+}
+
+//
+// Instance cleanup for wireless network adapters.
+//
+void foobar_network_adapter_wifi_finalize( GObject* object )
+{
+ FoobarNetworkAdapterWifi* self = (FoobarNetworkAdapterWifi*)object;
+
+ GPtrArray const* aps = nm_device_wifi_get_access_points( self->device );
+ for ( guint i = 0; i < aps->len; ++i )
+ {
+ NMAccessPoint* ap = g_ptr_array_index( aps, i );
+ g_signal_handlers_disconnect_by_data( ap, self );
+ }
+
+ g_clear_handle_id( &self->refresh_source_id, g_source_remove );
+ g_clear_signal_handler( &self->enabled_handler_id, self->client );
+ g_clear_signal_handler( &self->active_handler_id, self->device );
+ g_clear_signal_handler( &self->added_handler_id, self->device );
+ g_clear_signal_handler( &self->removed_handler_id, self->device );
+ g_clear_object( &self->client );
+ g_clear_object( &self->device );
+ g_clear_object( &self->sorted_networks );
+ g_clear_object( &self->filtered_networks );
+ g_clear_object( &self->networks );
+ g_clear_object( &self->active );
+ g_clear_pointer( &self->named_networks, g_hash_table_unref );
+ g_clear_pointer( &self->ap_networks, g_hash_table_unref );
+
+ G_OBJECT_CLASS( foobar_network_adapter_wifi_parent_class )->finalize( object );
+}
+
+//
+// Create a new wireless network adapter wrapping the provided NetworkManager device.
+//
+FoobarNetworkAdapterWifi* foobar_network_adapter_wifi_new(
+ NMClient* client,
+ NMDeviceWifi* device )
+{
+ FoobarNetworkAdapterWifi* self = g_object_new( FOOBAR_TYPE_NETWORK_ADAPTER_WIFI, NULL );
+ foobar_network_adapter_set_device( FOOBAR_NETWORK_ADAPTER( self ), NM_DEVICE( device ) );
+
+ self->client = g_object_ref( client );
+ self->device = g_object_ref( device );
+ self->enabled_handler_id = g_signal_connect(
+ self->client,
+ "notify::wireless-enabled",
+ G_CALLBACK( foobar_network_adapter_wifi_handle_enabled_change ),
+ self );
+ self->active_handler_id = g_signal_connect(
+ self->device,
+ "notify::active-access-point",
+ G_CALLBACK( foobar_network_adapter_wifi_handle_active_change ),
+ self );
+ self->added_handler_id = g_signal_connect(
+ self->device,
+ "access-point-added",
+ G_CALLBACK( foobar_network_adapter_wifi_handle_added ),
+ self );
+ self->removed_handler_id = g_signal_connect(
+ self->device,
+ "access-point-removed",
+ G_CALLBACK( foobar_network_adapter_wifi_handle_removed ),
+ self );
+
+ GPtrArray const* aps = nm_device_wifi_get_access_points( self->device );
+ for ( guint i = 0; i < aps->len; ++i )
+ {
+ NMAccessPoint* ap = g_ptr_array_index( aps, i );
+ g_signal_connect(
+ ap,
+ "notify::ssid",
+ G_CALLBACK( foobar_network_adapter_wifi_handle_ssid_change ),
+ self );
+ foobar_network_adapter_wifi_add_ap_to_network( self, ap );
+ }
+
+ foobar_network_adapter_wifi_update_active( self );
+ return self;
+}
+
+//
+// Get a sorted list of available networks.
+//
+GListModel* foobar_network_adapter_wifi_get_networks( FoobarNetworkAdapterWifi* self )
+{
+ g_return_val_if_fail( FOOBAR_IS_NETWORK_ADAPTER_WIFI( self ), NULL );
+ return G_LIST_MODEL( self->sorted_networks );
+}
+
+//
+// Get the currently active network (if any).
+//
+FoobarNetwork* foobar_network_adapter_wifi_get_active( FoobarNetworkAdapterWifi* self )
+{
+ g_return_val_if_fail( FOOBAR_IS_NETWORK_ADAPTER_WIFI( self ), NULL );
+ return self->active;
+}
+
+//
+// Check whether the wifi adapter is currently enabled.
+//
+gboolean foobar_network_adapter_wifi_is_enabled( FoobarNetworkAdapterWifi* self )
+{
+ g_return_val_if_fail( FOOBAR_IS_NETWORK_ADAPTER_WIFI( self ), FALSE );
+ return nm_client_wireless_get_enabled( self->client );
+}
+
+//
+// Check whether the wifi adapter is currently scanning for available networks.
+//
+gboolean foobar_network_adapter_wifi_is_scanning( FoobarNetworkAdapterWifi* self )
+{
+ g_return_val_if_fail( FOOBAR_IS_NETWORK_ADAPTER_WIFI( self ), FALSE );
+ return self->is_scanning;
+}
+
+//
+// Update whether the wifi adapter is currently enabled.
+//
+void foobar_network_adapter_wifi_set_enabled(
+ FoobarNetworkAdapterWifi* self,
+ gboolean value )
+{
+ g_return_if_fail( FOOBAR_IS_NETWORK_ADAPTER_WIFI( self ) );
+
+ if ( !value ) { foobar_network_adapter_wifi_set_scanning( self, FALSE ); }
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+ nm_client_wireless_set_enabled( self->client, value );
+#pragma GCC diagnostic pop
+}
+
+//
+// Update whether the wifi adapter is currently scanning for available networks.
+//
+void foobar_network_adapter_wifi_set_scanning(
+ FoobarNetworkAdapterWifi* self,
+ gboolean value )
+{
+ g_return_if_fail( FOOBAR_IS_NETWORK_ADAPTER_WIFI( self ) );
+
+ value = value && foobar_network_adapter_wifi_is_enabled( self );
+ if ( self->is_scanning != value )
+ {
+ self->is_scanning = value;
+ g_object_notify_by_pspec( G_OBJECT( self ), wifi_props[WIFI_PROP_IS_SCANNING] );
+
+ // Periodically start scans until "is-scanning" is disabled again.
+
+ g_clear_handle_id( &self->refresh_source_id, g_source_remove );
+ if ( self->is_scanning )
+ {
+ foobar_network_adapter_wifi_refresh_list( self );
+ self->refresh_source_id = g_timeout_add(
+ SCAN_REFRESH_INTERVAL,
+ foobar_network_adapter_wifi_refresh_list,
+ self );
+ }
+ }
+}
+
+//
+// Ensure that a network for the specified access point exists.
+//
+// If the AP has an SSID, it is added to the network for this SSID. Otherwise, a new network for this AP is created just
+// for this network.
+//
+void foobar_network_adapter_wifi_add_ap_to_network(
+ FoobarNetworkAdapterWifi* self,
+ NMAccessPoint* ap )
+{
+ // The AP is automatically added/removed from the network when using g_hash_table_replace because of the
+ // ApNetworkMembership destroy function.
+
+ GBytes* ssid = nm_access_point_get_ssid( ap );
+ if ( ssid )
+ {
+ FoobarNetwork* network = g_hash_table_lookup( self->named_networks, ssid );
+ if ( !network )
+ {
+ network = foobar_network_new( );
+ foobar_network_set_ssid( network, ssid );
+ g_list_store_append( self->networks, network );
+ g_hash_table_insert( self->named_networks, g_bytes_ref( ssid ), network );
+ }
+
+ g_hash_table_replace( self->ap_networks, g_object_ref( ap ), ap_network_membership_new( ap, network ) );
+ }
+ else
+ {
+ g_autoptr( FoobarNetwork ) network = foobar_network_new( );
+ g_hash_table_replace( self->ap_networks, g_object_ref( ap ), ap_network_membership_new( ap, network ) );
+ }
+
+ gtk_filter_changed( gtk_filter_list_model_get_filter( self->filtered_networks ), GTK_FILTER_CHANGE_DIFFERENT );
+}
+
+//
+// Update the active network based on the active access point.
+//
+void foobar_network_adapter_wifi_update_active( FoobarNetworkAdapterWifi* self )
+{
+ NMAccessPoint* active_ap = nm_device_wifi_get_active_access_point( self->device );
+ ApNetworkMembership* membership = active_ap ? g_hash_table_lookup( self->ap_networks, active_ap ) : NULL;
+ FoobarNetwork* new_active = membership ? membership->network : NULL;
+
+ if ( self->active != new_active )
+ {
+ if ( self->active )
+ {
+ foobar_network_set_active( self->active, FALSE );
+ }
+ g_clear_object( &self->active );
+
+ self->active = new_active;
+ if ( self->active )
+ {
+ g_object_ref( self->active );
+ foobar_network_set_active( self->active, TRUE );
+ }
+
+ g_object_notify_by_pspec( G_OBJECT( self ), wifi_props[WIFI_PROP_ACTIVE] );
+ }
+}
+
+//
+// Called when the SSID for an access point changes.
+//
+// This will remove the access point from the old network and associate it with its new network.
+//
+void foobar_network_adapter_wifi_handle_ssid_change(
+ GObject* ap,
+ GParamSpec* pspec,
+ gpointer userdata )
+{
+ (void)pspec;
+ FoobarNetworkAdapterWifi* self = (FoobarNetworkAdapterWifi*)userdata;
+
+ foobar_network_adapter_wifi_add_ap_to_network( self, NM_ACCESS_POINT( ap ) );
+ foobar_network_adapter_wifi_update_active( self );
+}
+
+//
+// Called when the "wireless-enabled" state of the client changes.
+//
+void foobar_network_adapter_wifi_handle_enabled_change(
+ GObject* client,
+ GParamSpec* pspec,
+ gpointer userdata )
+{
+ (void)client;
+ (void)pspec;
+ FoobarNetworkAdapterWifi* self = (FoobarNetworkAdapterWifi*)userdata;
+
+ g_object_notify_by_pspec( G_OBJECT( self ), wifi_props[WIFI_PROP_IS_ENABLED] );
+}
+
+//
+// Called when the active access point of the backing device changes.
+//
+void foobar_network_adapter_wifi_handle_active_change(
+ GObject* device,
+ GParamSpec* pspec,
+ gpointer userdata )
+{
+ (void)device;
+ (void)pspec;
+ FoobarNetworkAdapterWifi* self = (FoobarNetworkAdapterWifi*)userdata;
+
+ foobar_network_adapter_wifi_update_active( self );
+}
+
+//
+// Called when a new access point is added.
+//
+// This will associate the access point with its network.
+//
+void foobar_network_adapter_wifi_handle_added(
+ NMDeviceWifi* device,
+ NMAccessPoint* ap,
+ gpointer userdata )
+{
+ (void)device;
+ FoobarNetworkAdapterWifi* self = (FoobarNetworkAdapterWifi*)userdata;
+
+ g_signal_connect(
+ ap,
+ "notify::ssid",
+ G_CALLBACK( foobar_network_adapter_wifi_handle_ssid_change ),
+ self );
+ foobar_network_adapter_wifi_add_ap_to_network( self, ap );
+}
+
+//
+// Called when an access point is removed.
+//
+// This will remove it from its network.
+//
+void foobar_network_adapter_wifi_handle_removed(
+ NMDeviceWifi* device,
+ NMAccessPoint* ap,
+ gpointer userdata )
+{
+ (void)device;
+ FoobarNetworkAdapterWifi* self = (FoobarNetworkAdapterWifi*)userdata;
+
+ g_signal_handlers_disconnect_by_data( ap, self );
+ g_hash_table_remove( self->ap_networks, ap ); // Will remove the AP from the network.
+ gtk_filter_changed( gtk_filter_list_model_get_filter( self->filtered_networks ), GTK_FILTER_CHANGE_MORE_STRICT );
+}
+
+//
+// Periodic callback to start a network scan.
+//
+gboolean foobar_network_adapter_wifi_refresh_list( gpointer userdata )
+{
+ FoobarNetworkAdapterWifi* self = (FoobarNetworkAdapterWifi*)userdata;
+
+ g_autoptr( GError ) error = NULL;
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+ if ( !nm_device_wifi_request_scan( self->device, NULL, &error ) )
+#pragma GCC diagnostic pop
+ {
+ g_warning( "Unable to start Wi-Fi network scan: %s", error->message );
+ }
+
+ return G_SOURCE_CONTINUE;
+}
+
+//
+// Filtering callback for the list of networks. Only networks with at least one AP are shown.
+//
+gboolean foobar_network_adapter_wifi_filter_func(
+ gpointer item,
+ gpointer userdata )
+{
+ (void)userdata;
+
+ FoobarNetwork* network = (FoobarNetwork*)item;
+ return g_hash_table_size( network->aps ) > 0;
+}
+
+//
+// Sorting callback for the list of networks.
+//
+gint foobar_network_adapter_wifi_sort_func(
+ gconstpointer item_a,
+ gconstpointer item_b,
+ gpointer userdata )
+{
+ (void)userdata;
+
+ FoobarNetwork* network_a = (FoobarNetwork*)item_a;
+ FoobarNetwork* network_b = (FoobarNetwork*)item_b;
+ return g_strcmp0( foobar_network_get_name( network_a ), foobar_network_get_name( network_b ) );
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+// Access Point Network Memberships
+// ---------------------------------------------------------------------------------------------------------------------
+
+//
+// Create an association between an access point and the network it belongs to.
+//
+ApNetworkMembership* ap_network_membership_new(
+ NMAccessPoint* ap,
+ FoobarNetwork* network )
+{
+ ApNetworkMembership* self = g_new0( ApNetworkMembership, 1 );
+ self->ap = ap;
+ self->network = g_object_ref( network );
+ foobar_network_add_ap( self->network, self->ap );
+ return self;
+}
+
+//
+// Destroy an association between an access point and its previous network.
+//
+void ap_network_membership_destroy( ApNetworkMembership* self )
+{
+ if ( self )
+ {
+ foobar_network_remove_ap( self->network, self->ap );
+ g_object_unref( self->network );
+ g_free( self );
+ }
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+// Service Implementation
+// ---------------------------------------------------------------------------------------------------------------------
+
+//
+// Static initialization for the network service.
+//
+void foobar_network_service_class_init( FoobarNetworkServiceClass* klass )
+{
+ GObjectClass* object_klass = G_OBJECT_CLASS( klass );
+ object_klass->get_property = foobar_network_service_get_property;
+ object_klass->finalize = foobar_network_service_finalize;
+
+ props[PROP_WIRED] = g_param_spec_object(
+ "wired",
+ "Wired",
+ "The wired network adapter (if available).",
+ FOOBAR_TYPE_NETWORK_ADAPTER_WIRED,
+ G_PARAM_READABLE );
+ props[PROP_WIFI] = g_param_spec_object(
+ "wifi",
+ "WiFi",
+ "The wireless network adapter (if available).",
+ FOOBAR_TYPE_NETWORK_ADAPTER_WIFI,
+ G_PARAM_READABLE );
+ props[PROP_ACTIVE] = g_param_spec_object(
+ "active",
+ "Active",
+ "The network adapter currently in use.",
+ FOOBAR_TYPE_NETWORK_ADAPTER,
+ G_PARAM_READABLE );
+ g_object_class_install_properties( object_klass, N_PROPS, props );
+}
+
+//
+// Instance initialization for the network service.
+//
+void foobar_network_service_init( FoobarNetworkService* self )
+{
+ g_autoptr( GError ) error = NULL;
+ self->client = nm_client_new( NULL, &error );
+ if ( !self->client )
+ {
+ g_warning( "Unable to connect to NetworkManager: %s", error->message );
+ return;
+ }
+
+ NMDeviceEthernet* device_wired = NULL;
+ NMDeviceWifi* device_wifi = NULL;
+ GPtrArray const* devices = nm_client_get_devices( self->client );
+ for ( guint i = 0; i < devices->len; ++i )
+ {
+ // XXX: Add support for multiple devices
+ NMDevice* device = g_ptr_array_index( devices, i );
+ if ( !device_wired && NM_IS_DEVICE_ETHERNET( device ) )
+ {
+ device_wired = NM_DEVICE_ETHERNET( device );
+ }
+ if ( !device_wifi && NM_IS_DEVICE_WIFI( device ) )
+ {
+ device_wifi = NM_DEVICE_WIFI( device );
+ }
+ if ( device_wired && device_wifi )
+ {
+ break;
+ }
+ }
+
+ self->wired = device_wired ? foobar_network_adapter_wired_new( device_wired ) : NULL;
+ self->wifi = device_wifi ? foobar_network_adapter_wifi_new( self->client, device_wifi ) : NULL;
+
+ foobar_network_service_update_active( self );
+ self->primary_handler_id = g_signal_connect(
+ self->client,
+ "notify::primary-connection",
+ G_CALLBACK( foobar_network_service_handle_connection_change ),
+ self );
+ self->activating_handler_id = g_signal_connect(
+ self->client,
+ "notify::activating-connection",
+ G_CALLBACK( foobar_network_service_handle_connection_change ),
+ self );
+}
+
+//
+// Property getter implementation, mapping a property id to a method.
+//
+void foobar_network_service_get_property(
+ GObject* object,
+ guint prop_id,
+ GValue* value,
+ GParamSpec* pspec )
+{
+ FoobarNetworkService* self = (FoobarNetworkService*)object;
+
+ switch ( prop_id )
+ {
+ case PROP_WIRED:
+ g_value_set_object( value, foobar_network_service_get_wired( self ) );
+ break;
+ case PROP_WIFI:
+ g_value_set_object( value, foobar_network_service_get_wifi( self ) );
+ break;
+ case PROP_ACTIVE:
+ g_value_set_object( value, foobar_network_service_get_active( self ) );
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID( object, prop_id, pspec );
+ break;
+ }
+}
+
+//
+// Instance cleanup for the network service.
+//
+void foobar_network_service_finalize( GObject* object )
+{
+ FoobarNetworkService* self = (FoobarNetworkService*)object;
+
+ g_clear_signal_handler( &self->primary_handler_id, self->client );
+ g_clear_signal_handler( &self->activating_handler_id, self->client );
+ g_clear_object( &self->wifi );
+ g_clear_object( &self->wired );
+ g_clear_object( &self->client );
+
+ G_OBJECT_CLASS( foobar_network_service_parent_class )->finalize( object );
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+// Public API
+// ---------------------------------------------------------------------------------------------------------------------
+
+//
+// Create a new network service instance.
+//
+FoobarNetworkService* foobar_network_service_new( void )
+{
+ return g_object_new( FOOBAR_TYPE_NETWORK_SERVICE, NULL );
+}
+
+//
+// Get the wired network adapter (if available).
+//
+FoobarNetworkAdapterWired* foobar_network_service_get_wired( FoobarNetworkService* self )
+{
+ g_return_val_if_fail( FOOBAR_IS_NETWORK_SERVICE( self ), NULL );
+ return self->wired;
+}
+
+//
+// Get the wireless network adapter (if available).
+//
+FoobarNetworkAdapterWifi* foobar_network_service_get_wifi( FoobarNetworkService* self )
+{
+ g_return_val_if_fail( FOOBAR_IS_NETWORK_SERVICE( self ), NULL );
+ return self->wifi;
+}
+
+//
+// Get the network adapter currently in use.
+//
+FoobarNetworkAdapter* foobar_network_service_get_active( FoobarNetworkService* self )
+{
+ g_return_val_if_fail( FOOBAR_IS_NETWORK_SERVICE( self ), NULL );
+ return self->active;
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+// Signal Handlers
+// ---------------------------------------------------------------------------------------------------------------------
+
+//
+// Called when either the primary connection or the activating connection of the client has changed.
+//
+void foobar_network_service_handle_connection_change(
+ GObject* client,
+ GParamSpec* pspec,
+ gpointer userdata )
+{
+ (void)client;
+ (void)pspec;
+ FoobarNetworkService* self = (FoobarNetworkService*)userdata;
+
+ foobar_network_service_update_active( self );
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+// Helper Methods
+// ---------------------------------------------------------------------------------------------------------------------
+
+//
+// Update the active network adapter.
+//
+// This uses the primary connection of the client to determine whether the wireless or wired adapter is used. Otherwise,
+// the activating connection is used instead.
+//
+void foobar_network_service_update_active( FoobarNetworkService* self )
+{
+ NMActiveConnection* connection = nm_client_get_primary_connection( self->client );
+ connection = connection ? connection : nm_client_get_activating_connection( self->client );
+ gchar const* type = connection ? nm_active_connection_get_connection_type( connection ) : NULL;
+
+ FoobarNetworkAdapter* new_active;
+ if ( !g_strcmp0( type, "802-11-wireless" ) )
+ {
+ new_active = FOOBAR_NETWORK_ADAPTER( self->wifi );
+ }
+ else if ( !g_strcmp0( type, "802-3-ethernet" ) )
+ {
+ new_active = FOOBAR_NETWORK_ADAPTER( self->wired );
+ }
+ else
+ {
+ new_active = self->wifi ? FOOBAR_NETWORK_ADAPTER( self->wifi ) : FOOBAR_NETWORK_ADAPTER( self->wired );
+ }
+
+ if ( self->active != new_active )
+ {
+ self->active = new_active;
+ g_object_notify_by_pspec( G_OBJECT( self ), props[PROP_ACTIVE] );
+ }
+}