From 731a352b41f8fe7b65351be24fdeee900d7529ef Mon Sep 17 00:00:00 2001 From: Alexis Date: Thu, 9 Aug 2018 16:38:24 +0200 Subject: [PATCH] feat: add network module --- include/factory.hpp | 1 + include/modules/network.hpp | 49 ++++++++++++++ meson.build | 4 ++ resources/config | 6 +- resources/style.css | 6 +- src/factory.cpp | 2 + src/modules/network.cpp | 123 ++++++++++++++++++++++++++++++++++++ 7 files changed, 189 insertions(+), 2 deletions(-) create mode 100644 include/modules/network.hpp create mode 100644 src/modules/network.cpp diff --git a/include/factory.hpp b/include/factory.hpp index d1ef5b28..261cc751 100644 --- a/include/factory.hpp +++ b/include/factory.hpp @@ -6,6 +6,7 @@ #include "modules/battery.hpp" #include "modules/memory.hpp" #include "modules/cpu.hpp" +#include "modules/network.hpp" namespace waybar { diff --git a/include/modules/network.hpp b/include/modules/network.hpp new file mode 100644 index 00000000..afb01398 --- /dev/null +++ b/include/modules/network.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include +#include +#include +#include +#include +#include // TODO +#include +#include +#include +#include +#include "util/chrono.hpp" +#include "IModule.hpp" + +namespace waybar::modules { + + class Network : public IModule { + public: + Network(Json::Value config); + auto update() -> void; + operator Gtk::Widget &(); + typedef struct { + int flags; + char essid[IW_ESSID_MAX_SIZE + 1]; + uint8_t bssid[ETH_ALEN]; + int quality; + int quality_max; + int quality_average; + int signal_level; + int signal_level_max; + int noise_level; + int noise_level_max; + int bitrate; + double frequency; + } wireless_info_t; + private: + void _parseEssid(struct nlattr** bss); + bool _associatedOrJoined(struct nlattr **bss); + static int _scanCb(struct nl_msg *msg, void *data); + auto _getInfo() -> void; + Gtk::Label _label; + waybar::util::SleeperThread _thread; + Json::Value _config; + std::size_t _ifid; + std::string _essid; + }; + +} diff --git a/meson.build b/meson.build index 17dbf7fa..c37123c9 100644 --- a/meson.build +++ b/meson.build @@ -26,6 +26,8 @@ wlroots = dependency('wlroots', fallback: ['wlroots', 'wlroots']) gtkmm = dependency('gtkmm-3.0') jsoncpp = dependency('jsoncpp') sigcpp = dependency('sigc++-2.0') +libnl = dependency('libnl-3.0') +libnlgen = dependency('libnl-genl-3.0') subdir('protocol') @@ -43,6 +45,8 @@ executable( libinput, wayland_cursor, gtkmm, + libnl, + libnlgen, ], include_directories: [include_directories('include')], install: true, diff --git a/resources/config b/resources/config index 614726a2..13a19d21 100644 --- a/resources/config +++ b/resources/config @@ -1,6 +1,6 @@ { "modules-left": ["workspaces"], - "modules-right": ["cpu", "memory", "battery", "clock"], + "modules-right": ["network", "cpu", "memory", "battery", "clock"], "cpu": { "format": "{}% " }, @@ -9,5 +9,9 @@ }, "battery": { "format": "{}% " + }, + "network": { + "interface": "wlp2s0", + "format": "{} " } } diff --git a/resources/style.css b/resources/style.css index fae6d3a5..b7a22e11 100644 --- a/resources/style.css +++ b/resources/style.css @@ -23,7 +23,7 @@ window { border-bottom: 3px solid white; } -.clock, .battery, .cpu, .memory { +.clock, .battery, .cpu, .memory, .network { padding: 0 10px; margin: 0 5px; } @@ -50,3 +50,7 @@ window { .memory { background: #9b59b6; } + +.network { + background: #2980b9; +} diff --git a/src/factory.cpp b/src/factory.cpp index 2955332a..82cc951b 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -16,5 +16,7 @@ waybar::IModule &waybar::Factory::makeModule(std::string name) return *new waybar::modules::Cpu(_config[name]); if (name == "clock") return *new waybar::modules::Clock(_config[name]); + if (name == "network") + return *new waybar::modules::Network(_config[name]); throw std::runtime_error("Unknown module: " + name); } diff --git a/src/modules/network.cpp b/src/modules/network.cpp new file mode 100644 index 00000000..7cae9098 --- /dev/null +++ b/src/modules/network.cpp @@ -0,0 +1,123 @@ +#include "modules/network.hpp" + +#include + +waybar::modules::Network::Network(Json::Value config) + : _config(config), _ifid(if_nametoindex(config["interface"].asString().c_str())) +{ + _label.get_style_context()->add_class("network"); + _thread = [this] { + update(); + _thread.sleep_for(chrono::minutes(1)); + }; +}; + +auto waybar::modules::Network::update() -> void +{ + _getInfo(); + auto format = _config["format"] ? _config["format"].asString() : "{}"; + _label.set_text(fmt::format(format, _essid)); +} + +int waybar::modules::Network::_scanCb(struct nl_msg *msg, void *data) { + auto net = static_cast(data); + auto gnlh = static_cast(nlmsg_data(nlmsg_hdr(msg))); + struct nlattr* tb[NL80211_ATTR_MAX + 1]; + struct nlattr* bss[NL80211_BSS_MAX + 1]; + struct nla_policy bss_policy[NL80211_BSS_MAX + 1]{}; + bss_policy[NL80211_BSS_TSF].type = NLA_U64; + bss_policy[NL80211_BSS_FREQUENCY].type = NLA_U32; + bss_policy[NL80211_BSS_BSSID].type = NLA_UNSPEC; + bss_policy[NL80211_BSS_BEACON_INTERVAL].type = NLA_U16; + bss_policy[NL80211_BSS_CAPABILITY].type = NLA_U16; + bss_policy[NL80211_BSS_INFORMATION_ELEMENTS].type = NLA_UNSPEC; + bss_policy[NL80211_BSS_SIGNAL_MBM].type = NLA_U32; + bss_policy[NL80211_BSS_SIGNAL_UNSPEC].type = NLA_U8; + bss_policy[NL80211_BSS_STATUS].type = NLA_U32; + + if (nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), nullptr) < 0) + return NL_SKIP; + if (!tb[NL80211_ATTR_BSS]) + return NL_SKIP; + if (nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS], bss_policy)) + return NL_SKIP; + if (!net->_associatedOrJoined(bss)) + return NL_SKIP; + net->_parseEssid(bss); + // TODO: parse signal + return NL_SKIP; +} + +void waybar::modules::Network::_parseEssid(struct nlattr **bss) +{ + _essid.clear(); + if (bss[NL80211_BSS_INFORMATION_ELEMENTS] != nullptr) { + auto ies = + static_cast(nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS])); + auto ies_len = nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]); + const auto hdr_len = 2; + while (ies_len > hdr_len && ies[0] != 0) { + ies_len -= ies[1] + hdr_len; + ies += ies[1] + hdr_len; + } + if (ies_len > hdr_len && ies_len > ies[1] + hdr_len) { + auto essid_begin = ies + hdr_len; + auto essid_end = essid_begin + ies[1]; + // Only use printable characters of the current locale + std::copy_if(essid_begin, essid_end, std::back_inserter(_essid), + [](char c) { return isprint(static_cast(c)); }); + } + } +} + +bool waybar::modules::Network::_associatedOrJoined(struct nlattr** bss) +{ + if (!bss[NL80211_BSS_STATUS]) + return false; + auto status = nla_get_u32(bss[NL80211_BSS_STATUS]); + switch (status) { + case NL80211_BSS_STATUS_ASSOCIATED: + case NL80211_BSS_STATUS_IBSS_JOINED: + case NL80211_BSS_STATUS_AUTHENTICATED: + return true; + default: + return false; + } + } + +auto waybar::modules::Network::_getInfo() -> void +{ + if (_ifid == 0) + return; + struct nl_sock *sk = nl_socket_alloc(); + if (genl_connect(sk) != 0) { + nl_socket_free(sk); + return; + } + if (nl_socket_modify_cb(sk, NL_CB_VALID, NL_CB_CUSTOM, _scanCb, this) < 0) { + nl_socket_free(sk); + return; + } + const int nl80211_id = genl_ctrl_resolve(sk, "nl80211"); + if (nl80211_id < 0) { + nl_socket_free(sk); + return; + } + struct nl_msg *msg = nlmsg_alloc(); + if (!msg) { + nl_socket_free(sk); + return; + } + if (!genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, nl80211_id, 0, + NLM_F_DUMP, NL80211_CMD_GET_SCAN, 0) || + nla_put_u32(msg, NL80211_ATTR_IFINDEX, _ifid) < 0) { + nlmsg_free(msg); + return; + } + nl_send_sync(sk, msg); + nl_socket_free(sk); +} + +waybar::modules::Network::operator Gtk::Widget &() { + return _label; +}