diff options
Diffstat (limited to 'src/services/network-service.c')
| -rw-r--r-- | src/services/network-service.c | 1577 |
1 files changed, 1577 insertions, 0 deletions
diff --git a/src/services/network-service.c b/src/services/network-service.c new file mode 100644 index 0000000..bbbe857 --- /dev/null +++ b/src/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] ); + } +}
\ No newline at end of file |
