From dbc06abf1826a3c5752a39f1843144a0cc2a87a1 Mon Sep 17 00:00:00 2001 From: Anthony PERARD Date: Sat, 15 May 2021 16:38:00 +0100 Subject: [PATCH 01/14] network: Initialise cidr_ like clearIface() does --- src/modules/network.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/network.cpp b/src/modules/network.cpp index a8aaffaf..8dcfb572 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -86,7 +86,7 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf family_(config["family"] == "ipv6" ? AF_INET6 : AF_INET), efd_(-1), ev_fd_(-1), - cidr_(-1), + cidr_(0), signal_strength_dbm_(0), signal_strength_(0), #ifdef WANT_RFKILL From 9357a6cb8802b3d57a6c140a3d9a9a2068403089 Mon Sep 17 00:00:00 2001 From: Anthony PERARD Date: Sat, 15 May 2021 16:38:00 +0100 Subject: [PATCH 02/14] network: Start the module with some text in the label_ Fix modules starting with no text, but not hidding. Start with some "text" in the module's label_, update() will then update it. Since the text should be different, update() will be able to show or hide the event_box_. This is to work around the case where the module start with no text, but the the event_box_ is shown. --- src/modules/network.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/modules/network.cpp b/src/modules/network.cpp index 8dcfb572..e52ed027 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -93,6 +93,13 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf rfkill_{RFKILL_TYPE_WLAN}, #endif frequency_(0) { + + // Start with some "text" in the module's label_, update() will then + // update it. Since the text should be different, update() will be able + // to show or hide the event_box_. This is to work around the case where + // the module start with no text, but the the event_box_ is shown. + label_.set_markup(""); + auto down_octets = read_netstat(BANDWIDTH_CATEGORY, BANDWIDTH_DOWN_TOTAL_KEY); auto up_octets = read_netstat(BANDWIDTH_CATEGORY, BANDWIDTH_UP_TOTAL_KEY); if (down_octets) { From 63fdf66ad66914354d4ac510f08d51dd25389c36 Mon Sep 17 00:00:00 2001 From: Anthony PERARD Date: Sat, 15 May 2021 16:38:00 +0100 Subject: [PATCH 03/14] network: Read all available messages on ev_sock_ When more than one message is available to read on the ev_sock_ socket, only the first one is read. Make some changes to be able to read all the messages available by setting the socket to non-blocking. This way we can detect when there's nothing left to read and loop back to wait with epoll. --- src/modules/network.cpp | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/modules/network.cpp b/src/modules/network.cpp index e52ed027..bd84c1a6 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -160,6 +160,9 @@ void waybar::modules::Network::createEventSocket() { if (nl_connect(ev_sock_, NETLINK_ROUTE) != 0) { throw std::runtime_error("Can't connect network socket"); } + if (nl_socket_set_nonblocking(ev_sock_)) { + throw std::runtime_error("Can't set non-blocking on network socket"); + } nl_socket_add_membership(ev_sock_, RTNLGRP_LINK); if (family_ == AF_INET) { nl_socket_add_membership(ev_sock_, RTNLGRP_IPV4_IFADDR); @@ -235,7 +238,23 @@ void waybar::modules::Network::worker() { int ec = epoll_wait(efd_, events.data(), EPOLL_MAX, -1); if (ec > 0) { for (auto i = 0; i < ec; i++) { - if (events[i].data.fd != nl_socket_get_fd(ev_sock_) || nl_recvmsgs_default(ev_sock_) < 0) { + if (events[i].data.fd == nl_socket_get_fd(ev_sock_)) { + int rc = 0; + // Read as many message as possible, until the socket blocks + while (true) { + errno = 0; + rc = nl_recvmsgs_default(ev_sock_); + if (rc == -NLE_AGAIN || errno == EAGAIN) { + rc = 0; + break; + } + } + if (rc < 0) { + spdlog::error("nl_recvmsgs_default error: {}", nl_geterror(-rc)); + thread_.stop(); + break; + } + } else { thread_.stop(); break; } From c9bbaa7241bb40f06bed523c54faa9a8643f7ec2 Mon Sep 17 00:00:00 2001 From: Anthony PERARD Date: Sat, 15 May 2021 16:38:00 +0100 Subject: [PATCH 04/14] network: Rework initial interface search by using a dump Instead of using an alternative way to list all links in order to choose one when an "interface" is in the configuration, we can ask for a dump of all interface an reuse the handleEvents() function. This patch also start to rework the handleEvents() function to grab more information out of each event, like the interface name. --- include/modules/network.hpp | 6 ++ src/modules/network.cpp | 130 ++++++++++++++++++++++++------------ 2 files changed, 95 insertions(+), 41 deletions(-) diff --git a/include/modules/network.hpp b/include/modules/network.hpp index 539f4583..2d3a1ff1 100644 --- a/include/modules/network.hpp +++ b/include/modules/network.hpp @@ -28,8 +28,11 @@ class Network : public ALabel { static const uint8_t EPOLL_MAX = 200; static int handleEvents(struct nl_msg*, void*); + static int handleEventsDone(struct nl_msg*, void*); static int handleScan(struct nl_msg*, void*); + void askForStateDump(void); + void worker(); void createInfoSocket(); void createEventSocket(); @@ -59,6 +62,9 @@ class Network : public ALabel { int nl80211_id_; std::mutex mutex_; + bool want_link_dump_; + bool dump_in_progress_; + unsigned long long bandwidth_down_total_; unsigned long long bandwidth_up_total_; diff --git a/src/modules/network.cpp b/src/modules/network.cpp index bd84c1a6..65d82b62 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -86,6 +86,8 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf family_(config["family"] == "ipv6" ? AF_INET6 : AF_INET), efd_(-1), ev_fd_(-1), + want_link_dump_(false), + dump_in_progress_(false), cidr_(0), signal_strength_dbm_(0), signal_strength_(0), @@ -114,6 +116,11 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf bandwidth_up_total_ = 0; } + if (config_["interface"].isString()) { + // Look for an interface that match "interface" + want_link_dump_ = true; + } + createEventSocket(); createInfoSocket(); auto default_iface = getPreferredIface(-1, false); @@ -125,6 +132,10 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf getInterfaceAddress(); } dp.emit(); + // Ask for a dump of interfaces and then addresses to populate our + // information. First the interface dump, and once done, the callback + // will be called again which will ask for addresses dump. + askForStateDump(); worker(); } @@ -155,6 +166,7 @@ void waybar::modules::Network::createEventSocket() { ev_sock_ = nl_socket_alloc(); nl_socket_disable_seq_check(ev_sock_); nl_socket_modify_cb(ev_sock_, NL_CB_VALID, NL_CB_CUSTOM, handleEvents, this); + nl_socket_modify_cb(ev_sock_, NL_CB_FINISH, NL_CB_CUSTOM, handleEventsDone, this); auto groups = RTMGRP_LINK | (family_ == AF_INET ? RTMGRP_IPV4_IFADDR : RTMGRP_IPV6_IFADDR); nl_join_groups(ev_sock_, groups); // Deprecated if (nl_connect(ev_sock_, NETLINK_ROUTE) != 0) { @@ -590,28 +602,8 @@ bool waybar::modules::Network::checkInterface(struct ifinfomsg *rtif, std::strin int waybar::modules::Network::getPreferredIface(int skip_idx, bool wait) const { int ifid = -1; if (config_["interface"].isString()) { - ifid = if_nametoindex(config_["interface"].asCString()); - if (ifid > 0) { - return ifid; - } else { - // Try with wildcard - struct ifaddrs *ifaddr, *ifa; - int success = getifaddrs(&ifaddr); - if (success != 0) { - return -1; - } - ifa = ifaddr; - ifid = -1; - while (ifa != nullptr) { - if (wildcardMatch(config_["interface"].asString(), ifa->ifa_name)) { - ifid = if_nametoindex(ifa->ifa_name); - break; - } - ifa = ifa->ifa_next; - } - freeifaddrs(ifaddr); - return ifid; - } + // Using link dump instead + return -1; } // getExternalInterface may need some delay to detect external interface for (uint8_t tries = 0; tries < MAX_RETRY; tries += 1) { @@ -656,6 +648,55 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { std::lock_guard lock(net->mutex_); auto nh = nlmsg_hdr(msg); auto ifi = static_cast(NLMSG_DATA(nh)); + bool is_del_event = false; + + switch (nh->nlmsg_type) { + case RTM_DELLINK: + is_del_event = true; + case RTM_NEWLINK: { + struct ifinfomsg *ifi = static_cast(NLMSG_DATA(nh)); + ssize_t attrlen = IFLA_PAYLOAD(nh); + struct rtattr *ifla = IFLA_RTA(ifi); + const char *ifname = NULL; + size_t ifname_len = 0; + + if (net->ifid_ != -1 && ifi->ifi_index != net->ifid_) { + return NL_OK; + } + + for (; RTA_OK(ifla, attrlen); ifla = RTA_NEXT(ifla, attrlen)) { + switch (ifla->rta_type) { + case IFLA_IFNAME: + ifname = static_cast(RTA_DATA(ifla)); + ifname_len = RTA_PAYLOAD(ifla) - 1; // minus \0 + break; + } + } + + if (!is_del_event && net->ifid_ == -1) { + // Checking if it's an interface we care about. + std::string new_ifname (ifname, ifname_len); + if (net->checkInterface(ifi, new_ifname)) { + spdlog::debug("network: selecting new interface {}/{}", new_ifname, ifi->ifi_index); + + net->ifname_ = new_ifname; + net->ifid_ = ifi->ifi_index; + net->getInterfaceAddress(); + net->thread_timer_.wake_up(); + } + } else if (is_del_event && net->ifid_ >= 0) { + // Our interface has been deleted, start looking/waiting for one we care. + spdlog::debug("network: interface {}/{} deleted", net->ifname_, net->ifid_); + + net->clearIface(); + // Check for a new interface and get info + net->checkNewInterface(ifi); + net->dp.emit(); + } + return NL_OK; + } + } + if (nh->nlmsg_type == RTM_DELADDR) { // Check for valid interface if (ifi->ifi_index == net->ifid_) { @@ -671,25 +712,6 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { } return NL_OK; } - } else if (nh->nlmsg_type == RTM_NEWLINK || nh->nlmsg_type == RTM_DELLINK) { - char ifname[IF_NAMESIZE]; - if_indextoname(ifi->ifi_index, ifname); - // Check for valid interface - if (ifi->ifi_index != net->ifid_ && net->checkInterface(ifi, ifname)) { - net->ifname_ = ifname; - net->ifid_ = ifi->ifi_index; - // Get Iface and WIFI info - net->getInterfaceAddress(); - net->thread_timer_.wake_up(); - return NL_OK; - } else if (ifi->ifi_index == net->ifid_ && - (!(ifi->ifi_flags & IFF_RUNNING) || !(ifi->ifi_flags & IFF_UP) || - !net->checkInterface(ifi, ifname))) { - net->clearIface(); - // Check for a new interface and get info - net->checkNewInterface(ifi); - return NL_OK; - } } else { char ifname[IF_NAMESIZE]; if_indextoname(ifi->ifi_index, ifname); @@ -713,6 +735,32 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { return NL_SKIP; } +void waybar::modules::Network::askForStateDump(void) { + /* We need to wait until the current dump is done before sending new + * messages. handleEventsDone() is called when a dump is done. */ + if (dump_in_progress_) + return; + + struct rtgenmsg rt_hdr = { + .rtgen_family = AF_UNSPEC, + }; + + if (want_link_dump_) { + nl_send_simple(ev_sock_, RTM_GETLINK, NLM_F_DUMP, + &rt_hdr, sizeof (rt_hdr)); + want_link_dump_ = false; + dump_in_progress_ = true; + + } +} + +int waybar::modules::Network::handleEventsDone(struct nl_msg *msg, void *data) { + auto net = static_cast(data); + net->dump_in_progress_ = false; + net->askForStateDump(); + return NL_OK; +} + int waybar::modules::Network::handleScan(struct nl_msg *msg, void *data) { auto net = static_cast(data); auto gnlh = static_cast(nlmsg_data(nlmsg_hdr(msg))); From 0fc7ef6685321e1b7d966c21c508e94e6fbf3692 Mon Sep 17 00:00:00 2001 From: Anthony PERARD Date: Sat, 15 May 2021 16:38:00 +0100 Subject: [PATCH 05/14] network: Rework address lookup to use only events In order to get the IP address of an interface, we can get the information out of NEWADDR events without needed to call getifaddrs(). And when now events are expected, we can requests a dump of all addresses and handle addresses changes the same way via handleEvents() only. --- include/modules/network.hpp | 3 +- src/modules/network.cpp | 176 ++++++++++++++++++------------------ 2 files changed, 87 insertions(+), 92 deletions(-) diff --git a/include/modules/network.hpp b/include/modules/network.hpp index 2d3a1ff1..cc8b3f59 100644 --- a/include/modules/network.hpp +++ b/include/modules/network.hpp @@ -2,7 +2,6 @@ #include #include -#include #include #include #include @@ -37,7 +36,6 @@ class Network : public ALabel { void createInfoSocket(); void createEventSocket(); int getExternalInterface(int skip_idx = -1) const; - void getInterfaceAddress(); int netlinkRequest(void*, uint32_t, uint32_t groups = 0) const; int netlinkResponse(void*, uint32_t, uint32_t groups = 0) const; void parseEssid(struct nlattr**); @@ -63,6 +61,7 @@ class Network : public ALabel { std::mutex mutex_; bool want_link_dump_; + bool want_addr_dump_; bool dump_in_progress_; unsigned long long bandwidth_down_total_; diff --git a/src/modules/network.cpp b/src/modules/network.cpp index 65d82b62..f4cee78b 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -87,6 +87,7 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf efd_(-1), ev_fd_(-1), want_link_dump_(false), + want_addr_dump_(false), dump_in_progress_(false), cidr_(0), signal_strength_dbm_(0), @@ -118,7 +119,9 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf if (config_["interface"].isString()) { // Look for an interface that match "interface" + // and then find the address associated with it. want_link_dump_ = true; + want_addr_dump_ = true; } createEventSocket(); @@ -129,7 +132,7 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf char ifname[IF_NAMESIZE]; if_indextoname(default_iface, ifname); ifname_ = ifname; - getInterfaceAddress(); + want_addr_dump_ = true; } dp.emit(); // Ask for a dump of interfaces and then addresses to populate our @@ -502,55 +505,6 @@ out: return ifidx; } -void waybar::modules::Network::getInterfaceAddress() { - struct ifaddrs *ifaddr, *ifa; - cidr_ = 0; - int success = getifaddrs(&ifaddr); - if (success != 0) { - return; - } - ifa = ifaddr; - while (ifa != nullptr) { - if (ifa->ifa_addr != nullptr && ifa->ifa_addr->sa_family == family_ && - ifa->ifa_name == ifname_) { - char ipaddr[INET6_ADDRSTRLEN]; - char netmask[INET6_ADDRSTRLEN]; - unsigned int cidr = 0; - if (family_ == AF_INET) { - ipaddr_ = inet_ntop(AF_INET, - &reinterpret_cast(ifa->ifa_addr)->sin_addr, - ipaddr, - INET_ADDRSTRLEN); - auto net_addr = reinterpret_cast(ifa->ifa_netmask); - netmask_ = inet_ntop(AF_INET, &net_addr->sin_addr, netmask, INET_ADDRSTRLEN); - unsigned int cidrRaw = net_addr->sin_addr.s_addr; - while (cidrRaw) { - cidr += cidrRaw & 1; - cidrRaw >>= 1; - } - } else { - ipaddr_ = inet_ntop(AF_INET6, - &reinterpret_cast(ifa->ifa_addr)->sin6_addr, - ipaddr, - INET6_ADDRSTRLEN); - auto net_addr = reinterpret_cast(ifa->ifa_netmask); - netmask_ = inet_ntop(AF_INET6, &net_addr->sin6_addr, netmask, INET6_ADDRSTRLEN); - for (size_t i = 0; i < sizeof(net_addr->sin6_addr.s6_addr); ++i) { - unsigned char cidrRaw = net_addr->sin6_addr.s6_addr[i]; - while (cidrRaw) { - cidr += cidrRaw & 1; - cidrRaw >>= 1; - } - } - } - cidr_ = cidr; - break; - } - ifa = ifa->ifa_next; - } - freeifaddrs(ifaddr); -} - int waybar::modules::Network::netlinkRequest(void *req, uint32_t reqlen, uint32_t groups) const { struct sockaddr_nl sa = {}; sa.nl_family = AF_NETLINK; @@ -635,7 +589,8 @@ void waybar::modules::Network::checkNewInterface(struct ifinfomsg *rtif) { char ifname[IF_NAMESIZE]; if_indextoname(new_iface, ifname); ifname_ = ifname; - getInterfaceAddress(); + want_addr_dump_ = true; + askForStateDump(); thread_timer_.wake_up(); } else { ifid_ = -1; @@ -647,7 +602,6 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { auto net = static_cast(data); std::lock_guard lock(net->mutex_); auto nh = nlmsg_hdr(msg); - auto ifi = static_cast(NLMSG_DATA(nh)); bool is_del_event = false; switch (nh->nlmsg_type) { @@ -681,8 +635,12 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { net->ifname_ = new_ifname; net->ifid_ = ifi->ifi_index; - net->getInterfaceAddress(); net->thread_timer_.wake_up(); + /* An address for this new interface should be received via an + * RTM_NEWADDR event either because we ask for a dump of both links + * and addrs, or because this interface has just been created and + * the addr will be sent after the RTM_NEWLINK event. + * So we don't need to do anything. */ } } else if (is_del_event && net->ifid_ >= 0) { // Our interface has been deleted, start looking/waiting for one we care. @@ -693,46 +651,78 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { net->checkNewInterface(ifi); net->dp.emit(); } - return NL_OK; + break; + } + + case RTM_DELADDR: + is_del_event = true; + case RTM_NEWADDR: { + struct ifaddrmsg *ifa = static_cast(NLMSG_DATA(nh)); + ssize_t attrlen = IFA_PAYLOAD(nh); + struct rtattr *ifa_rta = IFA_RTA(ifa); + + if ((int)ifa->ifa_index != net->ifid_) { + return NL_OK; + } + + if (ifa->ifa_family != net->family_) { + return NL_OK; + } + + // We ignore address mark as scope for the link or host, + // which should leave scope global addresses. + if (ifa->ifa_scope >= RT_SCOPE_LINK) { + return NL_OK; + } + + for (; RTA_OK(ifa_rta, attrlen); ifa_rta = RTA_NEXT(ifa_rta, attrlen)) { + switch (ifa_rta->rta_type) { + case IFA_ADDRESS: { + char ipaddr[INET6_ADDRSTRLEN]; + if (!is_del_event) { + net->ipaddr_ = inet_ntop(ifa->ifa_family, RTA_DATA(ifa_rta), + ipaddr, sizeof (ipaddr)); + net->cidr_ = ifa->ifa_prefixlen; + switch (ifa->ifa_family) { + case AF_INET: { + struct in_addr netmask; + netmask.s_addr = htonl(~0 << (32 - ifa->ifa_prefixlen)); + net->netmask_ = inet_ntop(ifa->ifa_family, &netmask, + ipaddr, sizeof (ipaddr)); + } + case AF_INET6: { + struct in6_addr netmask; + for (int i = 0; i < 16; i++) { + int v = (i + 1) * 8 - ifa->ifa_prefixlen; + if (v < 0) v = 0; + if (v > 8) v = 8; + netmask.s6_addr[i] = ~0 << v; + } + net->netmask_ = inet_ntop(ifa->ifa_family, &netmask, + ipaddr, sizeof (ipaddr)); + } + } + spdlog::debug("network: {}, new addr {}/{}", net->ifname_, net->ipaddr_, net->cidr_); + } else { + net->ipaddr_.clear(); + net->cidr_ = 0; + net->netmask_.clear(); + spdlog::debug("network: {} addr deleted {}/{}", + net->ifname_, + inet_ntop(ifa->ifa_family, RTA_DATA(ifa_rta), + ipaddr, sizeof (ipaddr)), + ifa->ifa_prefixlen); + } + net->dp.emit(); + break; + } + } + } + break; } } - if (nh->nlmsg_type == RTM_DELADDR) { - // Check for valid interface - if (ifi->ifi_index == net->ifid_) { - net->ipaddr_.clear(); - net->netmask_.clear(); - net->cidr_ = 0; - if (!(ifi->ifi_flags & IFF_RUNNING)) { - net->clearIface(); - // Check for a new interface and get info - net->checkNewInterface(ifi); - } else { - net->dp.emit(); - } - return NL_OK; - } - } else { - char ifname[IF_NAMESIZE]; - if_indextoname(ifi->ifi_index, ifname); - // Auto detected network can also be assigned here - if (ifi->ifi_index != net->ifid_ && net->checkInterface(ifi, ifname)) { - // If iface is different, clear data - if (ifi->ifi_index != net->ifid_) { - net->clearIface(); - } - net->ifname_ = ifname; - net->ifid_ = ifi->ifi_index; - } - // Check for valid interface - if (ifi->ifi_index == net->ifid_) { - // Get Iface and WIFI info - net->getInterfaceAddress(); - net->thread_timer_.wake_up(); - return NL_OK; - } - } - return NL_SKIP; + return NL_OK; } void waybar::modules::Network::askForStateDump(void) { @@ -751,6 +741,12 @@ void waybar::modules::Network::askForStateDump(void) { want_link_dump_ = false; dump_in_progress_ = true; + } else if (want_addr_dump_) { + rt_hdr.rtgen_family = family_; + nl_send_simple(ev_sock_, RTM_GETADDR, NLM_F_DUMP, + &rt_hdr, sizeof (rt_hdr)); + want_addr_dump_ = false; + dump_in_progress_ = true; } } From 0bb436f9493424d63e66a39e2dc2f5e6e2ada9a3 Mon Sep 17 00:00:00 2001 From: Anthony PERARD Date: Sat, 15 May 2021 16:38:00 +0100 Subject: [PATCH 06/14] network: Rework interface auto detection, handle route change events Last part of the rework of handleEvents(), this time we take the getExternalInterface() function and add it to the handleEvents() function. That way, waybar can react immediately when a new "external interface" is available and doesn't need to probe. Also that avoid to have two different functions consuming from the same socket and we don't need to recode some of the functions that are already available via libnl (to send and receive messages). --- include/modules/network.hpp | 9 +- src/modules/network.cpp | 356 +++++++++++++++--------------------- 2 files changed, 146 insertions(+), 219 deletions(-) diff --git a/include/modules/network.hpp b/include/modules/network.hpp index cc8b3f59..964597ab 100644 --- a/include/modules/network.hpp +++ b/include/modules/network.hpp @@ -3,7 +3,6 @@ #include #include #include -#include #include #include #include @@ -35,17 +34,12 @@ class Network : public ALabel { void worker(); void createInfoSocket(); void createEventSocket(); - int getExternalInterface(int skip_idx = -1) const; - int netlinkRequest(void*, uint32_t, uint32_t groups = 0) const; - int netlinkResponse(void*, uint32_t, uint32_t groups = 0) const; void parseEssid(struct nlattr**); void parseSignal(struct nlattr**); void parseFreq(struct nlattr**); bool associatedOrJoined(struct nlattr**); - bool checkInterface(struct ifinfomsg* rtif, std::string name); - int getPreferredIface(int skip_idx = -1, bool wait = true) const; + bool checkInterface(std::string name); auto getInfo() -> void; - void checkNewInterface(struct ifinfomsg* rtif); const std::string getNetworkState() const; void clearIface(); bool wildcardMatch(const std::string& pattern, const std::string& text) const; @@ -60,6 +54,7 @@ class Network : public ALabel { int nl80211_id_; std::mutex mutex_; + bool want_route_dump_; bool want_link_dump_; bool want_addr_dump_; bool dump_in_progress_; diff --git a/src/modules/network.cpp b/src/modules/network.cpp index f4cee78b..508af910 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -86,6 +86,7 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf family_(config["family"] == "ipv6" ? AF_INET6 : AF_INET), efd_(-1), ev_fd_(-1), + want_route_dump_(false), want_link_dump_(false), want_addr_dump_(false), dump_in_progress_(false), @@ -117,7 +118,11 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf bandwidth_up_total_ = 0; } - if (config_["interface"].isString()) { + if (!config_["interface"].isString()) { + // "interface" isn't configure, then try to guess the external + // interface currently used for internet. + want_route_dump_ = true; + } else { // Look for an interface that match "interface" // and then find the address associated with it. want_link_dump_ = true; @@ -126,14 +131,7 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf createEventSocket(); createInfoSocket(); - auto default_iface = getPreferredIface(-1, false); - if (default_iface != -1) { - ifid_ = default_iface; - char ifname[IF_NAMESIZE]; - if_indextoname(default_iface, ifname); - ifname_ = ifname; - want_addr_dump_ = true; - } + dp.emit(); // Ask for a dump of interfaces and then addresses to populate our // information. First the interface dump, and once done, the callback @@ -184,6 +182,14 @@ void waybar::modules::Network::createEventSocket() { } else { nl_socket_add_membership(ev_sock_, RTNLGRP_IPV6_IFADDR); } + if (!config_["interface"].isString()) { + if (family_ == AF_INET) { + nl_socket_add_membership(ev_sock_, RTNLGRP_IPV4_ROUTE); + } else { + nl_socket_add_membership(ev_sock_, RTNLGRP_IPV6_ROUTE); + } + } + efd_ = epoll_create1(EPOLL_CLOEXEC); if (efd_ < 0) { throw std::runtime_error("Can't create epoll"); @@ -383,196 +389,16 @@ auto waybar::modules::Network::update() -> void { ALabel::update(); } -// Based on https://gist.github.com/Yawning/c70d804d4b8ae78cc698 -int waybar::modules::Network::getExternalInterface(int skip_idx) const { - static const uint32_t route_buffer_size = 8192; - struct nlmsghdr * hdr = nullptr; - struct rtmsg * rt = nullptr; - char resp[route_buffer_size] = {0}; - int ifidx = -1; - - /* Prepare request. */ - constexpr uint32_t reqlen = NLMSG_SPACE(sizeof(*rt)); - char req[reqlen] = {0}; - - /* Build the RTM_GETROUTE request. */ - hdr = reinterpret_cast(req); - hdr->nlmsg_len = NLMSG_LENGTH(sizeof(*rt)); - hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; - hdr->nlmsg_type = RTM_GETROUTE; - rt = static_cast(NLMSG_DATA(hdr)); - rt->rtm_family = family_; - rt->rtm_table = RT_TABLE_MAIN; - - /* Issue the query. */ - if (netlinkRequest(req, reqlen) < 0) { - goto out; - } - - /* Read the response(s). - * - * WARNING: All the packets generated by the request must be consumed (as in, - * consume responses till NLMSG_DONE/NLMSG_ERROR is encountered). - */ - do { - auto len = netlinkResponse(resp, route_buffer_size); - if (len < 0) { - goto out; - } - - /* Parse the response payload into netlink messages. */ - for (hdr = reinterpret_cast(resp); NLMSG_OK(hdr, len); - hdr = NLMSG_NEXT(hdr, len)) { - if (hdr->nlmsg_type == NLMSG_DONE) { - goto out; - } - if (hdr->nlmsg_type == NLMSG_ERROR) { - /* Even if we found the interface index, something is broken with the - * netlink socket, so return an error. - */ - ifidx = -1; - goto out; - } - - /* If we found the correct answer, skip parsing the attributes. */ - if (ifidx != -1) { - continue; - } - - /* Find the message(s) concerting the main routing table, each message - * corresponds to a single routing table entry. - */ - rt = static_cast(NLMSG_DATA(hdr)); - if (rt->rtm_table != RT_TABLE_MAIN) { - continue; - } - - /* Parse all the attributes for a single routing table entry. */ - struct rtattr *attr = RTM_RTA(rt); - uint64_t attrlen = RTM_PAYLOAD(hdr); - bool has_gateway = false; - bool has_destination = false; - int temp_idx = -1; - for (; RTA_OK(attr, attrlen); attr = RTA_NEXT(attr, attrlen)) { - /* Determine if this routing table entry corresponds to the default - * route by seeing if it has a gateway, and if a destination addr is - * set, that it is all 0s. - */ - switch (attr->rta_type) { - case RTA_GATEWAY: - /* The gateway of the route. - * - * If someone every needs to figure out the gateway address as well, - * it's here as the attribute payload. - */ - has_gateway = true; - break; - case RTA_DST: { - /* The destination address. - * Should be either missing, or maybe all 0s. Accept both. - */ - const uint32_t nr_zeroes = (family_ == AF_INET) ? 4 : 16; - unsigned char c = 0; - size_t dstlen = RTA_PAYLOAD(attr); - if (dstlen != nr_zeroes) { - break; - } - for (uint32_t i = 0; i < dstlen; i += 1) { - c |= *((unsigned char *)RTA_DATA(attr) + i); - } - has_destination = (c == 0); - break; - } - case RTA_OIF: - /* The output interface index. */ - temp_idx = *static_cast(RTA_DATA(attr)); - break; - default: - break; - } - } - /* If this is the default route, and we know the interface index, - * we can stop parsing this message. - */ - if (has_gateway && !has_destination && temp_idx != -1 && temp_idx != skip_idx) { - ifidx = temp_idx; - break; - } - } - } while (true); - -out: - return ifidx; -} - -int waybar::modules::Network::netlinkRequest(void *req, uint32_t reqlen, uint32_t groups) const { - struct sockaddr_nl sa = {}; - sa.nl_family = AF_NETLINK; - sa.nl_groups = groups; - struct iovec iov = {req, reqlen}; - struct msghdr msg = { - .msg_name = &sa, - .msg_namelen = sizeof(sa), - .msg_iov = &iov, - .msg_iovlen = 1, - }; - return sendmsg(nl_socket_get_fd(ev_sock_), &msg, 0); -} - -int waybar::modules::Network::netlinkResponse(void *resp, uint32_t resplen, uint32_t groups) const { - struct sockaddr_nl sa = {}; - sa.nl_family = AF_NETLINK; - sa.nl_groups = groups; - struct iovec iov = {resp, resplen}; - struct msghdr msg = { - .msg_name = &sa, - .msg_namelen = sizeof(sa), - .msg_iov = &iov, - .msg_iovlen = 1, - }; - auto ret = recvmsg(nl_socket_get_fd(ev_sock_), &msg, 0); - if (msg.msg_flags & MSG_TRUNC) { - return -1; - } - return ret; -} - -bool waybar::modules::Network::checkInterface(struct ifinfomsg *rtif, std::string name) { +bool waybar::modules::Network::checkInterface(std::string name) { if (config_["interface"].isString()) { return config_["interface"].asString() == name || wildcardMatch(config_["interface"].asString(), name); } - // getExternalInterface may need some delay to detect external interface - for (uint8_t tries = 0; tries < MAX_RETRY; tries += 1) { - auto external_iface = getExternalInterface(); - if (external_iface > 0) { - return external_iface == rtif->ifi_index; - } - std::this_thread::sleep_for(std::chrono::milliseconds(500)); - } return false; } -int waybar::modules::Network::getPreferredIface(int skip_idx, bool wait) const { - int ifid = -1; - if (config_["interface"].isString()) { - // Using link dump instead - return -1; - } - // getExternalInterface may need some delay to detect external interface - for (uint8_t tries = 0; tries < MAX_RETRY; tries += 1) { - ifid = getExternalInterface(skip_idx); - if (ifid > 0) { - return ifid; - } - if (wait) { - std::this_thread::sleep_for(std::chrono::milliseconds(500)); - } - } - return -1; -} - void waybar::modules::Network::clearIface() { + ifid_ = -1; essid_.clear(); ipaddr_.clear(); netmask_.clear(); @@ -582,22 +408,6 @@ void waybar::modules::Network::clearIface() { frequency_ = 0; } -void waybar::modules::Network::checkNewInterface(struct ifinfomsg *rtif) { - auto new_iface = getPreferredIface(rtif->ifi_index); - if (new_iface != -1) { - ifid_ = new_iface; - char ifname[IF_NAMESIZE]; - if_indextoname(new_iface, ifname); - ifname_ = ifname; - want_addr_dump_ = true; - askForStateDump(); - thread_timer_.wake_up(); - } else { - ifid_ = -1; - dp.emit(); - } -} - int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { auto net = static_cast(data); std::lock_guard lock(net->mutex_); @@ -627,10 +437,16 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { } } - if (!is_del_event && net->ifid_ == -1) { + if (!is_del_event && ifi->ifi_index == net->ifid_) { + // Update inferface information + if (net->ifname_.empty()) { + std::string new_ifname (ifname, ifname_len); + net->ifname_ = new_ifname; + } + } else if (!is_del_event && net->ifid_ == -1) { // Checking if it's an interface we care about. std::string new_ifname (ifname, ifname_len); - if (net->checkInterface(ifi, new_ifname)) { + if (net->checkInterface(new_ifname)) { spdlog::debug("network: selecting new interface {}/{}", new_ifname, ifi->ifi_index); net->ifname_ = new_ifname; @@ -647,8 +463,6 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { spdlog::debug("network: interface {}/{} deleted", net->ifname_, net->ifid_); net->clearIface(); - // Check for a new interface and get info - net->checkNewInterface(ifi); net->dp.emit(); } break; @@ -720,6 +534,117 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { } break; } + + case RTM_DELROUTE: + is_del_event = true; + case RTM_NEWROUTE: { + // Based on https://gist.github.com/Yawning/c70d804d4b8ae78cc698 + // to find the interface used to reach the outside world + + struct rtmsg *rtm = static_cast(NLMSG_DATA(nh)); + ssize_t attrlen = RTM_PAYLOAD(nh); + struct rtattr *attr = RTM_RTA(rtm); + bool has_gateway = false; + bool has_destination = false; + int temp_idx = -1; + + /* If we found the correct answer, skip parsing the attributes. */ + if (!is_del_event && net->ifid_ != -1) { + return NL_OK; + } + + /* Find the message(s) concerting the main routing table, each message + * corresponds to a single routing table entry. + */ + if (rtm->rtm_table != RT_TABLE_MAIN) { + return NL_OK; + } + + /* Parse all the attributes for a single routing table entry. */ + for (; RTA_OK(attr, attrlen); attr = RTA_NEXT(attr, attrlen)) { + /* Determine if this routing table entry corresponds to the default + * route by seeing if it has a gateway, and if a destination addr is + * set, that it is all 0s. + */ + switch(attr->rta_type) { + case RTA_GATEWAY: + /* The gateway of the route. + * + * If someone every needs to figure out the gateway address as well, + * it's here as the attribute payload. + */ + has_gateway = true; + break; + case RTA_DST: { + /* The destination address. + * Should be either missing, or maybe all 0s. Accept both. + */ + const uint32_t nr_zeroes = (net->family_ == AF_INET) ? 4 : 16; + unsigned char c = 0; + size_t dstlen = RTA_PAYLOAD(attr); + if (dstlen != nr_zeroes) { + break; + } + for (uint32_t i = 0; i < dstlen; i += 1) { + c |= *((unsigned char *)RTA_DATA(attr) + i); + } + has_destination = (c == 0); + break; + } + case RTA_OIF: + /* The output interface index. */ + temp_idx = *static_cast(RTA_DATA(attr)); + break; + default: + break; + } + + /* If this is the default route, and we know the interface index, + * we can stop parsing this message. + */ + if (has_gateway && !has_destination && temp_idx != -1) { + if (!is_del_event) { + net->ifid_ = temp_idx; + + spdlog::debug("network: new default route via if{}", temp_idx); + + /* Ask ifname associated with temp_idx as well as carrier status */ + struct ifinfomsg ifinfo_hdr = { + .ifi_family = AF_UNSPEC, + .ifi_index = temp_idx, + }; + int err; + err = nl_send_simple(net->ev_sock_, RTM_GETLINK, NLM_F_REQUEST, + &ifinfo_hdr, sizeof (ifinfo_hdr)); + if (err < 0) { + spdlog::error("network: failed to ask link info: {}", err); + /* Ask for a dump of all links instead */ + net->want_link_dump_ = true; + } + + /* Also ask for the address. Asking for a addresses of a specific + * interface doesn't seems to work so ask for a dump of all + * addresses. */ + net->want_addr_dump_ = true; + net->askForStateDump(); + net->thread_timer_.wake_up(); + } else if (is_del_event && temp_idx == net->ifid_) { + spdlog::debug("network: default route deleted {}/if{}", + net->ifname_, temp_idx); + + net->ifname_.clear(); + net->clearIface(); + net->dp.emit(); + /* Ask for a dump of all routes in case another one is already + * setup. If there's none, there'll be an event with new one + * later. */ + net->want_route_dump_ = true; + net->askForStateDump(); + } + } + } + break; + } } return NL_OK; @@ -735,7 +660,14 @@ void waybar::modules::Network::askForStateDump(void) { .rtgen_family = AF_UNSPEC, }; - if (want_link_dump_) { + if (want_route_dump_) { + rt_hdr.rtgen_family = family_; + nl_send_simple(ev_sock_, RTM_GETROUTE, NLM_F_DUMP, + &rt_hdr, sizeof (rt_hdr)); + want_route_dump_ = false; + dump_in_progress_ = true; + + } else if (want_link_dump_) { nl_send_simple(ev_sock_, RTM_GETLINK, NLM_F_DUMP, &rt_hdr, sizeof (rt_hdr)); want_link_dump_ = false; From c1427ff8072aa9573a09941fbaa1c45af5005d89 Mon Sep 17 00:00:00 2001 From: Anthony PERARD Date: Sat, 15 May 2021 16:38:00 +0100 Subject: [PATCH 07/14] network: Handle carrier information IFLA_CARRIER allows to know when a cable is plugged to the Ethernet card or when the WiFi is connected. If there's no carrier, the interface will be considered disconnected. --- include/modules/network.hpp | 1 + src/modules/network.cpp | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/include/modules/network.hpp b/include/modules/network.hpp index 964597ab..2523dc22 100644 --- a/include/modules/network.hpp +++ b/include/modules/network.hpp @@ -64,6 +64,7 @@ class Network : public ALabel { std::string state_; std::string essid_; + bool carrier_; std::string ifname_; std::string ipaddr_; std::string netmask_; diff --git a/src/modules/network.cpp b/src/modules/network.cpp index 508af910..6102a43c 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -292,6 +292,7 @@ const std::string waybar::modules::Network::getNetworkState() const { #endif return "disconnected"; } + if (!carrier_) return "disconnected"; if (ipaddr_.empty()) return "linked"; if (essid_.empty()) return "ethernet"; return "wifi"; @@ -402,6 +403,7 @@ void waybar::modules::Network::clearIface() { essid_.clear(); ipaddr_.clear(); netmask_.clear(); + carrier_ = false; cidr_ = 0; signal_strength_dbm_ = 0; signal_strength_ = 0; @@ -423,6 +425,7 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { struct rtattr *ifla = IFLA_RTA(ifi); const char *ifname = NULL; size_t ifname_len = 0; + bool carrier = false; if (net->ifid_ != -1 && ifi->ifi_index != net->ifid_) { return NL_OK; @@ -434,6 +437,10 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { ifname = static_cast(RTA_DATA(ifla)); ifname_len = RTA_PAYLOAD(ifla) - 1; // minus \0 break; + case IFLA_CARRIER: { + carrier = *(char*)RTA_DATA(ifla) == 1; + break; + } } } @@ -443,6 +450,7 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { std::string new_ifname (ifname, ifname_len); net->ifname_ = new_ifname; } + net->carrier_ = carrier; } else if (!is_del_event && net->ifid_ == -1) { // Checking if it's an interface we care about. std::string new_ifname (ifname, ifname_len); @@ -451,6 +459,7 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { net->ifname_ = new_ifname; net->ifid_ = ifi->ifi_index; + net->carrier_ = carrier; net->thread_timer_.wake_up(); /* An address for this new interface should be received via an * RTM_NEWADDR event either because we ask for a dump of both links From c65ec9e14fa3509c2da67551e14add961f9850ce Mon Sep 17 00:00:00 2001 From: Yonatan Avhar Date: Fri, 21 May 2021 15:54:48 +0300 Subject: [PATCH 08/14] Add options to use a .json extension for the config filename --- src/client.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/client.cpp b/src/client.cpp index 1c48c813..f6330c32 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -207,9 +207,13 @@ std::tuple waybar::Client::getConfigs( const std::string &config, const std::string &style) const { auto config_file = config.empty() ? getValidPath({ "$XDG_CONFIG_HOME/waybar/config", + "$XDG_CONFIG_HOME/waybar/config.json", "$HOME/.config/waybar/config", + "$HOME/.config/waybar/config.json", "$HOME/waybar/config", + "$HOME/waybar/config.json", "/etc/xdg/waybar/config", + "/etc/xdg/waybar/config.json", SYSCONFDIR "/xdg/waybar/config", "./resources/config", }) From 99918205ede41e80fc5e55e0b2582ae3fc613e21 Mon Sep 17 00:00:00 2001 From: Yonatan Avhar Date: Fri, 21 May 2021 17:53:43 +0300 Subject: [PATCH 09/14] Correct .json to .jsonc --- src/client.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index f6330c32..ced9e492 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -207,13 +207,13 @@ std::tuple waybar::Client::getConfigs( const std::string &config, const std::string &style) const { auto config_file = config.empty() ? getValidPath({ "$XDG_CONFIG_HOME/waybar/config", - "$XDG_CONFIG_HOME/waybar/config.json", + "$XDG_CONFIG_HOME/waybar/config.jsonc", "$HOME/.config/waybar/config", - "$HOME/.config/waybar/config.json", + "$HOME/.config/waybar/config.jsonc", "$HOME/waybar/config", - "$HOME/waybar/config.json", + "$HOME/waybar/config.jsonc", "/etc/xdg/waybar/config", - "/etc/xdg/waybar/config.json", + "/etc/xdg/waybar/config.jsonc", SYSCONFDIR "/xdg/waybar/config", "./resources/config", }) From 729553d3bc8eeb3c7a5370692174d9962d242e9b Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Sun, 17 Jan 2021 01:05:18 -0800 Subject: [PATCH 10/14] feat(bar): add config flag for pointer event passthrough --- include/bar.hpp | 1 + src/bar.cpp | 43 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/include/bar.hpp b/include/bar.hpp index d6cd895f..6bf8c526 100644 --- a/include/bar.hpp +++ b/include/bar.hpp @@ -44,6 +44,7 @@ class BarSurface { virtual void setExclusiveZone(bool enable) = 0; virtual void setLayer(bar_layer layer) = 0; virtual void setMargins(const struct bar_margins &margins) = 0; + virtual void setPassThrough(bool enable) = 0; virtual void setPosition(const std::string_view &position) = 0; virtual void setSize(uint32_t width, uint32_t height) = 0; virtual void commit(){}; diff --git a/src/bar.cpp b/src/bar.cpp index 1dbd69a0..3361ebf1 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -33,6 +33,7 @@ struct GLSSurfaceImpl : public BarSurface, public sigc::trackable { gtk_layer_set_monitor(window_.gobj(), output.monitor->gobj()); gtk_layer_set_namespace(window_.gobj(), "waybar"); + window.signal_map_event().connect_notify(sigc::mem_fun(*this, &GLSSurfaceImpl::onMap)); window.signal_configure_event().connect_notify( sigc::mem_fun(*this, &GLSSurfaceImpl::onConfigure)); } @@ -62,6 +63,18 @@ struct GLSSurfaceImpl : public BarSurface, public sigc::trackable { gtk_layer_set_layer(window_.gobj(), layer); } + void setPassThrough(bool enable) override { + passthrough_ = enable; + auto gdk_window = window_.get_window(); + if (gdk_window) { + Cairo::RefPtr region; + if (enable) { + region = Cairo::Region::create(); + } + gdk_window->input_shape_combine_region(region, 0, 0); + } + } + void setPosition(const std::string_view& position) override { auto unanchored = GTK_LAYER_SHELL_EDGE_BOTTOM; vertical_ = false; @@ -93,8 +106,11 @@ struct GLSSurfaceImpl : public BarSurface, public sigc::trackable { std::string output_name_; uint32_t width_; uint32_t height_; + bool passthrough_ = false; bool vertical_ = false; + void onMap(GdkEventAny* ev) { setPassThrough(passthrough_); } + void onConfigure(GdkEventConfigure* ev) { /* * GTK wants new size for the window. @@ -182,6 +198,20 @@ struct RawSurfaceImpl : public BarSurface, public sigc::trackable { } } + void setPassThrough(bool enable) override { + passthrough_ = enable; + /* GTK overwrites any region changes applied directly to the wl_surface, + * thus the same GTK region API as in the GLS impl has to be used. */ + auto gdk_window = window_.get_window(); + if (gdk_window) { + Cairo::RefPtr region; + if (enable) { + region = Cairo::Region::create(); + } + gdk_window->input_shape_combine_region(region, 0, 0); + } + } + void setPosition(const std::string_view& position) override { anchor_ = HORIZONTAL_ANCHOR | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP; if (position == "bottom") { @@ -230,6 +260,7 @@ struct RawSurfaceImpl : public BarSurface, public sigc::trackable { uint32_t height_ = 0; uint8_t anchor_ = HORIZONTAL_ANCHOR | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP; bool exclusive_zone_ = true; + bool passthrough_ = false; struct bar_margins margins_; zwlr_layer_shell_v1_layer layer_ = ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM; @@ -262,6 +293,7 @@ struct RawSurfaceImpl : public BarSurface, public sigc::trackable { setSurfaceSize(width_, height_); setExclusiveZone(exclusive_zone_); + setPassThrough(passthrough_); commit(); wl_display_roundtrip(client->wl_display); @@ -377,6 +409,14 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) layer_ = bar_layer::OVERLAY; } + bool passthrough = false; + if (config["passthrough"].isBool()) { + passthrough = config["passthrough"].asBool(); + } else if (layer_ == bar_layer::OVERLAY) { + // swaybar defaults: overlay mode does not accept pointer events. + passthrough = true; + } + auto position = config["position"].asString(); if (position == "right" || position == "left") { @@ -386,7 +426,7 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) box_ = Gtk::Box(Gtk::ORIENTATION_VERTICAL, 0); vertical = true; } - + left_.get_style_context()->add_class("modules-left"); center_.get_style_context()->add_class("modules-center"); right_.get_style_context()->add_class("modules-right"); @@ -454,6 +494,7 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) surface_impl_->setLayer(layer_); surface_impl_->setExclusiveZone(true); surface_impl_->setMargins(margins_); + surface_impl_->setPassThrough(passthrough); surface_impl_->setPosition(position); surface_impl_->setSize(width, height); From 7aaa3df701cdf660c915efec5ee3145180dac9c7 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Sun, 17 Jan 2021 01:40:25 -0800 Subject: [PATCH 11/14] feat(bar): add config flag to disable exclusive zone --- include/bar.hpp | 1 + src/bar.cpp | 11 +++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/include/bar.hpp b/include/bar.hpp index 6bf8c526..6f3dfcf9 100644 --- a/include/bar.hpp +++ b/include/bar.hpp @@ -65,6 +65,7 @@ class Bar { struct waybar_output *output; Json::Value config; struct wl_surface * surface; + bool exclusive = true; bool visible = true; bool vertical = false; Gtk::Window window; diff --git a/src/bar.cpp b/src/bar.cpp index 3361ebf1..7d763599 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -409,6 +409,13 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) layer_ = bar_layer::OVERLAY; } + if (config["exclusive"].isBool()) { + exclusive = config["exclusive"].asBool(); + } else if (layer_ == bar_layer::OVERLAY) { + // swaybar defaults: overlay mode does not reserve an exclusive zone + exclusive = false; + } + bool passthrough = false; if (config["passthrough"].isBool()) { passthrough = config["passthrough"].asBool(); @@ -492,7 +499,7 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) } surface_impl_->setLayer(layer_); - surface_impl_->setExclusiveZone(true); + surface_impl_->setExclusiveZone(exclusive); surface_impl_->setMargins(margins_); surface_impl_->setPassThrough(passthrough); surface_impl_->setPosition(position); @@ -533,7 +540,7 @@ void waybar::Bar::setVisible(bool value) { window.set_opacity(1); surface_impl_->setLayer(layer_); } - surface_impl_->setExclusiveZone(visible); + surface_impl_->setExclusiveZone(exclusive && visible); surface_impl_->commit(); } From da2d603b53d42f68c41055731bd3a4a50dc11f9b Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Fri, 21 May 2021 22:38:48 -0700 Subject: [PATCH 12/14] doc: add man for exclusive and passthrough flags --- man/waybar.5.scd.in | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/man/waybar.5.scd.in b/man/waybar.5.scd.in index 430b9fcd..fe11c4a7 100644 --- a/man/waybar.5.scd.in +++ b/man/waybar.5.scd.in @@ -68,6 +68,17 @@ Also a minimal example configuration can be found on the at the bottom of this m typeof: string ++ Optional name added as a CSS class, for styling multiple waybars. +*exclusive* ++ + typeof: bool ++ + default: *true* unless the layer is set to *overlay* ++ + Option to request an exclusive zone from the compositor. Disable this to allow drawing application windows underneath or on top of the bar. + +*passthrough* ++ + typeof: bool ++ + default: *false* unless the layer is set to *overlay* ++ + Option to pass any pointer events to the window under the bar. + Intended to be used with either *top* or *overlay* layers and without exclusive zone. + *gtk-layer-shell* ++ typeof: bool ++ default: true ++ From 28dfb0ba41f2d0f97b2f84de77e878c6c24fc29d Mon Sep 17 00:00:00 2001 From: Anthony PERARD Date: Wed, 26 May 2021 18:28:49 +0100 Subject: [PATCH 13/14] network: Fix use of carrier information Some RTM_NEWLINK messages may not have the IFLA_CARRIER information. This is the case when a WiFi interface report scan result are available. `carrier` is used regardless of if it is present in the message or not. This would result in the interface appearing "disconnected" in waybar when it isn't. This patch now check that `carrier` is available before using it. The same thing could potentially happen to `ifname` so check if it's set before recording it. Fixes: c1427ff (network: Handle carrier information) Fixes #388 --- src/modules/network.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/modules/network.cpp b/src/modules/network.cpp index 6102a43c..9e0ed395 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -425,7 +425,7 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { struct rtattr *ifla = IFLA_RTA(ifi); const char *ifname = NULL; size_t ifname_len = 0; - bool carrier = false; + std::optional carrier; if (net->ifid_ != -1 && ifi->ifi_index != net->ifid_) { return NL_OK; @@ -446,11 +446,13 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { if (!is_del_event && ifi->ifi_index == net->ifid_) { // Update inferface information - if (net->ifname_.empty()) { + if (net->ifname_.empty() && ifname != NULL) { std::string new_ifname (ifname, ifname_len); net->ifname_ = new_ifname; } - net->carrier_ = carrier; + if (carrier.has_value()) { + net->carrier_ = carrier.value(); + } } else if (!is_del_event && net->ifid_ == -1) { // Checking if it's an interface we care about. std::string new_ifname (ifname, ifname_len); @@ -459,7 +461,9 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { net->ifname_ = new_ifname; net->ifid_ = ifi->ifi_index; - net->carrier_ = carrier; + if (carrier.has_value()) { + net->carrier_ = carrier.value(); + } net->thread_timer_.wake_up(); /* An address for this new interface should be received via an * RTM_NEWADDR event either because we ask for a dump of both links From f49a7a1acbe8489f37a88f5c8f7941a44bb90ac7 Mon Sep 17 00:00:00 2001 From: Anthony PERARD Date: Wed, 26 May 2021 18:51:02 +0100 Subject: [PATCH 14/14] network: Update WiFi information when available The module doesn't update the `essid_` as soon as a WiFi interface is connected, but that happens at some point later, depending on "interval" configuration. Fix that by rerunning the get WiFi information thread when the `carrier` state changes. Also, we will clear the state related to WiFi when the connection is drop to avoid stale information. --- src/modules/network.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/modules/network.cpp b/src/modules/network.cpp index 9e0ed395..583daaea 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -451,6 +451,18 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { net->ifname_ = new_ifname; } if (carrier.has_value()) { + if (net->carrier_ != *carrier) { + if (*carrier) { + // Ask for WiFi information + net->thread_timer_.wake_up(); + } else { + // clear state related to WiFi connection + net->essid_.clear(); + net->signal_strength_dbm_ = 0; + net->signal_strength_ = 0; + net->frequency_ = 0; + } + } net->carrier_ = carrier.value(); } } else if (!is_del_event && net->ifid_ == -1) {