diff --git a/data/css/default.css b/data/css/default.css
index eed40763..3dfa00cf 100644
--- a/data/css/default.css
+++ b/data/css/default.css
@@ -72,28 +72,37 @@
border-radius: 3px;
color: #41A6B5;
}
-.excellent {
+.excellent>label
+{
color: #00ff00;
}
-.good {
+.good>label
+{
color: #88ff00;
}
-.ok {
+.ok>label
+{
color: #ffff00;
}
-.weak {
+.weak>label
+{
color: #ff8800;
}
-.none {
+.none>label
+{
color: #ff0000;
}
+.bluetooth>label
+{
+ color: #8888ff;
+}
.wf-dock,
-.wf-dock .out-box {
+.wf-dock>.out-box {
background: transparent;
}
@@ -104,18 +113,18 @@
border-radius: 1em;
}
-.wf-dock button {
+.wf-dock button.toplevel-icon {
background: transparent;
padding-left: 0rem;
padding-right: 0rem;
}
-.wf-dock button.activated {
+.wf-dock .box button.activated {
border-bottom: 3px solid white;
margin-bottom: 5px;
}
-.wf-dock image {
+.wf-dock .box image {
background: transparent;
padding-left: 0rem;
padding-right: 0rem;
@@ -123,7 +132,7 @@
-gtk-icon-transform: scale(0.7);
}
-.wf-dock image:hover {
+.wf-dock .box image:hover {
background: transparent;
padding-left: 0rem;
padding-right: 0rem;
@@ -131,14 +140,14 @@
-gtk-icon-transform: scale(1.0);
}
-.wf-dock image {
+.wf-dock .box image.toplevel-icon {
animation-name: embiggen;
animation-duration: 1000ms;
animation-timing-function: linear;
animation-iteration-count: 1;
}
-.wf-dock .closing image {
+.wf-dock .box .closing image.toplevel-icon {
animation-name: kromulent;
animation-duration: 1000ms;
animation-timing-function: linear;
@@ -146,6 +155,38 @@
animation-fill-mode: forwards;
}
+.wf-dock .network .image-button{
+ -gtk-icon-size:64px;
+}
+
+.wf-dock .network-control-center {
+ -gtk-icon-size: 48px;
+}
+
+.network-control-center image.access-point {
+ -gtk-icon-size: 40px;
+}
+
+.network-control-center .access-point.secure .security {
+ -gtk-icon-size:20px;
+ color:red;
+}
+
+.network-control-center .access-point.secure.has-password .security {
+ color:green;
+}
+
+.network-control-center .access-point.has-password{
+ font-weight: bold;
+ background: #8f83;
+}
+
+
+.network-control-center .access-point .band {
+ font-size: 1em;
+ font-weight: bolder;
+}
+
@keyframes embiggen {
to {
-gtk-icon-size: 64px;
diff --git a/metadata/dock.xml b/metadata/dock.xml
index 3451c111..43aee187 100644
--- a/metadata/dock.xml
+++ b/metadata/dock.xml
@@ -45,5 +45,9 @@
<_long>Keep all window icons for up to 2 seconds to allow CSS animations.
true
+
diff --git a/metadata/panel.xml b/metadata/panel.xml
index b59900e7..451e9f96 100644
--- a/metadata/panel.xml
+++ b/metadata/panel.xml
@@ -165,6 +165,10 @@
<_short>On Click Command
default
+
<_short>Menu
diff --git a/src/dock/dock.cpp b/src/dock/dock.cpp
index 763cbfdb..9c0a695c 100644
--- a/src/dock/dock.cpp
+++ b/src/dock/dock.cpp
@@ -4,12 +4,16 @@
#include
#include
+#include
#include
#include
#include
#include "dock.hpp"
#include "../util/gtk-utils.hpp"
+#include "network/manager.hpp"
+#include "network/network-widget.hpp"
+#include "network/network.hpp"
#include
@@ -20,13 +24,19 @@ class WfDock::impl
wl_surface *_wl_surface;
Gtk::Box out_box;
Gtk::Box box;
+ std::unique_ptr network_image;
+ std::unique_ptr network_control;
+ std::shared_ptr network_manager;
WfOption css_path{"dock/css_path"};
WfOption dock_height{"dock/dock_height"};
+ WfOption network{"dock/show_network_status"};
public:
impl(WayfireOutput *output)
{
+ network_image = std::make_unique("dock");
+
this->output = output;
window = std::unique_ptr(
new WayfireAutohidingWindow(output, "dock"));
@@ -55,6 +65,34 @@ class WfDock::impl
}
}
+ if (network)
+ {
+ network_manager = NetworkManager::getInstance();
+ add_child(*network_image);
+ network_control = std::make_unique();
+ network_image->get_popover()->set_child(*network_control);
+ network_image->set_has_frame(false);
+ network_manager->signal_default_changed().connect(
+ [this] (std::shared_ptr network)
+ {
+ network_image->set_icon_name(network->get_icon_name());
+ for (auto clas : network_image->get_css_classes())
+ {
+ network_image->remove_css_class(clas);
+ }
+
+ for (auto clas : network->get_css_classes())
+ {
+ network_image->add_css_class(clas);
+ }
+
+ network_image->set_tooltip_text(network->get_name());
+
+ network_image->add_css_class("network");
+ network_image->add_css_class("flat");
+ });
+ }
+
window->present();
_wl_surface = gdk_wayland_surface_get_wl_surface(
window->get_surface()->gobj());
diff --git a/src/dock/dock.hpp b/src/dock/dock.hpp
index d4db695c..ea5b449c 100644
--- a/src/dock/dock.hpp
+++ b/src/dock/dock.hpp
@@ -1,10 +1,12 @@
#pragma once
#include
+#include
#include
#include
#include
+#include "network/manager.hpp"
#include "wf-shell-app.hpp"
class WfDock
diff --git a/src/panel/panel.cpp b/src/panel/panel.cpp
index 0864fb1f..852e65e3 100644
--- a/src/panel/panel.cpp
+++ b/src/panel/panel.cpp
@@ -14,6 +14,7 @@
#include
#include "panel.hpp"
+#include "network/manager.hpp"
#include "widgets/battery.hpp"
#include "widgets/command-output.hpp"
#include "widgets/language.hpp"
@@ -243,6 +244,7 @@ class WayfirePanel::impl
{
const auto lock_sn_watcher = Watcher::Instance();
const auto lock_notification_daemon = Daemon::Instance();
+ const auto lock_network_manager = NetworkManager::getInstanceIfExists();
for (auto child : box.get_children())
{
box.remove(*child);
diff --git a/src/panel/widgets/network.cpp b/src/panel/widgets/network.cpp
index 788a6745..a74fa7dc 100644
--- a/src/panel/widgets/network.cpp
+++ b/src/panel/widgets/network.cpp
@@ -1,367 +1,98 @@
#include "network.hpp"
+#include "gtkmm/gesture.h"
+#include "gtkmm/gestureclick.h"
+#include "gtkmm/gesturelongpress.h"
+#include "network/network.hpp"
#include
#include
-#include
#include
+#include
-#define NM_DBUS_NAME "org.freedesktop.NetworkManager"
-#define ACTIVE_CONNECTION "PrimaryConnection"
-#define STRENGTH "Strength"
+WayfireNetworkInfo::WayfireNetworkInfo()
+{}
-std::string WfNetworkConnectionInfo::get_control_center_section(DBusProxy& nm)
-{
- Glib::Variant wifi;
- nm->get_cached_property(wifi, "WirelessEnabled");
-
- return wifi.get() ? "wifi" : "network";
-}
-
-void WfNetworkConnectionInfo::spawn_control_center(DBusProxy& nm)
-{
- std::string command = "env XDG_CURRENT_DESKTOP=GNOME gnome-control-center ";
- command += get_control_center_section(nm);
-
- Glib::spawn_command_line_async(command);
-}
-
-struct NoConnectionInfo : public WfNetworkConnectionInfo
-{
- std::string get_icon_name(WfConnectionState state)
- {
- return "network-offline-symbolic";
- }
-
- int get_connection_strength()
- {
- return 0;
- }
-
- std::string get_strength_str()
- {
- return "none";
- }
-
- std::string get_ip()
- {
- return "127.0.0.1";
- }
-
- virtual ~NoConnectionInfo()
- {}
-};
-
-struct WifiConnectionInfo : public WfNetworkConnectionInfo
+void WayfireNetworkInfo::init(Gtk::Box *container)
{
- WayfireNetworkInfo *widget;
- DBusProxy ap;
- sigc::connection ap_sig;
-
- WifiConnectionInfo(const DBusConnection& connection, std::string path,
- WayfireNetworkInfo *widget)
- {
- this->widget = widget;
-
- ap = Gio::DBus::Proxy::create_sync(connection, NM_DBUS_NAME, path,
- "org.freedesktop.NetworkManager.AccessPoint");
-
- if (ap)
- {
- ap_sig = ap->signal_properties_changed().connect(
- sigc::mem_fun(*this, &WifiConnectionInfo::on_properties_changed));
- }
- }
-
- void on_properties_changed(DBusPropMap changed, DBusPropList invalid)
- {
- bool needs_refresh = false;
- for (auto& prop : changed)
- {
- if (prop.first == STRENGTH)
- {
- needs_refresh = true;
- }
- }
-
- if (needs_refresh)
- {
- widget->update_icon();
- widget->update_status();
- }
- }
-
- int get_strength()
- {
- assert(ap);
-
- Glib::Variant vstr;
- ap->get_cached_property(vstr, STRENGTH);
-
- return vstr.get();
- }
-
- std::string get_strength_str()
- {
- int value = get_strength();
-
- if (value > 80)
- {
- return "excellent";
- }
-
- if (value > 55)
- {
- return "good";
- }
-
- if (value > 30)
- {
- return "ok";
- }
-
- if (value > 5)
- {
- return "weak";
- }
+ network_manager = NetworkManager::getInstance();
+ button = std::make_unique("panel");
+ button->add_css_class("widget-icon");
+ button->add_css_class("flat");
+ button->add_css_class("network");
- return "none";
- }
+ container->append(*button);
+ button->set_child(button_content);
+ button->add_css_class("flat");
- virtual std::string get_icon_name(WfConnectionState state)
- {
- if ((state <= CSTATE_ACTIVATING) || (state == CSTATE_DEACTIVATING))
- {
- return "network-wireless-acquiring-symbolic";
- }
+ button->get_popover()->set_child(control);
- if (state == CSTATE_DEACTIVATED)
- {
- return "network-wireless-disconnected-symbolic";
- }
+ button_content.set_valign(Gtk::Align::CENTER);
+ button_content.append(icon);
+ button_content.append(status);
+ button_content.set_spacing(6);
- if (ap)
- {
- return "network-wireless-signal-" + get_strength_str() + "-symbolic";
- } else
- {
- return "network-wireless-no-route-symbolic";
- }
- }
+ icon.set_valign(Gtk::Align::CENTER);
- virtual int get_connection_strength()
+ auto show_hide_label = [this] ()
{
- if (ap)
+ if (no_label)
{
- return get_strength();
+ status.hide();
} else
{
- return 100;
- }
- }
-
- virtual std::string get_ip()
- {
- return "0.0.0.0";
- }
-
- virtual ~WifiConnectionInfo()
- {}
-};
-
-struct EthernetConnectionInfo : public WfNetworkConnectionInfo
-{
- DBusProxy ap;
- EthernetConnectionInfo(const DBusConnection& connection, std::string path)
- {}
-
- virtual std::string get_icon_name(WfConnectionState state)
- {
- if ((state <= CSTATE_ACTIVATING) || (state == CSTATE_DEACTIVATING))
- {
- return "network-wired-acquiring-symbolic";
+ status.show();
}
-
- if (state == CSTATE_DEACTIVATED)
- {
- return "network-wired-disconnected-symbolic";
- }
-
- return "network-wired-symbolic";
- }
-
- std::string get_connection_name()
- {
- return "Ethernet - " + connection_name;
- }
-
- std::string get_strength_str()
- {
- return "excellent";
- }
-
- virtual int get_connection_strength()
- {
- return 100;
- }
-
- virtual std::string get_ip()
- {
- return "0.0.0.0";
- }
-
- virtual ~EthernetConnectionInfo()
- {}
-};
-
-
-/* TODO: handle Connectivity */
-
-static WfConnectionState get_connection_state(DBusProxy connection)
-{
- if (!connection)
- {
- return CSTATE_DEACTIVATED;
- }
-
- Glib::Variant state;
- connection->get_cached_property(state, "State");
- return (WfConnectionState)state.get();
-}
-
-void WayfireNetworkInfo::update_icon()
-{
- auto icon_name = info->get_icon_name(
- get_connection_state(active_connection_proxy));
- icon.set_from_icon_name(icon_name);
-}
-
-struct status_color
-{
- int point;
- Gdk::RGBA rgba;
-} status_colors[] = {
- {0, Gdk::RGBA{"#ff0000"}},
- {25, Gdk::RGBA{"#ff0000"}},
- {40, Gdk::RGBA{"#ffff55"}},
- {100, Gdk::RGBA{"#00ff00"}},
-};
-
-#define MAX_COLORS (sizeof(status_colors) / sizeof(status_color))
-
-void WayfireNetworkInfo::update_status()
-{
- std::string description = info->get_connection_name();
-
- status.set_text(description);
- button.set_tooltip_text(description);
-
- status.remove_css_class("excellent");
- status.remove_css_class("good");
- status.remove_css_class("weak");
- status.remove_css_class("none");
- if (status_color_opt)
- {
- status.add_css_class(info->get_strength_str());
- }
-}
-
-void WayfireNetworkInfo::update_active_connection()
-{
- Glib::Variant active_conn_path;
- nm_proxy->get_cached_property(active_conn_path, ACTIVE_CONNECTION);
-
- if (active_conn_path && (active_conn_path.get() != "/"))
- {
- active_connection_proxy = Gio::DBus::Proxy::create_sync(
- connection, NM_DBUS_NAME, active_conn_path.get(),
- "org.freedesktop.NetworkManager.Connection.Active");
- } else
- {
- active_connection_proxy = DBusProxy();
- }
-
- auto set_no_connection = [=] ()
- {
- info = std::unique_ptr(new NoConnectionInfo());
- info->connection_name = "No connection";
};
+ no_label.set_callback(show_hide_label);
+ show_hide_label();
- if (!active_connection_proxy)
+ auto click = Gtk::GestureClick::create();
+ click->set_button(3);
+ signals.push_back(click->signal_released().connect(
+ [this] (int, double, double)
{
- set_no_connection();
- } else
+ on_click();
+ }));
+ signals.push_back(click->signal_pressed().connect(
+ [click] (int, double, double)
{
- Glib::Variant vtype, vobject;
- active_connection_proxy->get_cached_property(vtype, "Type");
- active_connection_proxy->get_cached_property(vobject, "SpecificObject");
- auto type = vtype.get();
- auto object = vobject.get();
+ click->set_state(Gtk::EventSequenceState::CLAIMED);
+ }));
- if (type.find("wireless") != type.npos)
- {
- info = std::unique_ptr(
- new WifiConnectionInfo(connection, object, this));
- } else if (type.find("ethernet") != type.npos)
- {
- info = std::unique_ptr(
- new EthernetConnectionInfo(connection, object));
- } else if (type.find("bluetooth"))
- {
- std::cout << "Unimplemented: bluetooth connection" << std::endl;
- set_no_connection();
- // TODO
- } else
- {
- std::cout << "Unimplemented: unknown connection type" << std::endl;
- set_no_connection();
- // TODO: implement Unknown connection
- }
+ auto touch = Gtk::GestureLongPress::create();
+ touch->set_touch_only(true);
+ signals.push_back(touch->signal_pressed().connect(
+ [this] (double, double)
+ {
+ on_click();
+ }));
- Glib::Variant vname;
- active_connection_proxy->get_cached_property(vname, "Id");
- info->connection_name = vname.get();
- }
+ button->add_controller(touch);
+ button->add_controller(click);
- update_icon();
- update_status();
+ signals.push_back(network_manager->signal_default_changed().connect(
+ sigc::mem_fun(*this, &WayfireNetworkInfo::set_connection)));
+ set_connection(network_manager->get_primary_network());
}
-void WayfireNetworkInfo::on_nm_properties_changed(
- const Gio::DBus::Proxy::MapChangedProperties& properties,
- const std::vector& invalidated)
+void WayfireNetworkInfo::set_connection(std::shared_ptr network)
{
- for (auto & prop : properties)
+ for (auto clas : button_content.get_css_classes())
{
- if (prop.first == ACTIVE_CONNECTION)
+ if ((clas == "flat") || (clas == "network") || (clas == "widget-icon"))
{
- update_active_connection();
+ continue;
}
- }
-}
-bool WayfireNetworkInfo::setup_dbus()
-{
- auto cancellable = Gio::Cancellable::create();
- connection = Gio::DBus::Connection::get_sync(Gio::DBus::BusType::SYSTEM, cancellable);
- if (!connection)
- {
- std::cerr << "Failed to connect to dbus" << std::endl;
- return false;
+ button_content.remove_css_class(clas);
}
- nm_proxy = Gio::DBus::Proxy::create_sync(connection, NM_DBUS_NAME,
- "/org/freedesktop/NetworkManager",
- "org.freedesktop.NetworkManager");
- if (!nm_proxy)
+ for (auto clas : network->get_css_classes())
{
- std::cerr << "Failed to connect to network manager, " <<
- "are you sure it is running?" << std::endl;
- return false;
+ button_content.add_css_class(clas);
}
- signals.push_back(nm_proxy->signal_properties_changed().connect(
- sigc::mem_fun(*this, &WayfireNetworkInfo::on_nm_properties_changed)));
-
- return true;
+ status.set_label(network->get_name());
+ icon.set_from_icon_name(network->get_icon_symbolic());
}
void WayfireNetworkInfo::on_click()
@@ -371,63 +102,9 @@ void WayfireNetworkInfo::on_click()
Glib::spawn_command_line_async((std::string)click_command_opt);
} else
{
- info->spawn_control_center(nm_proxy);
- }
-}
-
-void WayfireNetworkInfo::init(Gtk::Box *container)
-{
- if (!setup_dbus())
- {
- enabled = false;
- return;
- }
-
- button.add_css_class("widget-icon");
- button.add_css_class("flat");
- button.add_css_class("network");
-
- container->append(button);
- button.set_child(button_content);
- button.add_css_class("flat");
-
- signals.push_back(button.signal_clicked().connect(
- sigc::mem_fun(*this, &WayfireNetworkInfo::on_click)));
-
- button_content.set_valign(Gtk::Align::CENTER);
- button_content.append(icon);
- button_content.append(status);
- button_content.set_spacing(6);
-
- icon.set_valign(Gtk::Align::CENTER);
- signals.push_back(icon.property_scale_factor().signal_changed().connect(
- sigc::mem_fun(*this, &WayfireNetworkInfo::update_icon)));
- icon.add_css_class("network-icon");
-
- update_active_connection();
- handle_config_reload();
-}
-
-void WayfireNetworkInfo::handle_config_reload()
-{
- if (status_opt.value() == NETWORK_STATUS_ICON)
- {
- if (status.get_parent())
- {
- button_content.remove(status);
- }
- } else
- {
- if (!status.get_parent())
- {
- button_content.append(status);
- }
+ std::string command = "env XDG_CURRENT_DESKTOP=GNOME gnome-control-center";
+ Glib::spawn_command_line_async(command);
}
-
- // TODO: show IP for "full" status
-
- update_icon();
- update_status();
}
WayfireNetworkInfo::~WayfireNetworkInfo()
diff --git a/src/panel/widgets/network.hpp b/src/panel/widgets/network.hpp
index e6ab1d90..271ac444 100644
--- a/src/panel/widgets/network.hpp
+++ b/src/panel/widgets/network.hpp
@@ -5,82 +5,34 @@
#include
#include
#include
+#include
#include
+#include "wf-popover.hpp"
#include "../widget.hpp"
-
-using DBusConnection = Glib::RefPtr;
-using DBusProxy = Glib::RefPtr;
-
-using DBusPropMap = const Gio::DBus::Proxy::MapChangedProperties&;
-using DBusPropList = const std::vector&;
-
-enum WfConnectionState // NmActiveConnectionState
-{
- CSTATE_UNKNOWN = 0,
- CSTATE_ACTIVATING = 1,
- CSTATE_ACTIVATED = 2,
- CSTATE_DEACTIVATING = 3,
- CSTATE_DEACTIVATED = 4,
-};
-
-struct WfNetworkConnectionInfo
-{
- std::string connection_name;
-
- virtual void spawn_control_center(DBusProxy& nm);
- virtual std::string get_control_center_section(DBusProxy& nm);
-
- virtual std::string get_connection_name()
- {
- return connection_name;
- }
-
- virtual std::string get_icon_name(WfConnectionState state) = 0;
- virtual int get_connection_strength() = 0;
- virtual std::string get_ip() = 0;
- virtual std::string get_strength_str() = 0;
-
- virtual ~WfNetworkConnectionInfo()
- {}
-};
-
-static const std::string NETWORK_STATUS_ICON = "none";
-static const std::string NETWORK_STATUS_CONN_NAME = "connection";
-static const std::string NETWORK_STATUS_NAME_IP = "full";
+#include "../../util/network/manager.hpp"
+#include "network/network-widget.hpp"
class WayfireNetworkInfo : public WayfireWidget
{
- DBusConnection connection;
- DBusProxy nm_proxy, active_connection_proxy;
-
- std::unique_ptr info;
-
+ void on_click();
std::vector signals;
-
- Gtk::Button button;
+ std::shared_ptr network_manager;
+ std::unique_ptr button;
Gtk::Box button_content;
Gtk::Image icon;
Gtk::Label status;
-
- bool enabled = true;
WfOption status_opt{"panel/network_status"};
WfOption status_color_opt{"panel/network_status_use_color"};
WfOption status_font_opt{"panel/network_status_font"};
WfOption click_command_opt{"panel/network_onclick_command"};
+ WfOption no_label{"panel/network_no_label"};
- bool setup_dbus();
- void update_active_connection();
- void on_nm_properties_changed(DBusPropMap properties,
- DBusPropList invalidated);
-
- void on_click();
+ NetworkControlWidget control;
public:
- void update_icon();
- void update_status();
-
+ WayfireNetworkInfo();
+ ~WayfireNetworkInfo();
void init(Gtk::Box *container);
- void handle_config_reload();
- virtual ~WayfireNetworkInfo();
+ void set_connection(std::shared_ptr network);
};
diff --git a/src/util/meson.build b/src/util/meson.build
index 5621206e..d835637f 100644
--- a/src/util/meson.build
+++ b/src/util/meson.build
@@ -8,9 +8,26 @@ util = static_library(
'css-config.cpp',
'wf-ipc.cpp',
'animated-scale.cpp',
+ 'network/manager.cpp',
+ 'network/wifi.cpp',
+ 'network/wired.cpp',
+ 'network/network-widget.cpp',
+ 'network/wifi-ap.cpp',
+ 'network/network.cpp',
+ 'network/modem.cpp',
+ 'network/settings.cpp',
+ 'network/connection.cpp',
+ ],
+ dependencies: [
+ wf_protos,
+ gtklayershell,
+ wayland_client,
+ gtkmm,
+ wfconfig,
+ libinotify,
+ json,
],
- dependencies: [wf_protos, gtklayershell, wayland_client, gtkmm, wfconfig, libinotify, json],
)
util_includes = include_directories('.')
-libutil = declare_dependency(link_with: util, include_directories: util_includes)
+libutil = declare_dependency(link_with: util, include_directories: util_includes)
\ No newline at end of file
diff --git a/src/util/network/bluetooth.hpp b/src/util/network/bluetooth.hpp
new file mode 100644
index 00000000..b0bf2ecc
--- /dev/null
+++ b/src/util/network/bluetooth.hpp
@@ -0,0 +1,47 @@
+#pragma once
+
+#include
+#include
+
+#include "network.hpp"
+class BluetoothNetwork : public Network
+{
+ public:
+ std::shared_ptr bluetooth_proxy;
+ BluetoothNetwork(std::string path, std::shared_ptr device_proxy,
+ std::shared_ptr bluetooth_proxy) :
+ Network(path, device_proxy), bluetooth_proxy(bluetooth_proxy)
+ {}
+
+ std::string get_name() override
+ {
+ if (bluetooth_proxy)
+ {
+ Glib::Variant name;
+ bluetooth_proxy->get_cached_property(name, "Name");
+ return name.get();
+ }
+
+ return "Bluetooth";
+ }
+
+ std::string get_icon_name() override
+ {
+ if (is_active())
+ {
+ return "network-bluetooth-activated";
+ }
+
+ return "network-bluetooth-inactive";
+ }
+
+ std::vector get_css_classes() override
+ {
+ return {"bluetooth"};
+ }
+
+ std::string get_friendly_name() override
+ {
+ return "Bluetooth";
+ }
+};
diff --git a/src/util/network/connection.cpp b/src/util/network/connection.cpp
new file mode 100644
index 00000000..0b3c5c15
--- /dev/null
+++ b/src/util/network/connection.cpp
@@ -0,0 +1,89 @@
+#include "connection.hpp"
+#include "network/manager.hpp"
+
+Connection::Connection() :
+ Network("/", nullptr), connection_proxy(nullptr), devices({})
+{}
+
+Connection::Connection(std::string path, std::shared_ptr connection_proxy,
+ std::vector> devices) :
+ Network(path, nullptr), connection_proxy(connection_proxy), devices(devices)
+{
+ /* Bubble up emits from any device here */
+ for (auto & it : devices)
+ {
+ signals.push_back(it->signal_network_altered().connect(
+ [this] ()
+ {
+ network_altered.emit();
+ }));
+ }
+
+ Glib::Variant vpn_data;
+ connection_proxy->get_cached_property(vpn_data, "Vpn");
+ has_vpn = vpn_data.get();
+
+ if (has_vpn)
+ {
+ Glib::Variant vpn_path_start;
+ connection_proxy->get_cached_property(vpn_path_start, "Connection");
+ vpn_path = vpn_path_start.get();
+ }
+}
+
+Connection::~Connection()
+{
+ for (auto signal : signals)
+ {
+ signal.disconnect();
+ }
+}
+
+std::string Connection::get_name()
+{
+ if (devices.size() == 0)
+ {
+ return "No connection";
+ }
+
+ std::string secure = "";
+ if (has_vpn)
+ {
+ auto settings = NetworkManager::getInstance()->get_vpn(vpn_path);
+ if (settings)
+ {
+ secure = " with " + settings->name;
+ } else
+ {
+ secure = " with VPN";
+ }
+ }
+
+ return devices[0]->get_name() + secure;
+}
+
+std::string Connection::get_icon_name()
+{
+ if (devices.size() == 0)
+ {
+ return "network-disconnected";
+ }
+
+ std::string secure = "";
+ if (has_vpn)
+ {
+ secure = devices[0]->get_secure_variant();
+ }
+
+ return devices[0]->get_icon_name() + secure;
+}
+
+std::vector Connection::get_css_classes()
+{
+ if (devices.size() == 0)
+ {
+ return {"none"};
+ }
+
+ return devices[0]->get_css_classes();
+}
diff --git a/src/util/network/connection.hpp b/src/util/network/connection.hpp
new file mode 100644
index 00000000..b42d72f3
--- /dev/null
+++ b/src/util/network/connection.hpp
@@ -0,0 +1,25 @@
+#pragma once
+#include
+#include
+#include
+
+#include "network.hpp"
+
+/* Information about an active connection */
+class Connection : public Network
+{
+ public:
+ bool has_vpn;
+ std::vector signals;
+ std::shared_ptr connection_proxy, vpn_proxy;
+ std::vector> devices;
+ std::string vpn_path = "";
+
+ Connection();
+ Connection(std::string path, std::shared_ptr connection_proxy,
+ std::vector> devices);
+ ~Connection();
+ std::string get_name();
+ std::string get_icon_name();
+ std::vector get_css_classes();
+};
diff --git a/src/util/network/manager.cpp b/src/util/network/manager.cpp
new file mode 100644
index 00000000..8a9ac238
--- /dev/null
+++ b/src/util/network/manager.cpp
@@ -0,0 +1,714 @@
+#include
+#include
+#include
+#include
+#include
+
+#include "manager.hpp"
+#include "bluetooth.hpp"
+#include "connection.hpp"
+#include "gtkmm/enums.h"
+#include "network.hpp"
+#include "network/settings.hpp"
+#include "sigc++/functors/mem_fun.h"
+#include "vpn.hpp"
+#include "wifi.hpp"
+#include "modem.hpp"
+#include "wired.hpp"
+#include "null.hpp"
+#define NM_DBUS_NAME "org.freedesktop.NetworkManager"
+#define MM_DBUS_NAME "org.freedesktop.ModemManager1"
+#define ACTIVE_CONNECTION "PrimaryConnection"
+#define STRENGTH "Strength"
+
+#define NM_PATH "/org/freedesktop/NetworkManager"
+
+#define DBUS_PROPERTIES_INTERFACE "org.freedesktop.DBus.Properties"
+#define NM_INTERFACE "org.freedesktop.NetworkManager"
+
+#define ETHERNET_TYPE 1
+#define WIFI_TYPE 2
+#define MODEM_TYPE 8
+#define GENERIC_TYPE 14
+#define BLUETOOTH_TYPE 5
+
+NetworkManager::NetworkManager()
+{
+ popup_window.set_child(popup_box);
+ popup_box.append(popup_label);
+ popup_box.append(popup_entry);
+ popup_box.set_orientation(Gtk::Orientation::VERTICAL);
+ popup_entry.set_visibility(false);
+ our_signals.push_back(popup_entry.signal_activate().connect(
+ sigc::mem_fun(*this, &NetworkManager::submit_password)));
+ Gio::DBus::Proxy::create_for_bus(Gio::DBus::BusType::SYSTEM,
+ "org.freedesktop.DBus",
+ "/org/freedesktop/DBus",
+ "org.freedesktop.DBus",
+ [this] (const Glib::RefPtr & result)
+ {
+ // Got a dbus proxy
+ manager_proxy = Gio::DBus::Proxy::create_finish(result);
+ auto val = manager_proxy->call_sync("ListNames");
+ Glib::Variant> list;
+ val.get_child(list, 0);
+ auto l2 = list.get();
+ for (auto t : l2)
+ {
+ if (t == NM_DBUS_NAME)
+ {
+ connect_nm();
+ }
+
+ if (t == MM_DBUS_NAME)
+ {
+ mm_start.emit();
+ }
+ }
+
+ /* https://dbus.freedesktop.org/doc/dbus-java/api/org/freedesktop/DBus.NameOwnerChanged.html */
+ our_signals.push_back(manager_proxy->signal_signal().connect(
+ [this] (const Glib::ustring & sender_name,
+ const Glib::ustring & signal_name,
+ const Glib::VariantContainerBase & params)
+ {
+ if (signal_name == "NameOwnerChanged")
+ {
+ Glib::Variant to, from, name;
+ params.get_child(name, 0);
+ params.get_child(from, 1);
+ params.get_child(to, 2);
+ if (name.get() == NM_DBUS_NAME)
+ {
+ if (from.get() == "")
+ {
+ connect_nm();
+ } else if (to.get() == "")
+ {
+ lost_nm();
+ }
+ } else if (name.get() == MM_DBUS_NAME)
+ {
+ if (from.get() == "")
+ {
+ mm_start.emit();
+ } else if (to.get() == "")
+ {
+ for_each(all_devices.cbegin(), all_devices.cend(),
+ [this] (std::map>::const_reference it)
+ {
+ if (std::dynamic_pointer_cast(it.second) != nullptr)
+ {
+ device_removed.emit(it.second);
+ all_devices.erase(it.first);
+ }
+ });
+ mm_stop.emit();
+ }
+ }
+ }
+ }));
+ });
+}
+
+void NetworkManager::setting_added(std::string path)
+{
+ auto proxy = Gio::DBus::Proxy::create_sync(
+ connection,
+ NM_DBUS_NAME,
+ path,
+ "org.freedesktop.NetworkManager.Settings.Connection");
+
+ all_settings.emplace(path,
+ new NetworkSettings(path, proxy));
+ auto setting = all_settings[path];
+
+ if (setting->get_ssid() != "")
+ {
+ for (auto & device : all_devices)
+ {
+ auto wifi = std::dynamic_pointer_cast(device.second);
+ if (wifi)
+ {
+ for (auto & ap : wifi->all_access_points)
+ {
+ if (ap.second && (ap.second->get_ssid() == setting->get_ssid()))
+ {
+ ap.second->set_has_saved_password(true);
+ }
+ }
+ }
+ }
+ }
+}
+
+void NetworkManager::setting_removed(std::string path)
+{
+ auto setting = all_settings[path];
+ if (setting)
+ {
+ for (auto & device : all_devices)
+ {
+ auto wifi = std::dynamic_pointer_cast(device.second);
+ if (wifi)
+ {
+ for (auto & ap : wifi->all_access_points)
+ {
+ if (ap.second->get_ssid() == setting->get_ssid())
+ {
+ ap.second->set_has_saved_password(false);
+ }
+ }
+ }
+ }
+ }
+
+ all_settings.erase(path);
+}
+
+void NetworkManager::lost_nm()
+{
+ if (primary_signal)
+ {
+ primary_signal.disconnect();
+ }
+
+ if (debounce)
+ {
+ debounce.disconnect();
+ }
+
+ std::cout << "NetworkManager Lost" << std::endl;
+ connection = nullptr;
+ settings_proxy = nullptr;
+ nm_proxy = nullptr;
+ all_devices.clear();
+ all_vpns.clear();
+ primary_connection = "/";
+ primary_connection_obj = std::make_shared();
+ for (auto signal : nm_dbus_signals)
+ {
+ signal.disconnect();
+ }
+
+ nm_stop.emit();
+}
+
+void NetworkManager::connect_nm()
+{
+ std::cout << "NetworkManager Found" << std::endl;
+ all_devices.emplace("/", new NullNetwork());
+ connection = Gio::DBus::Connection::get_sync(Gio::DBus::BusType::SYSTEM);
+ if (!connection)
+ {
+ std::cerr << "Failed to connect to dbus" << std::endl;
+ return;
+ }
+
+ /* Get known VPNs */
+ settings_proxy = Gio::DBus::Proxy::create_sync(connection,
+ NM_DBUS_NAME,
+ "/org/freedesktop/NetworkManager/Settings",
+ "org.freedesktop.NetworkManager.Settings");
+ if (!settings_proxy)
+ {
+ std::cerr << "No NM Settings proxy" << std::endl;
+ return;
+ }
+
+ auto ret1 = settings_proxy->call_sync("ListConnections").get_child();
+ Glib::Variant> ret =
+ Glib::VariantBase::cast_dynamic>>(ret1);
+ for (auto & it : ret.get())
+ {
+ setting_added(it);
+ check_add_vpn(it);
+ vpn_added.emit(it);
+ }
+
+ nm_dbus_signals.push_back(settings_proxy->signal_signal().connect(
+ [this] (const Glib::ustring& sender, const Glib::ustring& signal,
+ const Glib::VariantContainerBase& container)
+ {
+ if (signal == "ConnectionRemoved")
+ {
+ auto var =
+ Glib::VariantBase::cast_dynamic>(container.get_child()).get();
+ all_settings.erase(var);
+ all_vpns.erase(var);
+ vpn_removed.emit(var);
+ } else if (signal == "NewConnection")
+ {
+ auto var =
+ Glib::VariantBase::cast_dynamic>(container.get_child()).get();
+ setting_added(var);
+ check_add_vpn(var);
+ vpn_added.emit(var);
+ }
+ }));
+
+ nm_proxy = Gio::DBus::Proxy::create_sync(connection, NM_DBUS_NAME,
+ NM_PATH,
+ NM_INTERFACE);
+ if (!nm_proxy)
+ {
+ std::cerr << "Failed to connect to network manager, " <<
+ "are you sure it is running?" << std::endl;
+ return;
+ }
+
+ nm_dbus_signals.push_back(nm_proxy->signal_properties_changed().connect(
+ sigc::mem_fun(*this, &NetworkManager::on_nm_properties_changed)));
+
+ nm_dbus_signals.push_back(nm_proxy->signal_signal().connect(
+ sigc::mem_fun(*this, &NetworkManager::on_nm_signal)));
+
+ /* Fill Initial List*/
+
+ nm_proxy->call("GetAllDevices", sigc::mem_fun(*this, &NetworkManager::get_all_devices_cb));
+}
+
+void NetworkManager::check_add_vpn(std::string path)
+{
+ auto proxy = Gio::DBus::Proxy::create_sync(connection,
+ NM_DBUS_NAME,
+ path,
+ "org.freedesktop.NetworkManager.Settings.Connection");
+
+ auto values = Glib::VariantBase::cast_dynamic>>>(proxy->call_sync("GetSettings").get_child());
+
+ auto hash = values.get();
+ if ((hash.count("connection") == 1) && (hash["connection"].count("id") == 1) &&
+ (hash["connection"].count("type") == 1))
+ {
+ auto contype = hash["connection"]["type"];
+ auto conname = hash["connection"]["id"];
+ auto strtype = Glib::VariantType("s");
+ if (contype.is_of_type(strtype) && conname.is_of_type(strtype))
+ {
+ auto name = Glib::VariantBase::cast_dynamic>(conname).get();
+ auto contype_str = Glib::VariantBase::cast_dynamic>(contype).get();
+ if (contype_str == "vpn")
+ {
+ all_vpns[path] = std::make_shared(path, proxy, name);
+ }
+ } else
+ {
+ std::cerr << "INVALID TYPES " << conname.get_type_string() << " " << contype.get_type_string() <<
+ std::endl;
+ }
+ }
+}
+
+void NetworkManager::get_all_devices_cb(std::shared_ptr async)
+{
+ auto list = Glib::VariantBase::cast_dynamic>>(nm_proxy->call_finish(
+ async).get_child()).get();
+ for (auto & val : list)
+ {
+ add_network(val);
+ }
+
+ /* Now get the current connection */
+ nm_dbus_signals.push_back(Glib::signal_idle().connect([this] ()
+ {
+ Glib::Variant path_read;
+ nm_proxy->get_cached_property(path_read, "PrimaryConnection");
+ changed_primary(path_read.get());
+ return G_SOURCE_REMOVE;
+ }));
+ /* And emit a start event */
+ nm_start.emit();
+}
+
+void NetworkManager::add_network(std::string path)
+{
+ Glib::RefPtr device_proxy = Gio::DBus::Proxy::create_sync(connection,
+ NM_DBUS_NAME,
+ path,
+ "org.freedesktop.NetworkManager.Device");
+ Glib::Variant type;
+ device_proxy->get_cached_property(type, "DeviceType");
+ uint connection_type = type.get();
+ if (connection_type == WIFI_TYPE)
+ {
+ auto wifi_proxy = Gio::DBus::Proxy::create_sync(connection,
+ NM_DBUS_NAME,
+ path,
+ "org.freedesktop.NetworkManager.Device.Wireless");
+ all_devices.emplace(path, new WifiNetwork(path, device_proxy, wifi_proxy));
+ device_added.emit(all_devices[path]);
+ } else if (connection_type == MODEM_TYPE)
+ {
+ auto modem_proxy = Gio::DBus::Proxy::create_sync(connection,
+ NM_DBUS_NAME,
+ path,
+ "org.freedesktop.NetworkManager.Device.Modem");
+ all_devices.emplace(path, new ModemNetwork(path, device_proxy, modem_proxy));
+ device_added.emit(all_devices[path]);
+ } else if (connection_type == BLUETOOTH_TYPE)
+ {
+ auto bluetooth_proxy = Gio::DBus::Proxy::create_sync(connection,
+ NM_DBUS_NAME,
+ path,
+ "org.freedesktop.NetworkManager.Device.Bluetooth");
+ all_devices.emplace(path, new BluetoothNetwork(path, device_proxy, bluetooth_proxy));
+ device_added.emit(all_devices[path]);
+ } else if (connection_type == ETHERNET_TYPE)
+ {
+ all_devices.emplace(path, new WiredNetwork(path, device_proxy));
+ device_added.emit(all_devices[path]);
+ }
+}
+
+void NetworkManager::on_nm_properties_changed(const Gio::DBus::Proxy::MapChangedProperties& properties,
+ const std::vector& invalidated)
+{
+ for (auto & it : properties)
+ {
+ if (it.first == "PrimaryConnection")
+ {
+ auto value = Glib::VariantBase::cast_dynamic>(it.second).get();
+ changed_primary(value);
+ } else if ((it.first == "NetworkingEnabled") || (it.first == "WirelessEnabled") ||
+ (it.first == "WirelessHardwareEnabled") ||
+ (it.first == "WwanEnabled") || (it.first == "WwanHardwareEnabled"))
+ {
+ global_change.emit();
+ }
+ }
+}
+
+void NetworkManager::changed_primary(std::string value)
+{
+ if (value != primary_connection)
+ {
+ if (primary_signal)
+ {
+ primary_signal.disconnect();
+ }
+
+ primary_connection = value;
+ auto network = get_connection(value);
+ primary_connection_obj = network;
+
+ /* Any change inside the primary connection also called default_changed */
+ primary_signal = network->signal_network_altered().connect(
+ [this, network] ()
+ {
+ default_changed.emit(network);
+ });
+ /* Tell clients */
+ default_changed.emit(network);
+ }
+}
+
+std::shared_ptr NetworkManager::get_connection(std::string path)
+{
+ auto connection_proxy = Gio::DBus::Proxy::create_sync(connection,
+ NM_DBUS_NAME,
+ path,
+ "org.freedesktop.NetworkManager.Connection.Active");
+
+ std::vector> list;
+ Glib::Variant> paths;
+ connection_proxy->get_cached_property(paths, "Devices");
+ if (paths)
+ {
+ for (auto & it : paths.get())
+ {
+ list.push_back(all_devices[it]);
+ }
+ }
+
+ return std::make_shared(path, connection_proxy, list);
+}
+
+void NetworkManager::on_nm_signal(const Glib::ustring& sender, const Glib::ustring& signal,
+ const Glib::VariantContainerBase& container)
+{
+ if (signal == "DeviceAdded")
+ {
+ auto val = Glib::VariantBase::cast_dynamic>(container.get_child()).get();
+ add_network(val);
+ } else if (signal == "DeviceRemoved")
+ {
+ auto val = Glib::VariantBase::cast_dynamic>(container.get_child()).get();
+ all_devices.erase(val);
+ } else
+ {
+ return;
+ }
+
+ /* NM list changed, but let's not send instantly */
+ if (debounce)
+ {
+ debounce.disconnect();
+ }
+
+ debounce = Glib::signal_timeout().connect([this] ()
+ {
+ signal_device_list_changed().emit(all_devices);
+ return G_SOURCE_REMOVE;
+ }, 100);
+}
+
+std::shared_ptr NetworkManager::get_primary_network()
+{
+ return primary_connection_obj;
+}
+
+void NetworkManager::activate_connection(std::string p1, std::string p2, std::string p3)
+{
+ Glib::VariantStringBase path1, path2, path3;
+ Glib::VariantStringBase::create_object_path(path1, p1);
+ Glib::VariantStringBase::create_object_path(path2, p2);
+ Glib::VariantStringBase::create_object_path(path3, p3);
+ auto paths = Glib::VariantContainerBase::create_tuple({path1, path2, path3});
+ // auto data = Glib::VariantContainerBase::create_tuple(paths);
+
+ try {
+ nm_proxy->call_sync("ActivateConnection", paths);
+ } catch (...)
+ {
+ /* It's most likely a WIFI AP with no password set. Let's ask */
+ std::cout << p2 << std::endl;
+ if (p2.find("/Devices/") != std::string::npos)
+ {
+ auto device = std::dynamic_pointer_cast(all_devices[p2]);
+ if (device)
+ {
+ popup_cache_p2 = p2;
+ popup_cache_p3 = p3;
+ auto ap = device->get_access_points()[p3];
+ popup_label.set_label("Preshared Key required for Access Point '" + ap->get_ssid() + "'");
+ popup_window.present();
+ popup_window.get_focus();
+ }
+ }
+ }
+}
+
+void NetworkManager::deactivate_connection(std::string p1)
+{
+ auto manager = NetworkManager::getInstance()->get_nm_proxy();
+ Glib::VariantStringBase path1;
+ Glib::VariantStringBase::create_object_path(path1, p1);
+ auto paths = Glib::VariantContainerBase::create_tuple({path1});
+ // auto data = Glib::VariantContainerBase::create_tuple(paths);
+
+ try {
+ manager->call_sync("DeactivateConnection", paths);
+ } catch (...)
+ {}
+}
+
+/* Is Wifi Enabled, in software and in rfkill */
+std::tuple NetworkManager::wifi_global_enabled()
+{
+ if (!nm_proxy)
+ {
+ return {false, false};
+ }
+
+ Glib::Variant wifisoft, wifihard;
+ nm_proxy->get_cached_property(wifisoft, "WirelessEnabled");
+ nm_proxy->get_cached_property(wifihard, "WirelessHardwareEnabled");
+ return {wifisoft.get(), wifihard.get()};
+}
+
+/* Is Mobile Data Enabled, in software and in rfkill */
+std::tuple NetworkManager::mobile_global_enabled()
+{
+ if (!nm_proxy)
+ {
+ return {false, false};
+ }
+
+ Glib::Variant modemsoft, modemhard;
+ nm_proxy->get_cached_property(modemsoft, "WwanEnabled");
+ nm_proxy->get_cached_property(modemhard, "WwanHardwareEnabled");
+ return {modemsoft.get(), modemhard.get()};
+}
+
+/* Is Networking enabled */
+bool NetworkManager::networking_global_enabled()
+{
+ if (!nm_proxy)
+ {
+ return false;
+ }
+
+ Glib::Variant enabled;
+ nm_proxy->get_cached_property(enabled, "NetworkingEnabled");
+ return enabled.get();
+}
+
+void NetworkManager::wifi_global_set(bool value)
+{
+ auto another_proxy = Gio::DBus::Proxy::create_sync(nm_proxy->get_connection(),
+ NM_DBUS_NAME,
+ NM_PATH,
+ DBUS_PROPERTIES_INTERFACE);
+
+ auto params = Glib::VariantContainerBase::create_tuple(
+ {
+ Glib::Variant::create(NM_INTERFACE),
+ Glib::Variant::create("WirelessEnabled"),
+ Glib::Variant::create(Glib::Variant::create(value))
+ });
+ another_proxy->call_sync("Set", params);
+}
+
+void NetworkManager::mobile_global_set(bool value)
+{
+ auto another_proxy = Gio::DBus::Proxy::create_sync(nm_proxy->get_connection(),
+ NM_DBUS_NAME,
+ NM_PATH,
+ DBUS_PROPERTIES_INTERFACE);
+
+ auto params = Glib::VariantContainerBase::create_tuple(
+ {
+ Glib::Variant::create(NM_INTERFACE),
+ Glib::Variant::create("WwanEnabled"),
+
+ Glib::Variant::create(Glib::Variant::create(value))
+ });
+ another_proxy->call_sync("Set", params);
+}
+
+std::shared_ptr NetworkManager::get_setting_for_ssid(std::string ssid)
+{
+ if (ssid == "")
+ {
+ return nullptr;
+ }
+
+ for (auto setting : all_settings)
+ {
+ if (setting.second->get_ssid() == ssid)
+ {
+ return setting.second;
+ }
+ }
+
+ return nullptr;
+}
+
+std::shared_ptr NetworkManager::get_vpn(std::string path)
+{
+ return all_vpns[path];
+}
+
+void NetworkManager::submit_password()
+{
+ auto password = popup_entry.get_text();
+ if (password.length() == 0)
+ {
+ return;
+ }
+
+ popup_entry.set_text("");
+ auto wifi = std::dynamic_pointer_cast(all_devices[popup_cache_p2]);
+ if (!wifi)
+ {
+ return;
+ }
+
+ auto ap = wifi->get_access_points()[popup_cache_p3];
+ if (!ap)
+ {
+ return;
+ }
+
+ auto ssid = ap->get_ssid();
+ if (ssid.length() == 0)
+ {
+ return;
+ }
+
+ popup_window.hide();
+
+ // --- Build settings using glibmm (correct types) ---
+
+ // SSID as byte array 'ay'
+ std::vector ssid_bytes(ssid.begin(), ssid.end());
+ auto ssid_variant = Glib::Variant>::create(ssid_bytes);
+
+ // UUID
+ gchar *uuid_c = g_uuid_string_random();
+ Glib::ustring uuid(uuid_c);
+ g_free(uuid_c);
+
+ // ----- connection (a{sv})
+ std::map connection_map;
+ connection_map["type"] =
+ Glib::Variant::create("802-11-wireless");
+ connection_map["id"] = Glib::Variant::create(ssid);
+ connection_map["uuid"] = Glib::Variant::create(uuid);
+
+ // ----- 802-11-wireless (a{sv})
+ std::map wifi_map;
+ wifi_map["ssid"] = ssid_variant;
+ wifi_map["mode"] = Glib::Variant::create("infrastructure");
+
+ // ----- 802-11-wireless-security (a{sv})
+ std::map sec_map;
+ bool use_security = !password.empty();
+ if (use_security)
+ {
+ sec_map["key-mgmt"] = Glib::Variant::create("wpa-psk");
+ sec_map["psk"] = Glib::Variant::create(password);
+ }
+
+ // ------------------------
+ // TOP-LEVEL SETTINGS (a{sa{sv}})
+ // ------------------------
+ std::map>
+ settings_map;
+
+ settings_map["connection"] = connection_map;
+ settings_map["802-11-wireless"] = wifi_map;
+ if (use_security)
+ {
+ settings_map["802-11-wireless-security"] = sec_map;
+ }
+
+ auto settings = Glib::Variant<
+ std::map>>::
+ create(settings_map);
+ // ------------------------
+ // Object paths (o)
+ // ------------------------
+ auto device_path =
+ Glib::Variant::create(popup_cache_p2);
+ // Access point path is "/" → NM autoselects AP matching SSID
+ auto ap_path = Glib::Variant::create("/");
+ // ------------------------
+ // FINAL TUPLE (a{sa{sv}}, o, o)
+ // ------------------------
+ std::vector args_vec = {settings, device_path, ap_path};
+ auto args = Glib::VariantContainerBase::create_tuple(args_vec);
+
+ // ------------------------
+ // CALL NetworkManager
+ // ------------------------
+ nm_proxy->call_sync("AddAndActivateConnection", args);
+}
+
+void NetworkManager::networking_global_set(bool value)
+{
+ auto params = Glib::VariantContainerBase::create_tuple(Glib::Variant::create(value));
+ nm_proxy->call_sync("Enable", params);
+}
+
+NetworkManager::~NetworkManager()
+{
+ lost_nm();
+ /* Signals that outlast each NetworkManager start/stop but need to clear on widget reload */
+ for (auto signal : our_signals)
+ {
+ signal.disconnect();
+ }
+}
diff --git a/src/util/network/manager.hpp b/src/util/network/manager.hpp
new file mode 100644
index 00000000..17e4c21b
--- /dev/null
+++ b/src/util/network/manager.hpp
@@ -0,0 +1,182 @@
+#pragma once
+#include
+#include
+#include
+#include
+#include