diff --git a/.travis.yml b/.travis.yml index 75b012c2..ec87992e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,7 @@ env: - distro: archlinux - distro: opensuse - distro: fedora + - distro: alpine before_install: - docker pull alexays/waybar:${distro} diff --git a/Dockerfiles/alpine b/Dockerfiles/alpine new file mode 100644 index 00000000..20d2c48e --- /dev/null +++ b/Dockerfiles/alpine @@ -0,0 +1,3 @@ +FROM alpine:latest + +RUN apk add --no-cache git meson alpine-sdk libinput-dev wayland-dev wayland-protocols mesa-dev libxkbcommon-dev eudev-dev pixman-dev gtkmm3-dev jsoncpp-dev libnl3-dev pulseaudio-dev libmpdclient-dev \ No newline at end of file diff --git a/include/ALabel.hpp b/include/ALabel.hpp index d4d10878..cf2cc286 100644 --- a/include/ALabel.hpp +++ b/include/ALabel.hpp @@ -13,7 +13,7 @@ class ALabel : public IModule { ALabel(const Json::Value &, const std::string &format, uint16_t interval = 0); virtual ~ALabel(); virtual auto update() -> void; - virtual std::string getIcon(uint16_t, const std::string &alt = ""); + virtual std::string getIcon(uint16_t, const std::string &alt = "", uint16_t max = 0); virtual operator Gtk::Widget &(); protected: @@ -31,7 +31,7 @@ class ALabel : public IModule { virtual bool handleToggle(GdkEventButton *const &ev); virtual bool handleScroll(GdkEventScroll *); - virtual std::string getState(uint8_t value); + virtual std::string getState(uint8_t value, bool lesser = false); private: std::vector pid_; diff --git a/include/factory.hpp b/include/factory.hpp index fa41ef4b..65d7e3ce 100644 --- a/include/factory.hpp +++ b/include/factory.hpp @@ -7,11 +7,13 @@ #include "modules/sway/window.hpp" #include "modules/sway/workspaces.hpp" #endif +#ifndef NO_FILESYSTEM #include "modules/battery.hpp" +#endif #include "modules/cpu.hpp" #include "modules/idle_inhibitor.hpp" #include "modules/memory.hpp" -#ifdef HAVE_DBUSMENU +#if defined(HAVE_DBUSMENU) && !defined(NO_FILESYSTEM) #include "modules/sni/tray.hpp" #endif #ifdef HAVE_LIBNL diff --git a/include/modules/battery.hpp b/include/modules/battery.hpp index 9787a8dc..5f2290ee 100644 --- a/include/modules/battery.hpp +++ b/include/modules/battery.hpp @@ -30,10 +30,10 @@ class Battery : public ALabel { private: static inline const fs::path data_dir_ = "/sys/class/power_supply/"; - void getBatteries(); - void worker(); - const std::string getAdapterStatus(uint8_t capacity) const; - const std::tuple getInfos() const; + void getBatteries(); + void worker(); + const std::string getAdapterStatus(uint8_t capacity, uint32_t current_now) const; + const std::tuple getInfos() const; util::SleeperThread thread_; util::SleeperThread thread_timer_; diff --git a/include/modules/network.hpp b/include/modules/network.hpp index ade3bb2c..3afae571 100644 --- a/include/modules/network.hpp +++ b/include/modules/network.hpp @@ -28,7 +28,6 @@ class Network : public ALabel { static int handleScan(struct nl_msg*, void*); void worker(); - void disconnected(); void createInfoSocket(); void createEventSocket(); int getExternalInterface(); @@ -37,27 +36,35 @@ class Network : public ALabel { int netlinkResponse(void*, uint32_t, uint32_t groups = 0); void parseEssid(struct nlattr**); void parseSignal(struct nlattr**); + void parseFreq(struct nlattr**); bool associatedOrJoined(struct nlattr**); + bool checkInterface(int if_index, std::string name); + int getPreferredIface(); auto getInfo() -> void; + bool wildcardMatch(const std::string& pattern, const std::string& text); waybar::util::SleeperThread thread_; waybar::util::SleeperThread thread_timer_; int ifid_; + int last_ext_iface_; sa_family_t family_; struct sockaddr_nl nladdr_ = {0}; - struct nl_sock* sk_ = nullptr; - struct nl_sock* info_sock_ = nullptr; + struct nl_sock* sock_ = nullptr; + struct nl_sock* ev_sock_ = nullptr; int efd_; int ev_fd_; int nl80211_id_; + std::mutex mutex_; std::string essid_; std::string ifname_; std::string ipaddr_; std::string netmask_; int cidr_; + bool linked_; int32_t signal_strength_dbm_; uint8_t signal_strength_; + uint32_t frequency_; }; } // namespace waybar::modules diff --git a/include/modules/pulseaudio.hpp b/include/modules/pulseaudio.hpp index b16118cd..7d4b6290 100644 --- a/include/modules/pulseaudio.hpp +++ b/include/modules/pulseaudio.hpp @@ -20,7 +20,7 @@ class Pulseaudio : public ALabel { static void sinkInfoCb(pa_context*, const pa_sink_info*, int, void*); static void serverInfoCb(pa_context*, const pa_server_info*, void*); static void volumeModifyCb(pa_context*, int, void*); - bool handleScroll(GdkEventScroll* e); + bool handleVolume(GdkEventScroll* e); const std::string getPortIcon() const; @@ -33,6 +33,7 @@ class Pulseaudio : public ALabel { bool muted_; std::string port_name_; std::string desc_; + std::string monitor_; bool scrolling_; }; diff --git a/include/modules/sni/item.hpp b/include/modules/sni/item.hpp index ee209956..c8f835bd 100644 --- a/include/modules/sni/item.hpp +++ b/include/modules/sni/item.hpp @@ -46,7 +46,7 @@ class Item : public sigc::trackable { std::string menu; DbusmenuGtkMenu* dbus_menu = nullptr; Gtk::Menu* gtk_menu = nullptr; - bool item_is_menu; + bool item_is_menu = false; private: void proxyReady(Glib::RefPtr& result); @@ -59,12 +59,12 @@ class Item : public sigc::trackable { void updateImage(); Glib::RefPtr extractPixBuf(GVariant* variant); Glib::RefPtr getIconByName(const std::string& name, int size); - static void onMenuDestroyed(Item* self); - bool makeMenu(GdkEventButton* const& ev); + static void onMenuDestroyed(Item* self, GObject* old_menu_pointer); + void makeMenu(GdkEventButton* const& ev); bool handleClick(GdkEventButton* const& /*ev*/); - Glib::RefPtr cancellable_; Glib::RefPtr proxy_; + Glib::RefPtr cancellable_; bool update_pending_; }; diff --git a/include/modules/sway/mode.hpp b/include/modules/sway/mode.hpp index 36f53e14..e2c4e116 100644 --- a/include/modules/sway/mode.hpp +++ b/include/modules/sway/mode.hpp @@ -10,7 +10,7 @@ namespace waybar::modules::sway { -class Mode : public ALabel { +class Mode : public ALabel, public sigc::trackable { public: Mode(const std::string&, const Json::Value&); ~Mode() = default; diff --git a/include/modules/sway/window.hpp b/include/modules/sway/window.hpp index f8f33b00..3715eb2f 100644 --- a/include/modules/sway/window.hpp +++ b/include/modules/sway/window.hpp @@ -6,12 +6,12 @@ #include "bar.hpp" #include "client.hpp" #include "modules/sway/ipc/client.hpp" -#include "util/sleeper_thread.hpp" #include "util/json.hpp" +#include "util/sleeper_thread.hpp" namespace waybar::modules::sway { -class Window : public ALabel { +class Window : public ALabel, public sigc::trackable { public: Window(const std::string&, const waybar::Bar&, const Json::Value&); ~Window() = default; @@ -27,10 +27,11 @@ class Window : public ALabel { const Bar& bar_; waybar::util::SleeperThread thread_; Ipc ipc_; + std::mutex mutex_; std::string window_; int windowId_; std::string app_id_; - util::JsonParser parser_; + util::JsonParser parser_; }; } // namespace waybar::modules::sway diff --git a/include/modules/sway/workspaces.hpp b/include/modules/sway/workspaces.hpp index 1e0e8107..8c7875cc 100644 --- a/include/modules/sway/workspaces.hpp +++ b/include/modules/sway/workspaces.hpp @@ -7,12 +7,12 @@ #include "bar.hpp" #include "client.hpp" #include "modules/sway/ipc/client.hpp" -#include "util/sleeper_thread.hpp" #include "util/json.hpp" +#include "util/sleeper_thread.hpp" namespace waybar::modules::sway { -class Workspaces : public IModule { +class Workspaces : public IModule, public sigc::trackable { public: Workspaces(const std::string&, const waybar::Bar&, const Json::Value&); ~Workspaces() = default; @@ -27,10 +27,10 @@ class Workspaces : public IModule { Gtk::Button& addButton(const Json::Value&); void onButtonReady(const Json::Value&, Gtk::Button&); std::string getIcon(const std::string&, const Json::Value&); - bool handleScroll(GdkEventScroll*); const std::string getCycleWorkspace(std::vector::iterator, bool prev) const; uint16_t getWorkspaceIndex(const std::string& name) const; std::string trimWorkspaceName(std::string); + bool handleScroll(GdkEventScroll*); const Bar& bar_; const Json::Value& config_; diff --git a/include/modules/temperature.hpp b/include/modules/temperature.hpp index 49410036..22bc21c2 100644 --- a/include/modules/temperature.hpp +++ b/include/modules/temperature.hpp @@ -4,11 +4,6 @@ #include #include "ALabel.hpp" #include "util/sleeper_thread.hpp" -#ifdef FILESYSTEM_EXPERIMENTAL -#include -#else -#include -#endif namespace waybar::modules { diff --git a/include/util/json.hpp b/include/util/json.hpp index 3f83172f..581ad9b4 100644 --- a/include/util/json.hpp +++ b/include/util/json.hpp @@ -5,15 +5,16 @@ namespace waybar::util { struct JsonParser { - JsonParser() : reader_(builder_.newCharReader()) {} + JsonParser() {} const Json::Value parse(const std::string& data) const { Json::Value root(Json::objectValue); if (data.empty()) { return root; } - std::string err; - bool res = reader_->parse(data.c_str(), data.c_str() + data.size(), &root, &err); + std::unique_ptr const reader(builder_.newCharReader()); + std::string err; + bool res = reader->parse(data.c_str(), data.c_str() + data.size(), &root, &err); if (!res) throw std::runtime_error(err); return root; } @@ -21,8 +22,7 @@ struct JsonParser { ~JsonParser() = default; private: - Json::CharReaderBuilder builder_; - std::unique_ptr const reader_; + Json::CharReaderBuilder builder_; }; } // namespace waybar::util diff --git a/meson.build b/meson.build index dc7778b2..111b1a9a 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project( 'waybar', 'cpp', 'c', - version: '0.6.2', + version: '0.6.4', license: 'MIT', default_options : [ 'cpp_std=c++17', @@ -12,7 +12,7 @@ project( cpp_args = [] cpp_link_args = [] -if false # libc++ +if get_option('libcxx') cpp_args += ['-stdlib=libc++'] cpp_link_args += ['-stdlib=libc++', '-lc++abi'] @@ -34,7 +34,12 @@ else endif if not compiler.has_header('filesystem') - add_project_arguments('-DFILESYSTEM_EXPERIMENTAL', language: 'cpp') + if compiler.has_header('experimental/filesystem') + add_project_arguments('-DFILESYSTEM_EXPERIMENTAL', language: 'cpp') + else + add_project_arguments('-DNO_FILESYSTEM', language: 'cpp') + warning('No filesystem header found, some modules may not work') + endif endif add_global_arguments(cpp_args, language : 'cpp') diff --git a/meson_options.txt b/meson_options.txt index f49d9c14..d84bd22c 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1,3 +1,4 @@ +option('libcxx', type : 'boolean', value : false, description : 'Build with Clang\'s libc++ instead of libstdc++ on Linux.') option('libnl', type: 'feature', value: 'auto', description: 'Enable libnl support for network related features') option('libudev', type: 'feature', value: 'auto', description: 'Enable libudev support for udev related features') option('pulseaudio', type: 'feature', value: 'auto', description: 'Enable support for pulseaudio') diff --git a/resources/config b/resources/config index 2c1f69c3..f5349895 100644 --- a/resources/config +++ b/resources/config @@ -78,8 +78,9 @@ // "thermal-zone": 2, // "hwmon-path": "/sys/class/hwmon/hwmon2/temp1_input", "critical-threshold": 80, - // "format-critical": "{temperatureC}°C ", - "format": "{temperatureC}°C " + // "format-critical": "{temperatureC}°C {icon}", + "format": "{temperatureC}°C {icon}", + "format-icons": ["", "", ""] }, "backlight": { // "device": "acpi_video1", @@ -93,6 +94,8 @@ "critical": 15 }, "format": "{capacity}% {icon}", + "format-charging": "{capacity}% ", + "format-plugged": "{capacity}% ", // "format-good": "", // An empty format will hide the module // "format-full": "", "format-icons": ["", "", "", "", ""] @@ -101,16 +104,17 @@ "bat": "BAT2" }, "network": { - // "interface": "wlp2s0", // (Optional) To force the use of this interface + // "interface": "wlp2*", // (Optional) To force the use of this interface "format-wifi": "{essid} ({signalStrength}%) ", - "format-ethernet": "{ifname}: {ipaddr}/{cidr} ", + "format-ethernet": "{ifname}: {ipaddr}/{cidr} ", + "format-linked": "{ifname} (No IP) ", "format-disconnected": "Disconnected ⚠" }, "pulseaudio": { - //"scroll-step": 1, + // "scroll-step": 1, // %, can be a float "format": "{volume}% {icon}", "format-bluetooth": "{volume}% {icon}", - "format-muted": "", + "format-muted": "", "format-icons": { "headphones": "", "handsfree": "", @@ -118,7 +122,7 @@ "phone": "", "portable": "", "car": "", - "default": ["", ""] + "default": ["", "", ""] }, "on-click": "pavucontrol" }, diff --git a/resources/style.css b/resources/style.css index ea881313..b2c96269 100644 --- a/resources/style.css +++ b/resources/style.css @@ -32,8 +32,7 @@ window#waybar.termite { } window#waybar.chromium { - background-color: #DEE1E6; - color: #000000; + background-color: #000000; border: none; } @@ -45,19 +44,11 @@ window#waybar.chromium { border-bottom: 3px solid transparent; } -window#waybar.chromium #workspaces button { - color: #3F3F3F; -} - #workspaces button.focused { background: #64727D; border-bottom: 3px solid #ffffff; } -window#waybar.chromium #workspaces button.focused { - color: #ffffff; -} - #workspaces button.urgent { background-color: #eb4d4b; } @@ -142,6 +133,7 @@ label:focus { #custom-media { background: #66cc99; color: #2a5c45; + min-width: 100px; } .custom-spotify { diff --git a/src/ALabel.cpp b/src/ALabel.cpp index 9435e03b..e1fa212d 100644 --- a/src/ALabel.cpp +++ b/src/ALabel.cpp @@ -109,7 +109,7 @@ bool waybar::ALabel::handleScroll(GdkEventScroll* e) { return true; } -std::string waybar::ALabel::getIcon(uint16_t percentage, const std::string& alt) { +std::string waybar::ALabel::getIcon(uint16_t percentage, const std::string& alt, uint16_t max) { auto format_icons = config_["format-icons"]; if (format_icons.isObject()) { if (!alt.empty() && (format_icons[alt].isString() || format_icons[alt].isArray())) { @@ -120,7 +120,7 @@ std::string waybar::ALabel::getIcon(uint16_t percentage, const std::string& alt) } if (format_icons.isArray()) { auto size = format_icons.size(); - auto idx = std::clamp(percentage / (100 / size), 0U, size - 1); + auto idx = std::clamp(percentage / ((max == 0 ? 100 : max) / size), 0U, size - 1); format_icons = format_icons[idx]; } if (format_icons.isString()) { @@ -129,7 +129,10 @@ std::string waybar::ALabel::getIcon(uint16_t percentage, const std::string& alt) return ""; } -std::string waybar::ALabel::getState(uint8_t value) { +std::string waybar::ALabel::getState(uint8_t value, bool lesser) { + if (!config_["states"].isObject()) { + return ""; + } // Get current state std::vector> states; if (config_["states"].isObject()) { @@ -140,10 +143,12 @@ std::string waybar::ALabel::getState(uint8_t value) { } } // Sort states - std::sort(states.begin(), states.end(), [](auto& a, auto& b) { return a.second < b.second; }); + std::sort(states.begin(), states.end(), [&lesser](auto& a, auto& b) { + return lesser ? a.second < b.second : a.second > b.second; + }); std::string valid_state; for (auto const& state : states) { - if (value <= state.second && valid_state.empty()) { + if ((lesser ? value <= state.second : value >= state.second) && valid_state.empty()) { label_.get_style_context()->add_class(state.first); valid_state = state.first; } else { diff --git a/src/bar.cpp b/src/bar.cpp index 6c6de20c..dd7b8805 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -20,7 +20,6 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) height_ = 0; width_ = 1; } - window.set_size_request(width_, height_); auto gtk_window = window.gobj(); auto gtk_widget = GTK_WIDGET(gtk_window); @@ -43,8 +42,6 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) auto height = config["height"].isUInt() ? config["height"].asUInt() : height_; auto width = config["width"].isUInt() ? config["width"].asUInt() : width_; - window.signal_configure_event().connect_notify(sigc::mem_fun(*this, &Bar::onConfigure)); - std::size_t anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP; if (config["position"] == "bottom") { anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; @@ -73,6 +70,9 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) wl_display_roundtrip(client->wl_display); setupWidgets(); + + window.set_size_request(width_, height_); + window.signal_configure_event().connect_notify(sigc::mem_fun(*this, &Bar::onConfigure)); } void waybar::Bar::setMarginsAndZone(uint32_t height, uint32_t width) { diff --git a/src/client.cpp b/src/client.cpp index 8713ec82..b927684b 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -153,6 +153,7 @@ void waybar::Client::handleName(void * data, struct zxdg_output_v1 * /*xdg_ wl_output_destroy(output->output); zxdg_output_v1_destroy(output->xdg_output); } else { + wl_display_roundtrip(client->wl_display); for (const auto &config : configs) { client->bars.emplace_back(std::make_unique(output.get(), config)); Glib::RefPtr screen = client->bars.back()->window.get_screen(); diff --git a/src/factory.cpp b/src/factory.cpp index a2e8839e..100ca146 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -7,9 +7,11 @@ waybar::IModule* waybar::Factory::makeModule(const std::string& name) const { auto hash_pos = name.find('#'); auto ref = name.substr(0, hash_pos); auto id = hash_pos != std::string::npos ? name.substr(hash_pos + 1) : ""; +#ifndef NO_FILESYSTEM if (ref == "battery") { return new waybar::modules::Battery(id, config_[name]); } +#endif #ifdef HAVE_SWAY if (ref == "sway/mode") { return new waybar::modules::sway::Mode(id, config_[name]); @@ -33,7 +35,7 @@ waybar::IModule* waybar::Factory::makeModule(const std::string& name) const { if (ref == "clock") { return new waybar::modules::Clock(id, config_[name]); } -#ifdef HAVE_DBUSMENU +#if defined(HAVE_DBUSMENU) && !defined(NO_FILESYSTEM) if (ref == "tray") { return new waybar::modules::SNI::Tray(id, bar_, config_[name]); } diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index 607efdd4..2c1cc112 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -47,21 +47,21 @@ void waybar::modules::Battery::worker() { void waybar::modules::Battery::getBatteries() { try { - for (auto const& node : fs::directory_iterator(data_dir_)) { + for (auto& node : fs::directory_iterator(data_dir_)) { if (!fs::is_directory(node)) { continue; } auto dir_name = node.path().filename(); auto bat_defined = config_["bat"].isString(); if (((bat_defined && dir_name == config_["bat"].asString()) || !bat_defined) && - fs::exists(node / "capacity") && fs::exists(node / "uevent") && - fs::exists(node / "status")) { - batteries_.push_back(node); + fs::exists(node.path() / "capacity") && fs::exists(node.path() / "uevent") && + fs::exists(node.path() / "status")) { + batteries_.push_back(node.path()); } auto adap_defined = config_["adapter"].isString(); if (((adap_defined && dir_name == config_["adapter"].asString()) || !adap_defined) && - fs::exists(node / "online")) { - adapter_ = node; + fs::exists(node.path() / "online")) { + adapter_ = node.path(); } } } catch (fs::filesystem_error& e) { @@ -75,52 +75,69 @@ void waybar::modules::Battery::getBatteries() { } } -const std::tuple waybar::modules::Battery::getInfos() const { +const std::tuple waybar::modules::Battery::getInfos() const { try { uint16_t total = 0; + uint32_t total_current = 0; std::string status = "Unknown"; for (auto const& bat : batteries_) { uint16_t capacity; + uint32_t current_now; std::string _status; std::ifstream(bat / "capacity") >> capacity; std::ifstream(bat / "status") >> _status; + std::ifstream(bat / "current_now") >> current_now; if (_status != "Unknown") { status = _status; } total += capacity; + total_current += current_now; + } + if (!adapter_.empty() && status == "Discharging") { + bool online; + std::ifstream(adapter_ / "online") >> online; + if (online) { + status = "Plugged"; + } } uint16_t capacity = total / batteries_.size(); - return {capacity, status}; + return {capacity, total_current, status}; } catch (const std::exception& e) { std::cerr << e.what() << std::endl; - return {0, "Unknown"}; + return {0, 0, "Unknown"}; } } -const std::string waybar::modules::Battery::getAdapterStatus(uint8_t capacity) const { +const std::string waybar::modules::Battery::getAdapterStatus(uint8_t capacity, + uint32_t current_now) const { if (!adapter_.empty()) { bool online; std::ifstream(adapter_ / "online") >> online; if (capacity == 100) { return "Full"; } - return online ? "Charging" : "Discharging"; + if (online) { + return "Charging"; + } + return "Discharging"; } return "Unknown"; } auto waybar::modules::Battery::update() -> void { - auto [capacity, status] = getInfos(); + auto [capacity, current_now, status] = getInfos(); if (status == "Unknown") { - status = getAdapterStatus(capacity); + status = getAdapterStatus(capacity, current_now); } if (tooltipEnabled()) { label_.set_tooltip_text(status); } std::transform(status.begin(), status.end(), status.begin(), ::tolower); auto format = format_; - auto state = getState(capacity); - label_.get_style_context()->remove_class(old_status_); + auto state = getState(capacity, true); + if (!old_status_.empty()) { + label_.get_style_context()->remove_class(old_status_); + } label_.get_style_context()->add_class(status); old_status_ = status; if (!state.empty() && config_["format-" + status + "-" + state].isString()) { @@ -134,7 +151,7 @@ auto waybar::modules::Battery::update() -> void { event_box_.hide(); } else { event_box_.show(); - label_.set_markup( - fmt::format(format, fmt::arg("capacity", capacity), fmt::arg("icon", getIcon(capacity)))); + label_.set_markup(fmt::format( + format, fmt::arg("capacity", capacity), fmt::arg("icon", getIcon(capacity, state)))); } } diff --git a/src/modules/mpd.cpp b/src/modules/mpd.cpp index c019d366..b94eea91 100644 --- a/src/modules/mpd.cpp +++ b/src/modules/mpd.cpp @@ -65,7 +65,7 @@ std::thread waybar::modules::MPD::event_listener() { try { if (connection_ == nullptr) { // Retry periodically if no connection - update(); + dp.emit(); std::this_thread::sleep_for(interval_); } else { waitForEvent(); diff --git a/src/modules/network.cpp b/src/modules/network.cpp index 94f34cd0..cca1d50b 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -3,31 +3,27 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &config) : ALabel(config, "{ifname}", 60), - family_(AF_INET), + ifid_(-1), + last_ext_iface_(-1), + family_(config["family"] == "ipv6" ? AF_INET6 : AF_INET), efd_(-1), ev_fd_(-1), cidr_(-1), signal_strength_dbm_(0), - signal_strength_(0) { + signal_strength_(0), + frequency_(0) { label_.set_name("network"); if (!id.empty()) { label_.get_style_context()->add_class(id); } createInfoSocket(); createEventSocket(); - if (config_["interface"].isString()) { - ifid_ = if_nametoindex(config_["interface"].asCString()); - ifname_ = config_["interface"].asString(); - if (ifid_ <= 0) { - throw std::runtime_error("Can't found network interface"); - } - } else { - ifid_ = getExternalInterface(); - if (ifid_ > 0) { - char ifname[IF_NAMESIZE]; - if_indextoname(ifid_, ifname); - ifname_ = ifname; - } + auto default_iface = getPreferredIface(); + if (default_iface != -1) { + char ifname[IF_NAMESIZE]; + if_indextoname(default_iface, ifname); + ifname_ = ifname; + getInterfaceAddress(); } dp.emit(); worker(); @@ -42,39 +38,42 @@ waybar::modules::Network::~Network() { if (efd_ > -1) { close(efd_); } - if (info_sock_ != nullptr) { - nl_socket_drop_membership(info_sock_, RTMGRP_LINK); - nl_socket_drop_membership(info_sock_, RTMGRP_IPV4_IFADDR); - nl_close(info_sock_); - nl_socket_free(info_sock_); + if (ev_sock_ != nullptr) { + nl_socket_drop_membership(ev_sock_, RTNLGRP_LINK); + nl_socket_drop_membership(ev_sock_, RTNLGRP_IPV4_IFADDR); + nl_socket_drop_membership(ev_sock_, RTNLGRP_IPV6_IFADDR); + nl_socket_drop_membership(ev_sock_, RTNLGRP_IPV4_ROUTE); + nl_socket_drop_membership(ev_sock_, RTNLGRP_IPV6_ROUTE); + nl_close(ev_sock_); + nl_socket_free(ev_sock_); } - if (sk_ != nullptr) { - nl_close(sk_); - nl_socket_free(sk_); + if (sock_ != nullptr) { + nl_close(sock_); + nl_socket_free(sock_); } } void waybar::modules::Network::createInfoSocket() { - info_sock_ = nl_socket_alloc(); - if (nl_connect(info_sock_, NETLINK_ROUTE) != 0) { + 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_join_groups(ev_sock_, RTMGRP_LINK); + if (nl_connect(ev_sock_, NETLINK_ROUTE) != 0) { throw std::runtime_error("Can't connect network socket"); } - if (nl_socket_add_membership(info_sock_, RTMGRP_LINK) != 0) { - throw std::runtime_error("Can't add membership"); - } - if (nl_socket_add_membership(info_sock_, RTMGRP_IPV4_IFADDR) != 0) { - throw std::runtime_error("Can't add membership"); - } - nl_socket_disable_seq_check(info_sock_); - nl_socket_set_nonblocking(info_sock_); - nl_socket_modify_cb(info_sock_, NL_CB_VALID, NL_CB_CUSTOM, handleEvents, this); + nl_socket_add_membership(ev_sock_, RTNLGRP_LINK); + nl_socket_add_membership(ev_sock_, RTNLGRP_IPV4_IFADDR); + nl_socket_add_membership(ev_sock_, RTNLGRP_IPV6_IFADDR); + nl_socket_add_membership(ev_sock_, RTNLGRP_IPV4_ROUTE); + 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"); } { ev_fd_ = eventfd(0, EFD_NONBLOCK); - struct epoll_event event = {0}; + struct epoll_event event; + memset(&event, 0, sizeof(event)); event.events = EPOLLIN | EPOLLET; event.data.fd = ev_fd_; if (epoll_ctl(efd_, EPOLL_CTL_ADD, ev_fd_, &event) == -1) { @@ -82,8 +81,9 @@ void waybar::modules::Network::createInfoSocket() { } } { - auto fd = nl_socket_get_fd(info_sock_); - struct epoll_event event = {0}; + auto fd = nl_socket_get_fd(ev_sock_); + struct epoll_event event; + memset(&event, 0, sizeof(event)); event.events = EPOLLIN | EPOLLET | EPOLLRDHUP; event.data.fd = fd; if (epoll_ctl(efd_, EPOLL_CTL_ADD, fd, &event) == -1) { @@ -93,14 +93,14 @@ void waybar::modules::Network::createInfoSocket() { } void waybar::modules::Network::createEventSocket() { - sk_ = nl_socket_alloc(); - if (genl_connect(sk_) != 0) { + sock_ = nl_socket_alloc(); + if (genl_connect(sock_) != 0) { throw std::runtime_error("Can't connect to netlink socket"); } - if (nl_socket_modify_cb(sk_, NL_CB_VALID, NL_CB_CUSTOM, handleScan, this) < 0) { + if (nl_socket_modify_cb(sock_, NL_CB_VALID, NL_CB_CUSTOM, handleScan, this) < 0) { throw std::runtime_error("Can't set callback"); } - nl80211_id_ = genl_ctrl_resolve(sk_, "nl80211"); + nl80211_id_ = genl_ctrl_resolve(sock_, "nl80211"); if (nl80211_id_ < 0) { throw std::runtime_error("Can't resolve nl80211 interface"); } @@ -119,15 +119,13 @@ 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(info_sock_)) { - nl_recvmsgs_default(info_sock_); + if (events[i].data.fd == nl_socket_get_fd(ev_sock_)) { + nl_recvmsgs_default(ev_sock_); } else { thread_.stop(); break; } } - } else if (ec == -1) { - thread_.stop(); } }; } @@ -138,7 +136,7 @@ auto waybar::modules::Network::update() -> void { if (config_["tooltip-format"].isString()) { tooltip_format = config_["tooltip-format"].asString(); } - if (ifid_ <= 0 || ipaddr_.empty()) { + if (ifid_ <= 0 || !linked_) { if (config_["format-disconnected"].isString()) { default_format_ = config_["format-disconnected"].asString(); } @@ -156,6 +154,14 @@ auto waybar::modules::Network::update() -> void { tooltip_format = config_["tooltip-format-ethernet"].asString(); } connectiontype = "ethernet"; + } else if (ipaddr_.empty()) { + if (config_["format-linked"].isString()) { + default_format_ = config_["format-linked"].asString(); + } + if (config_["tooltip-format-linked"].isString()) { + tooltip_format = config_["tooltip-format-linked"].asString(); + } + connectiontype = "linked"; } else { if (config_["format-wifi"].isString()) { default_format_ = config_["format-wifi"].asString(); @@ -179,6 +185,7 @@ auto waybar::modules::Network::update() -> void { fmt::arg("netmask", netmask_), fmt::arg("ipaddr", ipaddr_), fmt::arg("cidr", cidr_), + fmt::arg("frequency", frequency_), fmt::arg("icon", getIcon(signal_strength_, connectiontype))); label_.set_markup(text); if (tooltipEnabled()) { @@ -191,6 +198,7 @@ auto waybar::modules::Network::update() -> void { fmt::arg("netmask", netmask_), fmt::arg("ipaddr", ipaddr_), fmt::arg("cidr", cidr_), + fmt::arg("frequency", frequency_), fmt::arg("icon", getIcon(signal_strength_, connectiontype))); label_.set_tooltip_text(tooltip_text); } else { @@ -199,21 +207,6 @@ auto waybar::modules::Network::update() -> void { } } -void waybar::modules::Network::disconnected() { - essid_.clear(); - signal_strength_dbm_ = 0; - signal_strength_ = 0; - ipaddr_.clear(); - netmask_.clear(); - cidr_ = 0; - if (!config_["interface"].isString()) { - ifname_.clear(); - ifid_ = -1; - } - // Need to wait otherwise we'll have the same information - thread_.sleep_for(std::chrono::seconds(1)); -} - // Based on https://gist.github.com/Yawning/c70d804d4b8ae78cc698 int waybar::modules::Network::getExternalInterface() { static const uint32_t route_buffer_size = 8192; @@ -333,6 +326,7 @@ int waybar::modules::Network::getExternalInterface() { } while (true); out: + last_ext_iface_ = ifidx; return ifidx; } @@ -343,26 +337,33 @@ void waybar::modules::Network::getInterfaceAddress() { netmask_.clear(); cidr_ = 0; int success = getifaddrs(&ifaddr); - if (success == 0) { - ifa = ifaddr; - while (ifa != nullptr && ipaddr_.empty() && netmask_.empty()) { - if (ifa->ifa_addr != nullptr && ifa->ifa_addr->sa_family == family_) { - if (strcmp(ifa->ifa_name, ifname_.c_str()) == 0) { - ipaddr_ = inet_ntoa(((struct sockaddr_in *)ifa->ifa_addr)->sin_addr); - netmask_ = inet_ntoa(((struct sockaddr_in *)ifa->ifa_netmask)->sin_addr); - cidrRaw = ((struct sockaddr_in *)(ifa->ifa_netmask))->sin_addr.s_addr; - unsigned int cidr = 0; - while (cidrRaw) { - cidr += cidrRaw & 1; - cidrRaw >>= 1; - } - cidr_ = cidr; - } - } - ifa = ifa->ifa_next; - } - freeifaddrs(ifaddr); + if (success != 0) { + return; } + ifa = ifaddr; + while (ifa != nullptr && ipaddr_.empty() && netmask_.empty()) { + if (ifa->ifa_addr != nullptr && ifa->ifa_addr->sa_family == family_ && + ifa->ifa_name == ifname_) { + char ipaddr[INET6_ADDRSTRLEN]; + ipaddr_ = inet_ntop(family_, + &reinterpret_cast(ifa->ifa_addr)->sin_addr, + ipaddr, + INET6_ADDRSTRLEN); + char netmask[INET6_ADDRSTRLEN]; + auto net_addr = reinterpret_cast(ifa->ifa_netmask); + netmask_ = inet_ntop(family_, &net_addr->sin_addr, netmask, INET6_ADDRSTRLEN); + cidrRaw = net_addr->sin_addr.s_addr; + linked_ = ifa->ifa_flags & IFF_RUNNING; + unsigned int cidr = 0; + while (cidrRaw) { + cidr += cidrRaw & 1; + cidrRaw >>= 1; + } + cidr_ = cidr; + } + ifa = ifa->ifa_next; + } + freeifaddrs(ifaddr); } int waybar::modules::Network::netlinkRequest(void *req, uint32_t reqlen, uint32_t groups) { @@ -370,8 +371,13 @@ int waybar::modules::Network::netlinkRequest(void *req, uint32_t reqlen, uint32_ sa.nl_family = AF_NETLINK; sa.nl_groups = groups; struct iovec iov = {req, reqlen}; - struct msghdr msg = {&sa, sizeof(sa), &iov, 1, nullptr, 0, 0}; - return sendmsg(nl_socket_get_fd(info_sock_), &msg, 0); + 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) { @@ -379,56 +385,125 @@ int waybar::modules::Network::netlinkResponse(void *resp, uint32_t resplen, uint sa.nl_family = AF_NETLINK; sa.nl_groups = groups; struct iovec iov = {resp, resplen}; - struct msghdr msg = {&sa, sizeof(sa), &iov, 1, nullptr, 0, 0}; - auto ret = recvmsg(nl_socket_get_fd(info_sock_), &msg, 0); + 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; } -int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { - int ret = 0; - auto net = static_cast(data); - bool need_update = false; - for (nlmsghdr *nh = nlmsg_hdr(msg); NLMSG_OK(nh, ret); nh = NLMSG_NEXT(nh, ret)) { - if (nh->nlmsg_type == RTM_NEWADDR) { - need_update = true; - } - if (nh->nlmsg_type < RTM_NEWADDR) { - auto rtif = static_cast(NLMSG_DATA(nh)); - if (rtif->ifi_index == static_cast(net->ifid_)) { - need_update = true; - if (!(rtif->ifi_flags & IFF_RUNNING)) { - net->disconnected(); - net->dp.emit(); - return NL_SKIP; +bool waybar::modules::Network::checkInterface(int if_index, std::string name) { + if (config_["interface"].isString()) { + return config_["interface"].asString() == name || + wildcardMatch(config_["interface"].asString(), name); + } + auto external_iface = getExternalInterface(); + if (external_iface == -1) { + // Try with lastest working external iface + return last_ext_iface_ == if_index; + } + return external_iface == if_index; +} + +int waybar::modules::Network::getPreferredIface() { + if (config_["interface"].isString()) { + ifid_ = if_nametoindex(config_["interface"].asCString()); + if (ifid_ > 0) { + ifname_ = config_["interface"].asString(); + 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_; } - if (need_update) break; } - if (net->ifid_ <= 0 && !net->config_["interface"].isString()) { - for (uint8_t i = 0; i < MAX_RETRY; i += 1) { - net->ifid_ = net->getExternalInterface(); - if (net->ifid_ > 0) { - break; - } - // Need to wait before get external interface - net->thread_.sleep_for(std::chrono::seconds(1)); - } - if (net->ifid_ > 0) { - char ifname[IF_NAMESIZE]; - if_indextoname(net->ifid_, ifname); + ifid_ = getExternalInterface(); + if (ifid_ > 0) { + char ifname[IF_NAMESIZE]; + if_indextoname(ifid_, ifname); + ifname_ = ifname; + return ifid_; + } + return -1; +} + +int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { + auto net = static_cast(data); + auto nh = nlmsg_hdr(msg); + std::lock_guard lock(net->mutex_); + + if (nh->nlmsg_type == RTM_NEWADDR) { + auto rtif = static_cast(NLMSG_DATA(nh)); + char ifname[IF_NAMESIZE]; + if_indextoname(rtif->ifi_index, ifname); + // Auto detected network can also be assigned here + if (net->checkInterface(rtif->ifi_index, ifname) && net->ifid_ == -1) { + net->linked_ = true; net->ifname_ = ifname; - need_update = true; + net->ifid_ = rtif->ifi_index; + net->dp.emit(); } - } - if (need_update) { - if (net->ifid_ > 0) { - net->getInfo(); + // Check for valid interface + if (rtif->ifi_index == static_cast(net->ifid_)) { + // Get Iface and WIFI info + net->thread_timer_.wake_up(); + net->getInterfaceAddress(); + net->dp.emit(); + } + } else if (nh->nlmsg_type == RTM_DELADDR) { + auto rtif = static_cast(NLMSG_DATA(nh)); + // Check for valid interface + if (rtif->ifi_index == static_cast(net->ifid_)) { + net->ipaddr_.clear(); + net->netmask_.clear(); + net->cidr_ = 0; + net->dp.emit(); + } + } else if (nh->nlmsg_type < RTM_NEWADDR) { + auto rtif = static_cast(NLMSG_DATA(nh)); + char ifname[IF_NAMESIZE]; + if_indextoname(rtif->ifi_index, ifname); + // Check for valid interface + if (net->checkInterface(rtif->ifi_index, ifname) && rtif->ifi_flags & IFF_RUNNING) { + net->linked_ = true; + net->ifname_ = ifname; + net->ifid_ = rtif->ifi_index; + net->dp.emit(); + } else if (rtif->ifi_index == net->ifid_) { + net->linked_ = false; + net->ifname_.clear(); + net->ifid_ = -1; + net->essid_.clear(); + net->signal_strength_dbm_ = 0; + net->signal_strength_ = 0; + // Check for a new interface and get info + auto new_iface = net->getPreferredIface(); + if (new_iface != -1) { + net->thread_timer_.wake_up(); + net->getInterfaceAddress(); + } + net->dp.emit(); } - net->dp.emit(); } return NL_SKIP; } @@ -464,7 +539,7 @@ int waybar::modules::Network::handleScan(struct nl_msg *msg, void *data) { } net->parseEssid(bss); net->parseSignal(bss); - // TODO(someone): parse quality + net->parseFreq(bss); return NL_SKIP; } @@ -504,6 +579,13 @@ void waybar::modules::Network::parseSignal(struct nlattr **bss) { } } +void waybar::modules::Network::parseFreq(struct nlattr **bss) { + if (bss[NL80211_BSS_FREQUENCY] != nullptr) { + // in MHz + frequency_ = nla_get_u32(bss[NL80211_BSS_FREQUENCY]); + } +} + bool waybar::modules::Network::associatedOrJoined(struct nlattr **bss) { if (bss[NL80211_BSS_STATUS] == nullptr) { return false; @@ -520,7 +602,6 @@ bool waybar::modules::Network::associatedOrJoined(struct nlattr **bss) { } auto waybar::modules::Network::getInfo() -> void { - getInterfaceAddress(); struct nl_msg *nl_msg = nlmsg_alloc(); if (nl_msg == nullptr) { return; @@ -532,5 +613,44 @@ auto waybar::modules::Network::getInfo() -> void { nlmsg_free(nl_msg); return; } - nl_send_sync(sk_, nl_msg); + nl_send_sync(sock_, nl_msg); +} + +// https://gist.github.com/rressi/92af77630faf055934c723ce93ae2495 +bool waybar::modules::Network::wildcardMatch(const std::string &pattern, const std::string &text) { + auto P = int(pattern.size()); + auto T = int(text.size()); + + auto p = 0, fallback_p = -1; + auto t = 0, fallback_t = -1; + + while (t < T) { + // Wildcard match: + if (p < P && pattern[p] == '*') { + fallback_p = p++; // starting point after failures + fallback_t = t; // starting point after failures + } + + // Simple match: + else if (p < P && (pattern[p] == '?' || pattern[p] == text[t])) { + p++; + t++; + } + + // Failure, fall back just after last matched '*': + else if (fallback_p >= 0) { + p = fallback_p + 1; // position just after last matched '*" + t = ++fallback_t; // re-try to match text from here + } + + // There were no '*' before, so we fail here: + else { + return false; + } + } + + // Consume all '*' at the end of pattern: + while (p < P && pattern[p] == '*') p++; + + return p == P; } diff --git a/src/modules/pulseaudio.cpp b/src/modules/pulseaudio.cpp index 3c768445..97c37936 100644 --- a/src/modules/pulseaudio.cpp +++ b/src/modules/pulseaudio.cpp @@ -39,7 +39,7 @@ waybar::modules::Pulseaudio::Pulseaudio(const std::string &id, const Json::Value // events are configured if (!config["on-scroll-up"].isString() && !config["on-scroll-down"].isString()) { event_box_.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK); - event_box_.signal_scroll_event().connect(sigc::mem_fun(*this, &Pulseaudio::handleScroll)); + event_box_.signal_scroll_event().connect(sigc::mem_fun(*this, &Pulseaudio::handleVolume)); } } @@ -71,15 +71,15 @@ void waybar::modules::Pulseaudio::contextStateCb(pa_context *c, void *data) { } } -bool waybar::modules::Pulseaudio::handleScroll(GdkEventScroll *e) { +bool waybar::modules::Pulseaudio::handleVolume(GdkEventScroll *e) { // Avoid concurrent scroll event - bool direction_up = false; - uint16_t change = config_["scroll-step"].isUInt() ? config_["scroll-step"].asUInt() * 100 : 100; - pa_cvolume pa_volume = pa_volume_; - if (scrolling_) { return false; } + bool direction_up = false; + double volume_tick = (double)PA_VOLUME_NORM / 100; + pa_volume_t change = volume_tick; + pa_cvolume pa_volume = pa_volume_; scrolling_ = true; if (e->direction == GDK_SCROLL_UP) { direction_up = true; @@ -98,6 +98,11 @@ bool waybar::modules::Pulseaudio::handleScroll(GdkEventScroll *e) { } } + // isDouble returns true for integers as well, just in case + if (config_["scroll-step"].isDouble()) { + change = round(config_["scroll-step"].asDouble() * volume_tick); + } + if (direction_up) { if (volume_ + 1 < 100) { pa_cvolume_inc(&pa_volume, change); @@ -148,6 +153,7 @@ void waybar::modules::Pulseaudio::sinkInfoCb(pa_context * /*context*/, const pa_ pa->volume_ = std::round(volume * 100.0F); pa->muted_ = i->mute != 0; pa->desc_ = i->description; + pa->monitor_ = i->monitor_source_name; pa->port_name_ = i->active_port != nullptr ? i->active_port->name : "Unknown"; pa->dp.emit(); } @@ -192,7 +198,7 @@ auto waybar::modules::Pulseaudio::update() -> void { label_.get_style_context()->add_class("muted"); } else { label_.get_style_context()->remove_class("muted"); - if (port_name_.find("a2dp_sink") != std::string::npos) { + if (monitor_.find("a2dp_sink") != std::string::npos) { format = config_["format-bluetooth"].isString() ? config_["format-bluetooth"].asString() : format; label_.get_style_context()->add_class("bluetooth"); diff --git a/src/modules/sni/host.cpp b/src/modules/sni/host.cpp index b9204bcc..62a68a2e 100644 --- a/src/modules/sni/host.cpp +++ b/src/modules/sni/host.cpp @@ -130,8 +130,13 @@ std::tuple Host::getBusNameAndObjectPath(const std::st void Host::addRegisteredItem(std::string service) { auto [bus_name, object_path] = getBusNameAndObjectPath(service); - items_.emplace_back(new Item(bus_name, object_path, config_)); - on_add_(items_.back()); + auto it = std::find_if(items_.begin(), items_.end(), [&bus_name, &object_path](const auto& item) { + return bus_name == item->bus_name && object_path == item->object_path; + }); + if (it == items_.end()) { + items_.emplace_back(new Item(bus_name, object_path, config_)); + on_add_(items_.back()); + } } -} \ No newline at end of file +} // namespace waybar::modules::SNI \ No newline at end of file diff --git a/src/modules/sni/item.cpp b/src/modules/sni/item.cpp index cfb5fef0..106b0611 100644 --- a/src/modules/sni/item.cpp +++ b/src/modules/sni/item.cpp @@ -276,39 +276,38 @@ Glib::RefPtr Item::getIconByName(const std::string& name, int reque name.c_str(), tmp_size, Gtk::IconLookupFlags::ICON_LOOKUP_FORCE_SIZE); } -void Item::onMenuDestroyed(Item* self) { - self->gtk_menu = nullptr; - self->dbus_menu = nullptr; +void Item::onMenuDestroyed(Item* self, GObject* old_menu_pointer) { + if (old_menu_pointer == reinterpret_cast(self->dbus_menu)) { + self->gtk_menu = nullptr; + self->dbus_menu = nullptr; + } } -bool Item::makeMenu(GdkEventButton* const& ev) { - if (gtk_menu == nullptr) { - if (!menu.empty()) { - dbus_menu = dbusmenu_gtkmenu_new(bus_name.data(), menu.data()); - if (dbus_menu != nullptr) { - g_object_ref_sink(G_OBJECT(dbus_menu)); - g_object_weak_ref(G_OBJECT(dbus_menu), (GWeakNotify)onMenuDestroyed, this); - gtk_menu = Glib::wrap(GTK_MENU(dbus_menu)); - gtk_menu->attach_to_widget(event_box); - } +void Item::makeMenu(GdkEventButton* const& ev) { + if (gtk_menu == nullptr && !menu.empty()) { + dbus_menu = dbusmenu_gtkmenu_new(bus_name.data(), menu.data()); + if (dbus_menu != nullptr) { + g_object_ref_sink(G_OBJECT(dbus_menu)); + g_object_weak_ref(G_OBJECT(dbus_menu), (GWeakNotify)onMenuDestroyed, this); + gtk_menu = Glib::wrap(GTK_MENU(dbus_menu)); + gtk_menu->attach_to_widget(event_box); } } - if (gtk_menu != nullptr) { -#if GTK_CHECK_VERSION(3, 22, 0) - gtk_menu->popup_at_pointer(reinterpret_cast(ev)); -#else - gtk_menu->popup(ev->button, ev->time); -#endif - return true; - } - return false; } bool Item::handleClick(GdkEventButton* const& ev) { auto parameters = Glib::VariantContainerBase::create_tuple( {Glib::Variant::create(ev->x), Glib::Variant::create(ev->y)}); if ((ev->button == 1 && item_is_menu) || ev->button == 3) { - if (!makeMenu(ev)) { + makeMenu(ev); + if (gtk_menu != nullptr) { +#if GTK_CHECK_VERSION(3, 22, 0) + gtk_menu->popup_at_pointer(reinterpret_cast(ev)); +#else + gtk_menu->popup(ev->button, ev->time); +#endif + return true; + } else { proxy_->call("ContextMenu", parameters); return true; } diff --git a/src/modules/sni/watcher.cpp b/src/modules/sni/watcher.cpp index 3937d15a..596a06d4 100644 --- a/src/modules/sni/watcher.cpp +++ b/src/modules/sni/watcher.cpp @@ -14,11 +14,6 @@ Watcher::Watcher() watcher_(sn_watcher_skeleton_new()) {} Watcher::~Watcher() { - if (bus_name_id_ != 0) { - Gio::DBus::unown_name(bus_name_id_); - bus_name_id_ = 0; - } - if (hosts_ != nullptr) { g_slist_free_full(hosts_, gfWatchFree); hosts_ = nullptr; @@ -28,7 +23,8 @@ Watcher::~Watcher() { g_slist_free_full(items_, gfWatchFree); items_ = nullptr; } - g_dbus_interface_skeleton_unexport(G_DBUS_INTERFACE_SKELETON(watcher_)); + auto iface = G_DBUS_INTERFACE_SKELETON(watcher_); + g_dbus_interface_skeleton_unexport(iface); } void Watcher::busAcquired(const Glib::RefPtr& conn, Glib::ustring name) { diff --git a/src/modules/sway/window.cpp b/src/modules/sway/window.cpp index 2f0287fd..c17f5fea 100644 --- a/src/modules/sway/window.cpp +++ b/src/modules/sway/window.cpp @@ -25,16 +25,23 @@ void Window::onEvent(const struct Ipc::ipc_response& res) { getTree(); } void Window::onCmd(const struct Ipc::ipc_response& res) { try { + std::lock_guard lock(mutex_); auto payload = parser_.parse(res.payload); auto [nb, id, name, app_id] = getFocusedNode(payload); if (!app_id_.empty()) { bar_.window.get_style_context()->remove_class(app_id_); } if (nb == 0) { - bar_.window.get_style_context()->add_class("empty"); + bar_.window.get_style_context()->remove_class("solo"); + if (!bar_.window.get_style_context()->has_class("empty")) { + bar_.window.get_style_context()->add_class("empty"); + } } else if (nb == 1) { - bar_.window.get_style_context()->add_class("solo"); - if (!app_id.empty()) { + bar_.window.get_style_context()->remove_class("empty"); + if (!bar_.window.get_style_context()->has_class("solo")) { + bar_.window.get_style_context()->add_class("solo"); + } + if (!app_id.empty() && !bar_.window.get_style_context()->has_class(app_id)) { bar_.window.get_style_context()->add_class(app_id); } } else { @@ -99,4 +106,4 @@ void Window::getTree() { } } -} // namespace waybar::modules::sway \ No newline at end of file +} // namespace waybar::modules::sway diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index 50be8d94..a623b411 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -15,11 +15,22 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value ipc_.signal_event.connect(sigc::mem_fun(*this, &Workspaces::onEvent)); ipc_.signal_cmd.connect(sigc::mem_fun(*this, &Workspaces::onCmd)); ipc_.sendCmd(IPC_GET_WORKSPACES); + if (!config["disable-bar-scroll"].asBool()) { + auto &window = const_cast(bar_).window; + window.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK); + window.signal_scroll_event().connect(sigc::mem_fun(*this, &Workspaces::handleScroll)); + } // Launch worker worker(); } -void Workspaces::onEvent(const struct Ipc::ipc_response &res) { ipc_.sendCmd(IPC_GET_WORKSPACES); } +void Workspaces::onEvent(const struct Ipc::ipc_response &res) { + try { + ipc_.sendCmd(IPC_GET_WORKSPACES); + } catch (const std::exception &e) { + std::cerr << "Workspaces: " << e.what() << std::endl; + } +} void Workspaces::onCmd(const struct Ipc::ipc_response &res) { if (res.type == IPC_GET_WORKSPACES) { @@ -194,7 +205,11 @@ bool Workspaces::handleScroll(GdkEventScroll *e) { return false; } } - ipc_.sendCmd(IPC_COMMAND, fmt::format("workspace \"{}\"", name)); + try { + ipc_.sendCmd(IPC_COMMAND, fmt::format("workspace \"{}\"", name)); + } catch (const std::exception &e) { + std::cerr << "Workspaces: " << e.what() << std::endl; + } return true; } @@ -208,7 +223,7 @@ const std::string Workspaces::getCycleWorkspace(std::vector::iterat else if (!prev && it != workspaces_.end()) ++it; if (!prev && it == workspaces_.end()) { - return (*(++workspaces_.begin()))["name"].asString(); + return (*(workspaces_.begin()))["name"].asString(); } return (*it)["name"].asString(); } @@ -235,4 +250,4 @@ void Workspaces::onButtonReady(const Json::Value &node, Gtk::Button &button) { Workspaces::operator Gtk::Widget &() { return box_; } -} // namespace waybar::modules::sway \ No newline at end of file +} // namespace waybar::modules::sway diff --git a/src/modules/temperature.cpp b/src/modules/temperature.cpp index 72c2880c..9e6bff79 100644 --- a/src/modules/temperature.cpp +++ b/src/modules/temperature.cpp @@ -8,11 +8,8 @@ waybar::modules::Temperature::Temperature(const std::string& id, const Json::Val auto zone = config_["thermal-zone"].isInt() ? config_["thermal-zone"].asInt() : 0; file_path_ = fmt::format("/sys/class/thermal/thermal_zone{}/temp", zone); } -#ifdef FILESYSTEM_EXPERIMENTAL - if (!std::experimental::filesystem::exists(file_path_)) { -#else - if (!std::filesystem::exists(file_path_)) { -#endif + std::ifstream temp(file_path_); + if (!temp.is_open()) { throw std::runtime_error("Can't open " + file_path_); } label_.set_name("temperature"); @@ -35,8 +32,11 @@ auto waybar::modules::Temperature::update() -> void { } else { label_.get_style_context()->remove_class("critical"); } - label_.set_markup(fmt::format( - format, fmt::arg("temperatureC", temperature_c), fmt::arg("temperatureF", temperature_f))); + auto max_temp = config_["critical-threshold"].isInt() ? config_["critical-threshold"].asInt() : 0; + label_.set_markup(fmt::format(format, + fmt::arg("temperatureC", temperature_c), + fmt::arg("temperatureF", temperature_f), + fmt::arg("icon", getIcon(temperature_c, "", max_temp)))); } std::tuple waybar::modules::Temperature::getTemperature() {