666 lines
19 KiB
C
666 lines
19 KiB
C
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
|
*
|
|
* Copyright (C) 2017 Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include "cc-network-resources.h"
|
|
#include "cc-wifi-panel.h"
|
|
#include "net-device.h"
|
|
#include "net-device-wifi.h"
|
|
#include "network-dialogs.h"
|
|
|
|
#include "shell/cc-object-storage.h"
|
|
#include "list-box-helper.h"
|
|
|
|
#include <glib/gi18n.h>
|
|
#include <NetworkManager.h>
|
|
|
|
typedef enum
|
|
{
|
|
OPERATION_NULL,
|
|
OPERATION_SHOW_DEVICE,
|
|
OPERATION_CREATE_WIFI,
|
|
OPERATION_CONNECT_HIDDEN,
|
|
OPERATION_CONNECT_8021X
|
|
} CmdlineOperation;
|
|
|
|
struct _CcWifiPanel
|
|
{
|
|
CcPanel parent;
|
|
|
|
/* RFKill (Airplane Mode) */
|
|
GDBusProxy *rfkill_proxy;
|
|
GtkSwitch *rfkill_switch;
|
|
GtkWidget *rfkill_widget;
|
|
|
|
/* Main widgets */
|
|
GtkStack *center_stack;
|
|
GtkStack *header_stack;
|
|
GtkStack *main_stack;
|
|
GtkSizeGroup *sizegroup;
|
|
GtkStack *stack;
|
|
|
|
NMClient *client;
|
|
|
|
GPtrArray *devices;
|
|
|
|
GCancellable *cancellable;
|
|
|
|
/* Command-line arguments */
|
|
CmdlineOperation arg_operation;
|
|
gchar *arg_device;
|
|
gchar *arg_access_point;
|
|
};
|
|
|
|
static void rfkill_switch_notify_activate_cb (GtkSwitch *rfkill_switch,
|
|
GParamSpec *pspec,
|
|
CcWifiPanel *self);
|
|
|
|
static void update_devices_names (CcWifiPanel *self);
|
|
|
|
G_DEFINE_TYPE (CcWifiPanel, cc_wifi_panel, CC_TYPE_PANEL)
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
PROP_PARAMETERS,
|
|
N_PROPS
|
|
};
|
|
|
|
/* Auxiliary methods */
|
|
|
|
static void
|
|
add_wifi_device (CcWifiPanel *self,
|
|
NMDevice *device)
|
|
{
|
|
GtkWidget *header_widget;
|
|
NetObject *net_device;
|
|
|
|
/* Only manage Wi-Fi devices */
|
|
if (!NM_IS_DEVICE_WIFI (device))
|
|
return;
|
|
|
|
/* Create the NetDevice */
|
|
net_device = g_object_new (NET_TYPE_DEVICE_WIFI,
|
|
"panel", self,
|
|
"removable", FALSE,
|
|
"cancellable", self->cancellable,
|
|
"client", self->client,
|
|
"nm-device", device,
|
|
"id", nm_device_get_udi (device),
|
|
NULL);
|
|
|
|
net_object_add_to_stack (net_device, self->stack, self->sizegroup);
|
|
|
|
/* And add to the header widgets */
|
|
header_widget = net_device_wifi_get_header_widget (NET_DEVICE_WIFI (net_device));
|
|
|
|
gtk_stack_add_named (self->header_stack, header_widget, net_object_get_id (net_device));
|
|
|
|
/* Setup custom title properties */
|
|
g_ptr_array_add (self->devices, net_device);
|
|
|
|
update_devices_names (self);
|
|
}
|
|
|
|
static void
|
|
check_main_stack_page (CcWifiPanel *self)
|
|
{
|
|
const gchar *nm_version;
|
|
|
|
nm_version = nm_client_get_version (self->client);
|
|
|
|
if (!nm_version)
|
|
gtk_stack_set_visible_child_name (self->main_stack, "nm-not-running");
|
|
else if (self->devices->len == 0 ||
|
|
!nm_client_wireless_get_enabled (self->client))
|
|
gtk_stack_set_visible_child_name (self->main_stack, "no-wifi-devices");
|
|
else
|
|
gtk_stack_set_visible_child_name (self->main_stack, "wifi-connections");
|
|
|
|
}
|
|
|
|
static void
|
|
load_wifi_devices (CcWifiPanel *self)
|
|
{
|
|
const GPtrArray *devices;
|
|
guint i;
|
|
|
|
devices = nm_client_get_devices (self->client);
|
|
|
|
/* Cold-plug existing devices */
|
|
if (devices)
|
|
{
|
|
for (i = 0; i < devices->len; i++)
|
|
add_wifi_device (self, g_ptr_array_index (devices, i));
|
|
}
|
|
|
|
check_main_stack_page (self);
|
|
}
|
|
|
|
static inline gboolean
|
|
get_cached_rfkill_property (CcWifiPanel *self,
|
|
const gchar *property)
|
|
{
|
|
g_autoptr (GVariant) result;
|
|
|
|
result = g_dbus_proxy_get_cached_property (self->rfkill_proxy, property);
|
|
return result ? g_variant_get_boolean (result) : FALSE;
|
|
}
|
|
|
|
static void
|
|
sync_airplane_mode_switch (CcWifiPanel *self)
|
|
{
|
|
gboolean enabled, should_show, hw_enabled;
|
|
|
|
enabled = get_cached_rfkill_property (self, "HasAirplaneMode");
|
|
should_show = get_cached_rfkill_property (self, "ShouldShowAirplaneMode");
|
|
|
|
gtk_widget_set_visible (GTK_WIDGET (self->rfkill_widget), enabled && should_show);
|
|
if (!enabled || !should_show)
|
|
return;
|
|
|
|
enabled = get_cached_rfkill_property (self, "AirplaneMode");
|
|
hw_enabled = get_cached_rfkill_property (self, "HardwareAirplaneMode");
|
|
|
|
enabled |= hw_enabled;
|
|
|
|
if (enabled != gtk_switch_get_active (self->rfkill_switch))
|
|
{
|
|
g_signal_handlers_block_by_func (self->rfkill_switch,
|
|
rfkill_switch_notify_activate_cb,
|
|
self);
|
|
gtk_switch_set_active (self->rfkill_switch, enabled);
|
|
g_signal_handlers_unblock_by_func (self->rfkill_switch,
|
|
rfkill_switch_notify_activate_cb,
|
|
self);
|
|
}
|
|
|
|
gtk_widget_set_sensitive (GTK_WIDGET (self->rfkill_switch), !hw_enabled);
|
|
|
|
check_main_stack_page (self);
|
|
}
|
|
|
|
static void
|
|
update_devices_names (CcWifiPanel *self)
|
|
{
|
|
guint number_of_devices = self->devices->len;
|
|
|
|
if (number_of_devices == 1)
|
|
{
|
|
GtkWidget *title_widget;
|
|
NetObject *net_device;
|
|
|
|
net_device = g_ptr_array_index (self->devices, 0);
|
|
title_widget = net_device_wifi_get_title_widget (NET_DEVICE_WIFI (net_device));
|
|
|
|
gtk_stack_add_named (self->center_stack, title_widget, "single");
|
|
gtk_stack_set_visible_child_name (self->center_stack, "single");
|
|
|
|
net_object_set_title (net_device, _("Wi-Fi"));
|
|
}
|
|
else
|
|
{
|
|
GtkWidget *single_page_widget;
|
|
guint i;
|
|
|
|
for (i = 0; i < number_of_devices; i++)
|
|
{
|
|
NetObject *object;
|
|
NMDevice *device;
|
|
|
|
object = g_ptr_array_index (self->devices, i);
|
|
device = net_device_get_nm_device (NET_DEVICE (object));
|
|
|
|
net_object_set_title (object, nm_device_get_product (device));
|
|
}
|
|
|
|
/* Remove the widget at the "single" page */
|
|
single_page_widget = gtk_stack_get_child_by_name (self->center_stack, "single");
|
|
|
|
if (single_page_widget)
|
|
{
|
|
g_object_ref (single_page_widget);
|
|
gtk_container_remove (GTK_CONTAINER (self->center_stack), single_page_widget);
|
|
g_object_unref (single_page_widget);
|
|
}
|
|
|
|
/* Show the stack-switcher page */
|
|
gtk_stack_set_visible_child_name (self->center_stack, "many");
|
|
}
|
|
}
|
|
|
|
/* Command-line arguments */
|
|
|
|
static void
|
|
reset_command_line_args (CcWifiPanel *self)
|
|
{
|
|
self->arg_operation = OPERATION_NULL;
|
|
g_clear_pointer (&self->arg_device, g_free);
|
|
g_clear_pointer (&self->arg_access_point, g_free);
|
|
}
|
|
|
|
static gboolean
|
|
handle_argv_for_device (CcWifiPanel *self,
|
|
NetObject *net_object)
|
|
{
|
|
GtkWidget *toplevel;
|
|
NMDevice *device;
|
|
gboolean ret;
|
|
|
|
toplevel = cc_shell_get_toplevel (cc_panel_get_shell (CC_PANEL (self)));
|
|
device = net_device_get_nm_device (NET_DEVICE (net_object));
|
|
ret = FALSE;
|
|
|
|
if (self->arg_operation == OPERATION_CREATE_WIFI)
|
|
{
|
|
cc_network_panel_create_wifi_network (toplevel, self->client);
|
|
ret = TRUE;
|
|
}
|
|
else if (self->arg_operation == OPERATION_CONNECT_HIDDEN)
|
|
{
|
|
cc_network_panel_connect_to_hidden_network (toplevel, self->client);
|
|
ret = TRUE;
|
|
}
|
|
else if (g_str_equal (nm_object_get_path (NM_OBJECT (device)), self->arg_device))
|
|
{
|
|
if (self->arg_operation == OPERATION_CONNECT_8021X)
|
|
{
|
|
cc_network_panel_connect_to_8021x_network (toplevel,
|
|
self->client,
|
|
device,
|
|
self->arg_access_point);
|
|
ret = TRUE;
|
|
}
|
|
else if (self->arg_operation == OPERATION_SHOW_DEVICE)
|
|
{
|
|
gtk_stack_set_visible_child_name (self->stack, net_object_get_id (net_object));
|
|
ret = TRUE;
|
|
}
|
|
}
|
|
|
|
if (ret)
|
|
reset_command_line_args (self);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void
|
|
handle_argv (CcWifiPanel *self)
|
|
{
|
|
guint i;
|
|
|
|
if (self->arg_operation == OPERATION_NULL)
|
|
return;
|
|
|
|
for (i = 0; i < self->devices->len; i++)
|
|
{
|
|
if (handle_argv_for_device (self, g_ptr_array_index (self->devices, i)))
|
|
break;
|
|
}
|
|
}
|
|
|
|
static GPtrArray *
|
|
variant_av_to_string_array (GVariant *array)
|
|
{
|
|
GVariantIter iter;
|
|
GVariant *v;
|
|
GPtrArray *strv;
|
|
gsize count;
|
|
|
|
count = g_variant_iter_init (&iter, array);
|
|
strv = g_ptr_array_sized_new (count + 1);
|
|
|
|
while (g_variant_iter_next (&iter, "v", &v))
|
|
{
|
|
g_ptr_array_add (strv, (gpointer) g_variant_get_string (v, NULL));
|
|
g_variant_unref (v);
|
|
}
|
|
|
|
g_ptr_array_add (strv, NULL); /* NULL-terminate the strv data array */
|
|
return strv;
|
|
}
|
|
|
|
static gboolean
|
|
verify_argv (CcWifiPanel *self,
|
|
const char **args)
|
|
{
|
|
switch (self->arg_operation)
|
|
{
|
|
case OPERATION_CONNECT_8021X:
|
|
case OPERATION_SHOW_DEVICE:
|
|
if (!self->arg_device)
|
|
{
|
|
g_warning ("Operation %s requires an object path", args[0]);
|
|
return FALSE;
|
|
}
|
|
default:
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
/* Callbacks */
|
|
|
|
static void
|
|
device_added_cb (NMClient *client,
|
|
NMDevice *device,
|
|
CcWifiPanel *self)
|
|
{
|
|
add_wifi_device (self, device);
|
|
check_main_stack_page (self);
|
|
}
|
|
|
|
static void
|
|
device_removed_cb (NMClient *client,
|
|
NMDevice *device,
|
|
CcWifiPanel *self)
|
|
{
|
|
GtkWidget *child;
|
|
const gchar *id;
|
|
guint i;
|
|
|
|
if (!NM_IS_DEVICE_WIFI (device))
|
|
return;
|
|
|
|
id = nm_device_get_udi (device);
|
|
|
|
/* Destroy all stack pages related to this device */
|
|
child = gtk_stack_get_child_by_name (self->stack, id);
|
|
gtk_widget_destroy (child);
|
|
|
|
child = gtk_stack_get_child_by_name (self->header_stack, id);
|
|
gtk_widget_destroy (child);
|
|
|
|
/* Remove from the devices list */
|
|
for (i = 0; i < self->devices->len; i++)
|
|
{
|
|
NetObject *object = g_ptr_array_index (self->devices, i);
|
|
|
|
if (g_strcmp0 (net_object_get_id (object), id) == 0)
|
|
{
|
|
g_ptr_array_remove (self->devices, object);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Update the title widget */
|
|
update_devices_names (self);
|
|
|
|
/* And check which page should be visible */
|
|
check_main_stack_page (self);
|
|
}
|
|
|
|
static void
|
|
wireless_enabled_cb (NMClient *client,
|
|
NMDevice *device,
|
|
CcWifiPanel *self)
|
|
{
|
|
check_main_stack_page (self);
|
|
}
|
|
|
|
static void
|
|
rfkill_proxy_acquired_cb (GObject *source_object,
|
|
GAsyncResult *res,
|
|
gpointer user_data)
|
|
{
|
|
CcWifiPanel *self;
|
|
GDBusProxy *proxy;
|
|
GError *error;
|
|
|
|
error = NULL;
|
|
proxy = cc_object_storage_create_dbus_proxy_finish (res, &error);
|
|
|
|
if (error)
|
|
{
|
|
if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
|
g_printerr ("Error creating rfkill proxy: %s\n", error->message);
|
|
|
|
g_error_free (error);
|
|
return;
|
|
}
|
|
|
|
self = CC_WIFI_PANEL (user_data);
|
|
|
|
self->rfkill_proxy = proxy;
|
|
|
|
sync_airplane_mode_switch (self);
|
|
}
|
|
|
|
static void
|
|
rfkill_switch_notify_activate_cb (GtkSwitch *rfkill_switch,
|
|
GParamSpec *pspec,
|
|
CcWifiPanel *self)
|
|
{
|
|
gboolean enable;
|
|
|
|
enable = gtk_switch_get_active (rfkill_switch);
|
|
|
|
g_dbus_proxy_call (self->rfkill_proxy,
|
|
"org.freedesktop.DBus.Properties.Set",
|
|
g_variant_new_parsed ("('org.gnome.SettingsDaemon.Rfkill',"
|
|
"'AirplaneMode', %v)",
|
|
g_variant_new_boolean (enable)),
|
|
G_DBUS_CALL_FLAGS_NONE,
|
|
-1,
|
|
self->cancellable,
|
|
NULL,
|
|
NULL);
|
|
}
|
|
|
|
/* Overrides */
|
|
|
|
static const gchar *
|
|
cc_wifi_panel_get_help_uri (CcPanel *panel)
|
|
{
|
|
return "help:gnome-help/net-wireless";
|
|
}
|
|
|
|
static GtkWidget *
|
|
cc_wifi_panel_get_title_widget (CcPanel *panel)
|
|
{
|
|
CcWifiPanel *self = CC_WIFI_PANEL (panel);
|
|
|
|
return GTK_WIDGET (self->center_stack);
|
|
}
|
|
|
|
static void
|
|
cc_wifi_panel_constructed (GObject *object)
|
|
{
|
|
CcWifiPanel *self;
|
|
CcShell *shell;
|
|
|
|
self = CC_WIFI_PANEL (object);
|
|
shell = cc_panel_get_shell (CC_PANEL (object));
|
|
|
|
G_OBJECT_CLASS (cc_wifi_panel_parent_class)->constructed (object);
|
|
|
|
cc_shell_embed_widget_in_header (shell, GTK_WIDGET (self->header_stack));
|
|
}
|
|
|
|
static void
|
|
cc_wifi_panel_finalize (GObject *object)
|
|
{
|
|
CcWifiPanel *self = (CcWifiPanel *)object;
|
|
|
|
g_cancellable_cancel (self->cancellable);
|
|
|
|
g_clear_object (&self->cancellable);
|
|
g_clear_object (&self->client);
|
|
g_clear_object (&self->rfkill_proxy);
|
|
|
|
g_clear_pointer (&self->devices, g_ptr_array_unref);
|
|
|
|
reset_command_line_args (self);
|
|
|
|
G_OBJECT_CLASS (cc_wifi_panel_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
cc_wifi_panel_get_property (GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
}
|
|
|
|
static void
|
|
cc_wifi_panel_set_property (GObject *object,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
CcWifiPanel *self = CC_WIFI_PANEL (object);
|
|
GVariant *parameters;
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_PARAMETERS:
|
|
reset_command_line_args (self);
|
|
|
|
parameters = g_value_get_variant (value);
|
|
|
|
if (parameters)
|
|
{
|
|
GPtrArray *array;
|
|
const gchar **args;
|
|
|
|
array = variant_av_to_string_array (parameters);
|
|
args = (const gchar **) array->pdata;
|
|
|
|
if (args[0])
|
|
{
|
|
if (g_str_equal (args[0], "create-wifi"))
|
|
self->arg_operation = OPERATION_CREATE_WIFI;
|
|
else if (g_str_equal (args[0], "connect-hidden-wifi"))
|
|
self->arg_operation = OPERATION_CONNECT_HIDDEN;
|
|
else if (g_str_equal (args[0], "connect-8021x-wifi"))
|
|
self->arg_operation = OPERATION_CONNECT_8021X;
|
|
else if (g_str_equal (args[0], "show-device"))
|
|
self->arg_operation = OPERATION_SHOW_DEVICE;
|
|
else
|
|
self->arg_operation = OPERATION_NULL;
|
|
}
|
|
|
|
if (args[0] && args[1])
|
|
self->arg_device = g_strdup (args[1]);
|
|
if (args[0] && args[1] && args[2])
|
|
self->arg_access_point = g_strdup (args[2]);
|
|
|
|
if (!verify_argv (self, (const char **) args))
|
|
{
|
|
reset_command_line_args (self);
|
|
g_ptr_array_unref (array);
|
|
return;
|
|
}
|
|
|
|
g_ptr_array_unref (array);
|
|
|
|
handle_argv (self);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
}
|
|
}
|
|
|
|
static void
|
|
cc_wifi_panel_class_init (CcWifiPanelClass *klass)
|
|
{
|
|
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
CcPanelClass *panel_class = CC_PANEL_CLASS (klass);
|
|
|
|
panel_class->get_help_uri = cc_wifi_panel_get_help_uri;
|
|
panel_class->get_title_widget = cc_wifi_panel_get_title_widget;
|
|
|
|
object_class->constructed = cc_wifi_panel_constructed;
|
|
object_class->finalize = cc_wifi_panel_finalize;
|
|
object_class->get_property = cc_wifi_panel_get_property;
|
|
object_class->set_property = cc_wifi_panel_set_property;
|
|
|
|
gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/network/wifi.ui");
|
|
|
|
gtk_widget_class_bind_template_child (widget_class, CcWifiPanel, center_stack);
|
|
gtk_widget_class_bind_template_child (widget_class, CcWifiPanel, header_stack);
|
|
gtk_widget_class_bind_template_child (widget_class, CcWifiPanel, main_stack);
|
|
gtk_widget_class_bind_template_child (widget_class, CcWifiPanel, rfkill_switch);
|
|
gtk_widget_class_bind_template_child (widget_class, CcWifiPanel, rfkill_widget);
|
|
gtk_widget_class_bind_template_child (widget_class, CcWifiPanel, sizegroup);
|
|
gtk_widget_class_bind_template_child (widget_class, CcWifiPanel, stack);
|
|
|
|
gtk_widget_class_bind_template_callback (widget_class, rfkill_switch_notify_activate_cb);
|
|
|
|
g_object_class_override_property (object_class, PROP_PARAMETERS, "parameters");
|
|
}
|
|
|
|
static void
|
|
cc_wifi_panel_init (CcWifiPanel *self)
|
|
{
|
|
g_resources_register (cc_network_get_resource ());
|
|
|
|
gtk_widget_init_template (GTK_WIDGET (self));
|
|
|
|
self->cancellable = g_cancellable_new ();
|
|
self->devices = g_ptr_array_new_with_free_func (g_object_unref);
|
|
|
|
/* Create and store a NMClient instance if it doesn't exist yet */
|
|
if (!cc_object_storage_has_object (CC_OBJECT_NMCLIENT))
|
|
{
|
|
NMClient *client = nm_client_new (NULL, NULL);
|
|
cc_object_storage_add_object (CC_OBJECT_NMCLIENT, client);
|
|
g_object_unref (client);
|
|
}
|
|
|
|
/* Load NetworkManager */
|
|
self->client = cc_object_storage_get_object (CC_OBJECT_NMCLIENT);
|
|
|
|
g_signal_connect_object (self->client,
|
|
"device-added",
|
|
G_CALLBACK (device_added_cb),
|
|
self,
|
|
0);
|
|
|
|
g_signal_connect_object (self->client,
|
|
"device-removed",
|
|
G_CALLBACK (device_removed_cb),
|
|
self,
|
|
0);
|
|
|
|
g_signal_connect_object (self->client,
|
|
"notify::wireless-enabled",
|
|
G_CALLBACK (wireless_enabled_cb),
|
|
self,
|
|
0);
|
|
|
|
/* Load Wi-Fi devices */
|
|
load_wifi_devices (self);
|
|
|
|
/* Acquire Airplane Mode proxy */
|
|
cc_object_storage_create_dbus_proxy (G_BUS_TYPE_SESSION,
|
|
G_DBUS_PROXY_FLAGS_NONE,
|
|
"org.gnome.SettingsDaemon.Rfkill",
|
|
"/org/gnome/SettingsDaemon/Rfkill",
|
|
"org.gnome.SettingsDaemon.Rfkill",
|
|
self->cancellable,
|
|
rfkill_proxy_acquired_cb,
|
|
self);
|
|
|
|
/* Handle comment-line arguments after loading devices */
|
|
handle_argv (self);
|
|
}
|