Compare commits

...

85 Commits
0.6.1 ... 0.6.5

Author SHA1 Message Date
6ffc7ee3b3 chore: 0.6.5 2019-05-18 16:12:19 +02:00
ff28de0482 feat(custom): update on click/scroll 2019-05-18 16:07:55 +02:00
d5e8a37e63 Adding bandwidth support for network module (#328)
Adding bandwidth support for network module
2019-05-18 15:52:37 +02:00
67786c32a8 fix(merge): re-add missing code 2019-05-18 15:45:18 +02:00
93a644eec4 Merge branch 'master' into master 2019-05-18 15:42:27 +02:00
aa385e28b6 refactor: execute update on idle 2019-05-18 15:32:40 +02:00
2c1a3d0430 Adding logging 2019-05-18 09:27:47 -04:00
b31a64ad00 Displaying in ko/s and kb/s instead of interval dependant unit 2019-05-18 09:13:00 -04:00
4865a9ad6c fix(network): reset frequency 2019-05-18 13:57:50 +02:00
e70d8aff73 Merge pull request #327 from RX14/feature/disable-scroll-wraparound-option
Add option to disable scroll wraparound on workspaces
2019-05-18 13:50:35 +02:00
3e1c77d158 Add option to disable scroll wraparound on workspaces 2019-05-18 12:15:35 +01:00
d34c3a801c fix(Network): less updates 2019-05-18 12:27:10 +02:00
794fb12e8c Adding bandwidth support for network module [linux only] 2019-05-17 23:39:51 -04:00
43d724ebad Merge pull request #326 from RX14/rename-bar-scroll
Rename the "disable-workspace-scroll" option to "disable-bar-scroll"
2019-05-17 20:24:15 +02:00
c862fde986 Merge pull request #325 from RX14/bugfix/scroll-broken
Fix workspace scroll wrapping off the end of the list
2019-05-17 20:23:51 +02:00
0d59f7b7d1 Rename the "disable-workspace-scroll" option to "disable-bar-scroll" 2019-05-17 17:42:11 +01:00
1e95f5d9b6 Fix workspace scroll wrapping off the end of the list 2019-05-17 17:37:24 +01:00
9234be8544 revert: re-add rountrip before widgets setup 2019-05-17 14:45:02 +02:00
9d3255fe9f fix: remove redundant roundtrip 2019-05-17 14:41:12 +02:00
d2d9db23b5 fix: uninitialized bool 2019-05-17 14:23:52 +02:00
d8be72e4b6 refactor: unexport tray watcher 2019-05-17 13:51:55 +02:00
f8a47598ba fix: roundtrip before bar creation 2019-05-17 13:40:04 +02:00
cb2d6e1997 feat(Network): frequency 2019-05-17 11:27:38 +02:00
17291dffdf fix(Battery): plugged state 2019-05-17 10:59:54 +02:00
9a091d7740 chore: 0.6.4 2019-05-17 10:18:05 +02:00
2cb70c7324 Merge pull request #322 from RX14/feature/generic-scroll
Allow scrolling on the entire bar surface
2019-05-17 10:03:52 +02:00
4d4cadb5ae refactor: simpler code 2019-05-17 09:59:37 +02:00
cddee2626a Change scroll-step unit to percent (#316)
Change scroll-step unit to percent
2019-05-17 09:44:34 +02:00
d5c1e6f312 Change scroll-step unit to percent 2019-05-17 11:19:58 +08:00
b45dcdf74e Allow scrolling on the entire bar surface 2019-05-16 22:18:43 +01:00
a1ffa7fa9f chore: enable alpine on Travis 2019-05-16 17:20:27 +02:00
4b4b74db0c feat(Battery): get icon by state 2019-05-16 17:18:27 +02:00
8901df9702 chore: add alpine dockerfile 2019-05-16 17:10:35 +02:00
e12766a656 fix: compilation on some os 2019-05-16 17:09:25 +02:00
31f63398dc Merge pull request #319 from Alexays/network
Network improvements
2019-05-16 14:11:32 +02:00
0a14e7f3ab feat: ipv6 family 2019-05-16 12:22:08 +02:00
31416ffae6 fix: bar removed 2019-05-16 12:19:47 +02:00
fbe19d886a fix: drop memberships 2019-05-16 12:16:44 +02:00
9c67150884 refactor: prepare ipv6 2019-05-16 12:14:12 +02:00
f3c467cc46 refactor: remove non wanted headers 2019-05-16 11:27:22 +02:00
a09d2222be style: remove non wanted tags 2019-05-16 11:26:48 +02:00
45ebee52a6 fix: typo 2019-05-16 11:26:06 +02:00
841576497a refactor: cleaner events 2019-05-16 11:22:22 +02:00
e730105950 Merge pull request #318 from unresolvedsymbol/patch
Fix state behavior
2019-05-16 09:40:09 +02:00
963d4f68e4 refactor: remove useless param 2019-05-16 09:39:06 +02:00
7e8eee0571 fix state behavior 2019-05-15 22:14:51 -05:00
37f87be9dd Merge pull request #317 from jorgenbele/master
fix(battery): change comparison expr. to assignment
2019-05-15 21:02:04 +02:00
380fc58f3c fix(battery): change comparison expr. to assignment 2019-05-15 19:19:00 +02:00
22bf0b161a fix(Network): do not stop thread 2019-05-15 10:24:35 +02:00
5b3402e110 feat(Battery): plugged status 2019-05-14 15:43:57 +02:00
d209d350fe style(media): reduce min-width 2019-05-13 15:48:18 +02:00
0968170074 style(media): min-width 2019-05-13 15:40:02 +02:00
362c393b1d refactor: try/catch, sigc trackable 2019-05-13 15:15:50 +02:00
0c3c548bc0 fix(Window): avoid concurrency 2019-05-13 14:35:45 +02:00
b54160e02f fix(Tray): add item if not exist 2019-05-13 14:27:01 +02:00
db14fac038 style: remove chromium style 2019-05-13 13:23:32 +02:00
4f1defe6d5 fix(Pulseaudio): avoid handleScroll override 2019-05-13 11:46:12 +02:00
92967c7c06 fix(Label): reverse only battery states 2019-05-13 11:36:34 +02:00
fcb23d3104 feat(temperature): format-icons 2019-05-13 11:31:05 +02:00
62f8af8a39 fix(Window): avoid multiple same classes 2019-05-13 10:56:48 +02:00
d5a9eccb7b chore: v0.6.3 2019-05-12 20:02:53 +02:00
80e9ea746b fix(battery): use path for the / operator 2019-05-12 19:53:22 +02:00
84728f6fab Merge pull request #313 from apiote/master
remove empty and solo classes when they do not apply
2019-05-12 12:13:07 +02:00
10a17187a1 Merge pull request #309 from dlasky/btaudio
fix(btformat): fixes an issue where btformat was not detected
2019-05-12 12:12:16 +02:00
7cdde05568 remove empty and solo classes when they do not apply 2019-05-11 20:36:10 +02:00
Dan
e343cf4b00 fix(btformat): fixes an issue where btformat was not being correctly detected 2019-05-10 12:07:17 -04:00
e4756cf24e refactor: don't print an error when a watcher is already present 2019-05-10 14:56:28 +02:00
131dae5818 chore: v0.6.2 2019-05-10 13:40:45 +02:00
4688002f23 feat: margins 2019-05-09 15:10:13 +02:00
5bf0ca85ac refactor: try/catch around json parse 2019-05-09 10:30:54 +02:00
fd9b34adf8 chore: add fedora to travis 2019-05-07 14:04:45 +02:00
8a011e6d90 refactor(Dockerfile): fix fedora 2019-05-07 13:45:49 +02:00
5a44c8c6de refactor: avoid unneeded json parsing 2019-05-07 13:43:48 +02:00
74137befba fix(window): title flickers 2019-05-07 13:31:41 +02:00
b75e0bb0d0 refactor: remove useless bar param 2019-05-07 13:21:18 +02:00
77d9dd06af chore: fedora dockerfile 2019-05-07 13:15:59 +02:00
e5d5735e9d feat(percent): adds a percent class to numeric modules (#297)
feat(percent): adds a percent class to numeric modules
2019-05-03 17:35:21 +02:00
Dan
dd0ebe117c chore(cr): cr cleanup 2019-05-03 08:08:55 -04:00
Dan
5f0a3063d1 feat(states): add getState to other percent based modules 2019-05-02 22:24:29 -04:00
Dan
3bac96945c Revert "feat(percent): adds a percent class to numeric modules"
This reverts commit 82302e58f3b611f7ff6d686d1783b1c32914f7c9.
2019-05-02 22:24:29 -04:00
Dan
e158a3e132 feat(states): add generic 'states' to all labels 2019-05-02 22:24:29 -04:00
Dan
472363a623 feat(percent): adds a percent class to numeric modules 2019-05-02 22:24:29 -04:00
1a024db03c fix(idle_inhibitor): overload 2019-05-02 17:51:01 +02:00
5623bbecfe feat(idle_inhibitor): pass status to click events exec 2019-05-02 16:56:45 +02:00
1e871b2353 refactor: propagate the event further 2019-05-02 16:46:53 +02:00
42 changed files with 829 additions and 424 deletions

View File

@ -6,7 +6,9 @@ services:
env: env:
- distro: debian - distro: debian
- distro: archlinux - distro: archlinux
# - distro: opensuse - distro: opensuse
- distro: fedora
- distro: alpine
before_install: before_install:
- docker pull alexays/waybar:${distro} - docker pull alexays/waybar:${distro}
@ -15,4 +17,4 @@ script:
- echo FROM alexays/waybar:${distro} > Dockerfile - echo FROM alexays/waybar:${distro} > Dockerfile
- echo ADD . /root >> Dockerfile - echo ADD . /root >> Dockerfile
- docker build -t waybar . - docker build -t waybar .
- docker run waybar /bin/sh -c "cd /root && git clone https://github.com/swaywm/wlroots subprojects/wlroots && meson build && ninja -C build" - docker run waybar /bin/sh -c "cd /root && git clone --depth=1 https://github.com/swaywm/wlroots subprojects/wlroots && meson build && ninja -C build"

3
Dockerfiles/alpine Normal file
View File

@ -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

5
Dockerfiles/fedora Normal file
View File

@ -0,0 +1,5 @@
FROM fedora:30
RUN dnf install sway meson git libinput-devel wayland-devel wayland-protocols-devel egl-wayland-devel mesa-libEGL-devel mesa-libGLES-devel mesa-libgbm-devel libxkbcommon-devel libudev-devel pixman-devel gtkmm30-devel jsoncpp-devel -y && \
dnf group install "C Development Tools and Libraries" -y && \
dnf clean all -y

View File

@ -13,7 +13,7 @@ class ALabel : public IModule {
ALabel(const Json::Value &, const std::string &format, uint16_t interval = 0); ALabel(const Json::Value &, const std::string &format, uint16_t interval = 0);
virtual ~ALabel(); virtual ~ALabel();
virtual auto update() -> void; 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 &(); virtual operator Gtk::Widget &();
protected: protected:
@ -23,6 +23,7 @@ class ALabel : public IModule {
Gtk::Label label_; Gtk::Label label_;
const Json::Value & config_; const Json::Value & config_;
std::string format_; std::string format_;
std::string click_param;
std::mutex mutex_; std::mutex mutex_;
const std::chrono::seconds interval_; const std::chrono::seconds interval_;
bool alt_ = false; bool alt_ = false;
@ -30,6 +31,7 @@ class ALabel : public IModule {
virtual bool handleToggle(GdkEventButton *const &ev); virtual bool handleToggle(GdkEventButton *const &ev);
virtual bool handleScroll(GdkEventScroll *); virtual bool handleScroll(GdkEventScroll *);
virtual std::string getState(uint8_t value, bool lesser = false);
private: private:
std::vector<int> pid_; std::vector<int> pid_;

View File

@ -22,7 +22,7 @@ struct waybar_output {
class Bar { class Bar {
public: public:
Bar(struct waybar_output *w_output, const Json::Value&); Bar(struct waybar_output *w_output, const Json::Value &);
Bar(const Bar &) = delete; Bar(const Bar &) = delete;
~Bar() = default; ~Bar() = default;
@ -51,12 +51,19 @@ class Bar {
static void layerSurfaceHandleClosed(void *, struct zwlr_layer_surface_v1 *); static void layerSurfaceHandleClosed(void *, struct zwlr_layer_surface_v1 *);
void destroyOutput(); void destroyOutput();
void onWindowRealize(); void onConfigure(GdkEventConfigure *ev);
void setMarginsAndZone(uint32_t height, uint32_t width);
auto setupWidgets() -> void; auto setupWidgets() -> void;
void getModules(const Factory &, const std::string &); void getModules(const Factory &, const std::string &);
void setupAltFormatKeyForModule(const std::string &module_name); void setupAltFormatKeyForModule(const std::string &module_name);
void setupAltFormatKeyForModuleList(const char *module_list_name); void setupAltFormatKeyForModuleList(const char *module_list_name);
struct margins {
int top = 0;
int right = 0;
int bottom = 0;
int left = 0;
} margins_;
uint32_t width_ = 0; uint32_t width_ = 0;
uint32_t height_ = 1; uint32_t height_ = 1;
Gtk::Box left_; Gtk::Box left_;

View File

@ -7,11 +7,13 @@
#include "modules/sway/window.hpp" #include "modules/sway/window.hpp"
#include "modules/sway/workspaces.hpp" #include "modules/sway/workspaces.hpp"
#endif #endif
#ifndef NO_FILESYSTEM
#include "modules/battery.hpp" #include "modules/battery.hpp"
#endif
#include "modules/cpu.hpp" #include "modules/cpu.hpp"
#include "modules/idle_inhibitor.hpp" #include "modules/idle_inhibitor.hpp"
#include "modules/memory.hpp" #include "modules/memory.hpp"
#ifdef HAVE_DBUSMENU #if defined(HAVE_DBUSMENU) && !defined(NO_FILESYSTEM)
#include "modules/sni/tray.hpp" #include "modules/sni/tray.hpp"
#endif #endif
#ifdef HAVE_LIBNL #ifdef HAVE_LIBNL

View File

@ -30,11 +30,10 @@ class Battery : public ALabel {
private: private:
static inline const fs::path data_dir_ = "/sys/class/power_supply/"; static inline const fs::path data_dir_ = "/sys/class/power_supply/";
void getBatteries(); void getBatteries();
void worker(); void worker();
const std::string getAdapterStatus(uint8_t capacity) const; const std::string getAdapterStatus(uint8_t capacity, uint32_t current_now) const;
const std::tuple<uint8_t, std::string> getInfos() const; const std::tuple<uint8_t, uint32_t, std::string> getInfos() const;
const std::string getState(uint8_t) const;
util::SleeperThread thread_; util::SleeperThread thread_;
util::SleeperThread thread_timer_; util::SleeperThread thread_timer_;

View File

@ -22,6 +22,8 @@ class Custom : public ALabel {
void continuousWorker(); void continuousWorker();
void parseOutputRaw(); void parseOutputRaw();
void parseOutputJson(); void parseOutputJson();
bool handleScroll(GdkEventScroll* e);
bool handleToggle(GdkEventButton* const& e);
const std::string name_; const std::string name_;
std::string text_; std::string text_;

View File

@ -28,7 +28,6 @@ class Network : public ALabel {
static int handleScan(struct nl_msg*, void*); static int handleScan(struct nl_msg*, void*);
void worker(); void worker();
void disconnected();
void createInfoSocket(); void createInfoSocket();
void createEventSocket(); void createEventSocket();
int getExternalInterface(); int getExternalInterface();
@ -37,27 +36,38 @@ class Network : public ALabel {
int netlinkResponse(void*, uint32_t, uint32_t groups = 0); int netlinkResponse(void*, uint32_t, uint32_t groups = 0);
void parseEssid(struct nlattr**); void parseEssid(struct nlattr**);
void parseSignal(struct nlattr**); void parseSignal(struct nlattr**);
void parseFreq(struct nlattr**);
bool associatedOrJoined(struct nlattr**); bool associatedOrJoined(struct nlattr**);
bool checkInterface(struct ifinfomsg *rtif, std::string name);
int getPreferredIface();
auto getInfo() -> void; auto getInfo() -> void;
bool wildcardMatch(const std::string& pattern, const std::string& text);
waybar::util::SleeperThread thread_; waybar::util::SleeperThread thread_;
waybar::util::SleeperThread thread_timer_; waybar::util::SleeperThread thread_timer_;
int ifid_; int ifid_;
int last_ext_iface_;
sa_family_t family_; sa_family_t family_;
struct sockaddr_nl nladdr_ = {0}; struct sockaddr_nl nladdr_ = {0};
struct nl_sock* sk_ = nullptr; struct nl_sock* sock_ = nullptr;
struct nl_sock* info_sock_ = nullptr; struct nl_sock* ev_sock_ = nullptr;
int efd_; int efd_;
int ev_fd_; int ev_fd_;
int nl80211_id_; int nl80211_id_;
std::mutex mutex_;
unsigned long long bandwidth_down_total_;
unsigned long long bandwidth_up_total_;
std::string essid_; std::string essid_;
std::string ifname_; std::string ifname_;
std::string ipaddr_; std::string ipaddr_;
std::string netmask_; std::string netmask_;
int cidr_; int cidr_;
bool linked_;
int32_t signal_strength_dbm_; int32_t signal_strength_dbm_;
uint8_t signal_strength_; uint8_t signal_strength_;
uint32_t frequency_;
}; };
} // namespace waybar::modules } // namespace waybar::modules

View File

@ -20,7 +20,7 @@ class Pulseaudio : public ALabel {
static void sinkInfoCb(pa_context*, const pa_sink_info*, int, void*); static void sinkInfoCb(pa_context*, const pa_sink_info*, int, void*);
static void serverInfoCb(pa_context*, const pa_server_info*, void*); static void serverInfoCb(pa_context*, const pa_server_info*, void*);
static void volumeModifyCb(pa_context*, int, void*); static void volumeModifyCb(pa_context*, int, void*);
bool handleScroll(GdkEventScroll* e); bool handleVolume(GdkEventScroll* e);
const std::string getPortIcon() const; const std::string getPortIcon() const;
@ -33,6 +33,7 @@ class Pulseaudio : public ALabel {
bool muted_; bool muted_;
std::string port_name_; std::string port_name_;
std::string desc_; std::string desc_;
std::string monitor_;
bool scrolling_; bool scrolling_;
}; };

View File

@ -46,7 +46,7 @@ class Item : public sigc::trackable {
std::string menu; std::string menu;
DbusmenuGtkMenu* dbus_menu = nullptr; DbusmenuGtkMenu* dbus_menu = nullptr;
Gtk::Menu* gtk_menu = nullptr; Gtk::Menu* gtk_menu = nullptr;
bool item_is_menu; bool item_is_menu = false;
private: private:
void proxyReady(Glib::RefPtr<Gio::AsyncResult>& result); void proxyReady(Glib::RefPtr<Gio::AsyncResult>& result);
@ -59,12 +59,12 @@ class Item : public sigc::trackable {
void updateImage(); void updateImage();
Glib::RefPtr<Gdk::Pixbuf> extractPixBuf(GVariant* variant); Glib::RefPtr<Gdk::Pixbuf> extractPixBuf(GVariant* variant);
Glib::RefPtr<Gdk::Pixbuf> getIconByName(const std::string& name, int size); Glib::RefPtr<Gdk::Pixbuf> getIconByName(const std::string& name, int size);
static void onMenuDestroyed(Item* self); static void onMenuDestroyed(Item* self, GObject* old_menu_pointer);
bool makeMenu(GdkEventButton* const& ev); void makeMenu(GdkEventButton* const& ev);
bool handleClick(GdkEventButton* const& /*ev*/); bool handleClick(GdkEventButton* const& /*ev*/);
Glib::RefPtr<Gio::Cancellable> cancellable_;
Glib::RefPtr<Gio::DBus::Proxy> proxy_; Glib::RefPtr<Gio::DBus::Proxy> proxy_;
Glib::RefPtr<Gio::Cancellable> cancellable_;
bool update_pending_; bool update_pending_;
}; };

View File

@ -9,7 +9,6 @@
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include "ipc.hpp" #include "ipc.hpp"
#include "util/json.hpp"
namespace waybar::modules::sway { namespace waybar::modules::sway {
@ -21,11 +20,11 @@ class Ipc {
struct ipc_response { struct ipc_response {
uint32_t size; uint32_t size;
uint32_t type; uint32_t type;
Json::Value payload; std::string payload;
}; };
sigc::signal<void, const struct ipc_response&> signal_event; sigc::signal<void, const struct ipc_response &> signal_event;
sigc::signal<void, const struct ipc_response&> signal_cmd; sigc::signal<void, const struct ipc_response &> signal_cmd;
void sendCmd(uint32_t type, const std::string &payload = ""); void sendCmd(uint32_t type, const std::string &payload = "");
void subscribe(const std::string &payload); void subscribe(const std::string &payload);
@ -40,12 +39,9 @@ class Ipc {
struct ipc_response send(int fd, uint32_t type, const std::string &payload = ""); struct ipc_response send(int fd, uint32_t type, const std::string &payload = "");
struct ipc_response recv(int fd); struct ipc_response recv(int fd);
int fd_; int fd_;
int fd_event_; int fd_event_;
std::mutex mutex_; std::mutex mutex_;
std::mutex mutex_event_;
std::mutex mutex_parser_;
util::JsonParser parser_;
}; };
} // namespace waybar::modules::sway } // namespace waybar::modules::sway

View File

@ -6,12 +6,13 @@
#include "client.hpp" #include "client.hpp"
#include "modules/sway/ipc/client.hpp" #include "modules/sway/ipc/client.hpp"
#include "util/sleeper_thread.hpp" #include "util/sleeper_thread.hpp"
#include "util/json.hpp"
namespace waybar::modules::sway { namespace waybar::modules::sway {
class Mode : public ALabel { class Mode : public ALabel, public sigc::trackable {
public: public:
Mode(const std::string&, const waybar::Bar&, const Json::Value&); Mode(const std::string&, const Json::Value&);
~Mode() = default; ~Mode() = default;
auto update() -> void; auto update() -> void;
@ -19,10 +20,10 @@ class Mode : public ALabel {
void onEvent(const struct Ipc::ipc_response&); void onEvent(const struct Ipc::ipc_response&);
void worker(); void worker();
const Bar& bar_;
waybar::util::SleeperThread thread_; waybar::util::SleeperThread thread_;
Ipc ipc_; Ipc ipc_;
std::string mode_; std::string mode_;
util::JsonParser parser_;
}; };
} // namespace waybar::modules::sway } // namespace waybar::modules::sway

View File

@ -6,11 +6,12 @@
#include "bar.hpp" #include "bar.hpp"
#include "client.hpp" #include "client.hpp"
#include "modules/sway/ipc/client.hpp" #include "modules/sway/ipc/client.hpp"
#include "util/json.hpp"
#include "util/sleeper_thread.hpp" #include "util/sleeper_thread.hpp"
namespace waybar::modules::sway { namespace waybar::modules::sway {
class Window : public ALabel { class Window : public ALabel, public sigc::trackable {
public: public:
Window(const std::string&, const waybar::Bar&, const Json::Value&); Window(const std::string&, const waybar::Bar&, const Json::Value&);
~Window() = default; ~Window() = default;
@ -26,9 +27,11 @@ class Window : public ALabel {
const Bar& bar_; const Bar& bar_;
waybar::util::SleeperThread thread_; waybar::util::SleeperThread thread_;
Ipc ipc_; Ipc ipc_;
std::mutex mutex_;
std::string window_; std::string window_;
int windowId_; int windowId_;
std::string app_id_; std::string app_id_;
util::JsonParser parser_;
}; };
} // namespace waybar::modules::sway } // namespace waybar::modules::sway

View File

@ -7,11 +7,12 @@
#include "bar.hpp" #include "bar.hpp"
#include "client.hpp" #include "client.hpp"
#include "modules/sway/ipc/client.hpp" #include "modules/sway/ipc/client.hpp"
#include "util/json.hpp"
#include "util/sleeper_thread.hpp" #include "util/sleeper_thread.hpp"
namespace waybar::modules::sway { namespace waybar::modules::sway {
class Workspaces : public IModule { class Workspaces : public IModule, public sigc::trackable {
public: public:
Workspaces(const std::string&, const waybar::Bar&, const Json::Value&); Workspaces(const std::string&, const waybar::Bar&, const Json::Value&);
~Workspaces() = default; ~Workspaces() = default;
@ -26,10 +27,10 @@ class Workspaces : public IModule {
Gtk::Button& addButton(const Json::Value&); Gtk::Button& addButton(const Json::Value&);
void onButtonReady(const Json::Value&, Gtk::Button&); void onButtonReady(const Json::Value&, Gtk::Button&);
std::string getIcon(const std::string&, const Json::Value&); std::string getIcon(const std::string&, const Json::Value&);
bool handleScroll(GdkEventScroll*);
const std::string getCycleWorkspace(std::vector<Json::Value>::iterator, bool prev) const; const std::string getCycleWorkspace(std::vector<Json::Value>::iterator, bool prev) const;
uint16_t getWorkspaceIndex(const std::string& name) const; uint16_t getWorkspaceIndex(const std::string& name) const;
std::string trimWorkspaceName(std::string); std::string trimWorkspaceName(std::string);
bool handleScroll(GdkEventScroll*);
const Bar& bar_; const Bar& bar_;
const Json::Value& config_; const Json::Value& config_;
@ -38,6 +39,7 @@ class Workspaces : public IModule {
std::mutex mutex_; std::mutex mutex_;
Gtk::Box box_; Gtk::Box box_;
Ipc ipc_; Ipc ipc_;
util::JsonParser parser_;
bool scrolling_; bool scrolling_;
std::unordered_map<std::string, Gtk::Button> buttons_; std::unordered_map<std::string, Gtk::Button> buttons_;
}; };

View File

@ -4,11 +4,6 @@
#include <fstream> #include <fstream>
#include "ALabel.hpp" #include "ALabel.hpp"
#include "util/sleeper_thread.hpp" #include "util/sleeper_thread.hpp"
#ifdef FILESYSTEM_EXPERIMENTAL
#include <experimental/filesystem>
#else
#include <filesystem>
#endif
namespace waybar::modules { namespace waybar::modules {

View File

@ -5,15 +5,16 @@
namespace waybar::util { namespace waybar::util {
struct JsonParser { struct JsonParser {
JsonParser() : reader_(builder_.newCharReader()) {} JsonParser() {}
const Json::Value parse(const std::string& data) const { const Json::Value parse(const std::string& data) const {
Json::Value root(Json::objectValue); Json::Value root(Json::objectValue);
if (data.empty()) { if (data.empty()) {
return root; return root;
} }
std::string err; std::unique_ptr<Json::CharReader> const reader(builder_.newCharReader());
bool res = reader_->parse(data.c_str(), data.c_str() + data.size(), &root, &err); std::string err;
bool res = reader->parse(data.c_str(), data.c_str() + data.size(), &root, &err);
if (!res) throw std::runtime_error(err); if (!res) throw std::runtime_error(err);
return root; return root;
} }
@ -21,8 +22,7 @@ struct JsonParser {
~JsonParser() = default; ~JsonParser() = default;
private: private:
Json::CharReaderBuilder builder_; Json::CharReaderBuilder builder_;
std::unique_ptr<Json::CharReader> const reader_;
}; };
} // namespace waybar::util } // namespace waybar::util

View File

@ -1,6 +1,6 @@
project( project(
'waybar', 'cpp', 'c', 'waybar', 'cpp', 'c',
version: '0.6.1', version: '0.6.5',
license: 'MIT', license: 'MIT',
default_options : [ default_options : [
'cpp_std=c++17', 'cpp_std=c++17',
@ -12,7 +12,7 @@ project(
cpp_args = [] cpp_args = []
cpp_link_args = [] cpp_link_args = []
if false # libc++ if get_option('libcxx')
cpp_args += ['-stdlib=libc++'] cpp_args += ['-stdlib=libc++']
cpp_link_args += ['-stdlib=libc++', '-lc++abi'] cpp_link_args += ['-stdlib=libc++', '-lc++abi']
@ -34,7 +34,12 @@ else
endif endif
if not compiler.has_header('filesystem') 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 endif
add_global_arguments(cpp_args, language : 'cpp') add_global_arguments(cpp_args, language : 'cpp')

View File

@ -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('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('libudev', type: 'feature', value: 'auto', description: 'Enable libudev support for udev related features')
option('pulseaudio', type: 'feature', value: 'auto', description: 'Enable support for pulseaudio') option('pulseaudio', type: 'feature', value: 'auto', description: 'Enable support for pulseaudio')

View File

@ -78,8 +78,9 @@
// "thermal-zone": 2, // "thermal-zone": 2,
// "hwmon-path": "/sys/class/hwmon/hwmon2/temp1_input", // "hwmon-path": "/sys/class/hwmon/hwmon2/temp1_input",
"critical-threshold": 80, "critical-threshold": 80,
// "format-critical": "{temperatureC}°C ", // "format-critical": "{temperatureC}°C {icon}",
"format": "{temperatureC}°C " "format": "{temperatureC}°C {icon}",
"format-icons": ["", "", ""]
}, },
"backlight": { "backlight": {
// "device": "acpi_video1", // "device": "acpi_video1",
@ -93,6 +94,8 @@
"critical": 15 "critical": 15
}, },
"format": "{capacity}% {icon}", "format": "{capacity}% {icon}",
"format-charging": "{capacity}% ",
"format-plugged": "{capacity}% ",
// "format-good": "", // An empty format will hide the module // "format-good": "", // An empty format will hide the module
// "format-full": "", // "format-full": "",
"format-icons": ["", "", "", "", ""] "format-icons": ["", "", "", "", ""]
@ -101,16 +104,17 @@
"bat": "BAT2" "bat": "BAT2"
}, },
"network": { "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-wifi": "{essid} ({signalStrength}%) ",
"format-ethernet": "{ifname}: {ipaddr}/{cidr} ", "format-ethernet": "{ifname}: {ipaddr}/{cidr} ",
"format-linked": "{ifname} (No IP) ",
"format-disconnected": "Disconnected ⚠" "format-disconnected": "Disconnected ⚠"
}, },
"pulseaudio": { "pulseaudio": {
//"scroll-step": 1, // "scroll-step": 1, // %, can be a float
"format": "{volume}% {icon}", "format": "{volume}% {icon}",
"format-bluetooth": "{volume}% {icon}", "format-bluetooth": "{volume}% {icon}",
"format-muted": "", "format-muted": "",
"format-icons": { "format-icons": {
"headphones": "", "headphones": "",
"handsfree": "", "handsfree": "",
@ -118,7 +122,7 @@
"phone": "", "phone": "",
"portable": "", "portable": "",
"car": "", "car": "",
"default": ["", ""] "default": ["", "", ""]
}, },
"on-click": "pavucontrol" "on-click": "pavucontrol"
}, },

View File

@ -32,8 +32,7 @@ window#waybar.termite {
} }
window#waybar.chromium { window#waybar.chromium {
background-color: #DEE1E6; background-color: #000000;
color: #000000;
border: none; border: none;
} }
@ -45,19 +44,11 @@ window#waybar.chromium {
border-bottom: 3px solid transparent; border-bottom: 3px solid transparent;
} }
window#waybar.chromium #workspaces button {
color: #3F3F3F;
}
#workspaces button.focused { #workspaces button.focused {
background: #64727D; background: #64727D;
border-bottom: 3px solid #ffffff; border-bottom: 3px solid #ffffff;
} }
window#waybar.chromium #workspaces button.focused {
color: #ffffff;
}
#workspaces button.urgent { #workspaces button.urgent {
background-color: #eb4d4b; background-color: #eb4d4b;
} }
@ -142,6 +133,7 @@ label:focus {
#custom-media { #custom-media {
background: #66cc99; background: #66cc99;
color: #2a5c45; color: #2a5c45;
min-width: 100px;
} }
.custom-spotify { .custom-spotify {

View File

@ -1,8 +1,7 @@
#include "ALabel.hpp" #include "ALabel.hpp"
#include <fmt/format.h>
#include <util/command.hpp> #include <util/command.hpp>
#include <iostream>
waybar::ALabel::ALabel(const Json::Value& config, const std::string& format, uint16_t interval) waybar::ALabel::ALabel(const Json::Value& config, const std::string& format, uint16_t interval)
: config_(config), : config_(config),
format_(config_["format"].isString() ? config_["format"].asString() : format), format_(config_["format"].isString() ? config_["format"].asString() : format),
@ -27,7 +26,9 @@ waybar::ALabel::ALabel(const Json::Value& config, const std::string& format, uin
} }
// configure events' user commands // configure events' user commands
if (config_["on-click"].isString() || config_["on-click-right"].isString()) { if (config_["on-click"].isString() || config_["on-click-middle"].isString() ||
config_["on-click-backward"].isString() || config_["on-click-forward"].isString() ||
config_["on-click-right"].isString()) {
event_box_.add_events(Gdk::BUTTON_PRESS_MASK); event_box_.add_events(Gdk::BUTTON_PRESS_MASK);
event_box_.signal_button_press_event().connect(sigc::mem_fun(*this, &ALabel::handleToggle)); event_box_.signal_button_press_event().connect(sigc::mem_fun(*this, &ALabel::handleToggle));
} }
@ -50,16 +51,21 @@ auto waybar::ALabel::update() -> void {
} }
bool waybar::ALabel::handleToggle(GdkEventButton* const& e) { bool waybar::ALabel::handleToggle(GdkEventButton* const& e) {
std::string format;
if (config_["on-click"].isString() && e->button == 1) { if (config_["on-click"].isString() && e->button == 1) {
pid_.push_back(waybar::util::command::forkExec(config_["on-click"].asString())); format = config_["on-click"].asString();
} else if (config_["on-click-middle"].isString() && e->button == 2) { } else if (config_["on-click-middle"].isString() && e->button == 2) {
pid_.push_back(waybar::util::command::forkExec(config_["on-click-middle"].asString())); format = config_["on-click-middle"].asString();
} else if (config_["on-click-right"].isString() && e->button == 3) { } else if (config_["on-click-right"].isString() && e->button == 3) {
pid_.push_back(waybar::util::command::forkExec(config_["on-click-right"].asString())); format = config_["on-click-right"].asString();
} else if (config_["on-click-forward"].isString() && e->button == 8) { } else if (config_["on-click-forward"].isString() && e->button == 8) {
pid_.push_back(waybar::util::command::forkExec(config_["on-click-backward"].asString())); format = config_["on-click-backward"].asString();
} else if (config_["on-click-backward"].isString() && e->button == 9) { } else if (config_["on-click-backward"].isString() && e->button == 9) {
pid_.push_back(waybar::util::command::forkExec(config_["on-click-forward"].asString())); format = config_["on-click-forward"].asString();
}
if (!format.empty()) {
pid_.push_back(
waybar::util::command::forkExec(fmt::format(format, fmt::arg("arg", click_param))));
} }
if (config_["format-alt-click"].isUInt() && e->button == config_["format-alt-click"].asUInt()) { if (config_["format-alt-click"].isUInt() && e->button == config_["format-alt-click"].asUInt()) {
alt_ = !alt_; alt_ = !alt_;
@ -103,7 +109,7 @@ bool waybar::ALabel::handleScroll(GdkEventScroll* e) {
return true; 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"]; auto format_icons = config_["format-icons"];
if (format_icons.isObject()) { if (format_icons.isObject()) {
if (!alt.empty() && (format_icons[alt].isString() || format_icons[alt].isArray())) { if (!alt.empty() && (format_icons[alt].isString() || format_icons[alt].isArray())) {
@ -114,7 +120,7 @@ std::string waybar::ALabel::getIcon(uint16_t percentage, const std::string& alt)
} }
if (format_icons.isArray()) { if (format_icons.isArray()) {
auto size = format_icons.size(); 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]; format_icons = format_icons[idx];
} }
if (format_icons.isString()) { if (format_icons.isString()) {
@ -123,6 +129,35 @@ std::string waybar::ALabel::getIcon(uint16_t percentage, const std::string& alt)
return ""; return "";
} }
std::string waybar::ALabel::getState(uint8_t value, bool lesser) {
if (!config_["states"].isObject()) {
return "";
}
// Get current state
std::vector<std::pair<std::string, uint8_t>> states;
if (config_["states"].isObject()) {
for (auto it = config_["states"].begin(); it != config_["states"].end(); ++it) {
if (it->isUInt() && it.key().isString()) {
states.emplace_back(it.key().asString(), it->asUInt());
}
}
}
// Sort states
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 ((lesser ? value <= state.second : value >= state.second) && valid_state.empty()) {
label_.get_style_context()->add_class(state.first);
valid_state = state.first;
} else {
label_.get_style_context()->remove_class(state.first);
}
}
return valid_state;
}
bool waybar::ALabel::tooltipEnabled() { bool waybar::ALabel::tooltipEnabled() {
return config_["tooltip"].isBool() ? config_["tooltip"].asBool() : true; return config_["tooltip"].isBool() ? config_["tooltip"].asBool() : true;
} }

View File

@ -20,7 +20,6 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config)
height_ = 0; height_ = 0;
width_ = 1; width_ = 1;
} }
window.set_size_request(width_, height_);
auto gtk_window = window.gobj(); auto gtk_window = window.gobj();
auto gtk_widget = GTK_WIDGET(gtk_window); auto gtk_widget = GTK_WIDGET(gtk_window);
@ -29,8 +28,8 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config)
gdk_wayland_window_set_use_custom_surface(gdk_window); gdk_wayland_window_set_use_custom_surface(gdk_window);
surface = gdk_wayland_window_get_wl_surface(gdk_window); surface = gdk_wayland_window_get_wl_surface(gdk_window);
std::size_t layer = config["layer"] == "top" ? ZWLR_LAYER_SHELL_V1_LAYER_TOP std::size_t layer =
: ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM; config["layer"] == "top" ? ZWLR_LAYER_SHELL_V1_LAYER_TOP : ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM;
auto client = waybar::Client::inst(); auto client = waybar::Client::inst();
layer_surface = zwlr_layer_shell_v1_get_layer_surface( layer_surface = zwlr_layer_shell_v1_get_layer_surface(
client->layer_shell, surface, output->output, layer, "waybar"); client->layer_shell, surface, output->output, layer, "waybar");
@ -43,36 +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 height = config["height"].isUInt() ? config["height"].asUInt() : height_;
auto width = config["width"].isUInt() ? config["width"].asUInt() : width_; auto width = config["width"].isUInt() ? config["width"].asUInt() : width_;
window.signal_configure_event().connect_notify([&](GdkEventConfigure* ev) {
auto tmp_height = height_;
auto tmp_width = width_;
if (ev->height > static_cast<int>(height_)) {
// Default minimal value
if (height_ != 1) {
std::cout << fmt::format(MIN_HEIGHT_MSG, height_, ev->height) << std::endl;
}
if (config["height"].isUInt()) {
std::cout << fmt::format(SIZE_DEFINED, "Height") << std::endl;
} else {
tmp_height = ev->height;
}
}
if (ev->width > static_cast<int>(width_)) {
// Default minimal value
if (width_ != 1) {
std::cout << fmt::format(MIN_WIDTH_MSG, width_, ev->width) << std::endl;
}
if (config["width"].isUInt()) {
std::cout << fmt::format(SIZE_DEFINED, "Width") << std::endl;
} else {
tmp_width = ev->width;
}
}
if (tmp_width != width_ || tmp_height != height_) {
zwlr_layer_surface_v1_set_size(layer_surface, tmp_width, tmp_height);
}
});
std::size_t anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP; std::size_t anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP;
if (config["position"] == "bottom") { if (config["position"] == "bottom") {
anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
@ -94,13 +63,97 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config)
} }
zwlr_layer_surface_v1_set_anchor(layer_surface, anchor); zwlr_layer_surface_v1_set_anchor(layer_surface, anchor);
zwlr_layer_surface_v1_set_exclusive_zone(layer_surface, vertical ? width : height);
zwlr_layer_surface_v1_set_size(layer_surface, width, height); zwlr_layer_surface_v1_set_size(layer_surface, width, height);
setMarginsAndZone(height, width);
wl_surface_commit(surface); wl_surface_commit(surface);
wl_display_roundtrip(client->wl_display); wl_display_roundtrip(client->wl_display);
setupWidgets(); 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) {
if (config["margin-top"].isInt() || config["margin-right"].isInt() ||
config["margin-bottom"].isInt() || config["margin-left"].isInt()) {
margins_ = {
config["margin-top"].isInt() ? config["margin-top"].asInt() : 0,
config["margin-right"].isInt() ? config["margin-right"].asInt() : 0,
config["margin-bottom"].isInt() ? config["margin-bottom"].asInt() : 0,
config["margin-left"].isInt() ? config["margin-left"].asInt() : 0,
};
} else if (config["margin"].isString()) {
std::istringstream iss(config["margin"].asString());
std::vector<std::string> margins{std::istream_iterator<std::string>(iss), {}};
try {
if (margins.size() == 1) {
auto gaps = std::stoi(margins[0], nullptr, 10);
margins_ = {.top = gaps, .right = gaps, .bottom = gaps, .left = gaps};
}
if (margins.size() == 2) {
auto vertical_margins = std::stoi(margins[0], nullptr, 10);
auto horizontal_margins = std::stoi(margins[1], nullptr, 10);
margins_ = {.top = vertical_margins,
.right = horizontal_margins,
.bottom = vertical_margins,
.left = horizontal_margins};
}
if (margins.size() == 3) {
auto horizontal_margins = std::stoi(margins[1], nullptr, 10);
margins_ = {.top = std::stoi(margins[0], nullptr, 10),
.right = horizontal_margins,
.bottom = std::stoi(margins[2], nullptr, 10),
.left = horizontal_margins};
}
if (margins.size() == 4) {
margins_ = {.top = std::stoi(margins[0], nullptr, 10),
.right = std::stoi(margins[1], nullptr, 10),
.bottom = std::stoi(margins[2], nullptr, 10),
.left = std::stoi(margins[3], nullptr, 10)};
}
} catch (...) {
std::cerr << "Invalid margins: " + config["margin"].asString() << std::endl;
}
} else if (config["margin"].isInt()) {
auto gaps = config["margin"].asInt();
margins_ = {.top = gaps, .right = gaps, .bottom = gaps, .left = gaps};
}
zwlr_layer_surface_v1_set_margin(
layer_surface, margins_.top, margins_.right, margins_.bottom, margins_.left);
auto zone = vertical ? width + margins_.right : height + margins_.bottom;
zwlr_layer_surface_v1_set_exclusive_zone(layer_surface, zone);
}
void waybar::Bar::onConfigure(GdkEventConfigure* ev) {
auto tmp_height = height_;
auto tmp_width = width_;
if (ev->height > static_cast<int>(height_)) {
// Default minimal value
if (height_ != 1) {
std::cout << fmt::format(MIN_HEIGHT_MSG, height_, ev->height) << std::endl;
}
if (config["height"].isUInt()) {
std::cout << fmt::format(SIZE_DEFINED, "Height") << std::endl;
} else {
tmp_height = ev->height;
}
}
if (ev->width > static_cast<int>(width_)) {
// Default minimal value
if (width_ != 1) {
std::cout << fmt::format(MIN_WIDTH_MSG, width_, ev->width) << std::endl;
}
if (config["width"].isUInt()) {
std::cout << fmt::format(SIZE_DEFINED, "Width") << std::endl;
} else {
tmp_width = ev->width;
}
}
if (tmp_width != width_ || tmp_height != height_) {
zwlr_layer_surface_v1_set_size(layer_surface, tmp_width, tmp_height);
}
} }
// Converting string to button code rn as to avoid doing it later // Converting string to button code rn as to avoid doing it later
@ -111,15 +164,13 @@ void waybar::Bar::setupAltFormatKeyForModule(const std::string& module_name) {
if (module.isMember("format-alt-click")) { if (module.isMember("format-alt-click")) {
Json::Value& click = module["format-alt-click"]; Json::Value& click = module["format-alt-click"];
if (click.isString()) { if (click.isString()) {
std::string str_click = click.asString(); if (click == "click-right") {
if (str_click == "click-right") {
module["format-alt-click"] = 3U; module["format-alt-click"] = 3U;
} else if (str_click == "click-middle") { } else if (click == "click-middle") {
module["format-alt-click"] = 2U; module["format-alt-click"] = 2U;
} else if (str_click == "click-backward") { } else if (click == "click-backward") {
module["format-alt-click"] = 8U; module["format-alt-click"] = 8U;
} else if (str_click == "click-forward") { } else if (click == "click-forward") {
module["format-alt-click"] = 9U; module["format-alt-click"] = 9U;
} else { } else {
module["format-alt-click"] = 1U; // default click-left module["format-alt-click"] = 1U; // default click-left
@ -174,8 +225,8 @@ void waybar::Bar::layerSurfaceHandleConfigure(void* data, struct zwlr_layer_surf
o->height_ = height; o->height_ = height;
o->window.set_size_request(o->width_, o->height_); o->window.set_size_request(o->width_, o->height_);
o->window.resize(o->width_, o->height_); o->window.resize(o->width_, o->height_);
zwlr_layer_surface_v1_set_exclusive_zone(o->layer_surface, auto zone = o->vertical ? width + o->margins_.right : height + o->margins_.bottom;
o->vertical ? o->width_ : o->height_); zwlr_layer_surface_v1_set_exclusive_zone(o->layer_surface, zone);
std::cout << fmt::format(BAR_SIZE_MSG, std::cout << fmt::format(BAR_SIZE_MSG,
o->width_ == 1 ? "auto" : std::to_string(o->width_), o->width_ == 1 ? "auto" : std::to_string(o->width_),
o->height_ == 1 ? "auto" : std::to_string(o->height_), o->height_ == 1 ? "auto" : std::to_string(o->height_),
@ -221,11 +272,14 @@ void waybar::Bar::getModules(const Factory& factory, const std::string& pos) {
modules_right_.emplace_back(module); modules_right_.emplace_back(module);
} }
module->dp.connect([module, &name] { module->dp.connect([module, &name] {
try { // Fix https://github.com/Alexays/Waybar/issues/320, proper way?
module->update(); Glib::signal_idle().connect_once([module, &name] {
} catch (const std::exception& e) { try {
std::cerr << name.asString() + ": " + e.what() << std::endl; module->update();
} } catch (const std::exception& e) {
std::cerr << name.asString() + ": " + e.what() << std::endl;
}
});
}); });
} catch (const std::exception& e) { } catch (const std::exception& e) {
std::cerr << e.what() << std::endl; std::cerr << e.what() << std::endl;

View File

@ -153,6 +153,7 @@ void waybar::Client::handleName(void * data, struct zxdg_output_v1 * /*xdg_
wl_output_destroy(output->output); wl_output_destroy(output->output);
zxdg_output_v1_destroy(output->xdg_output); zxdg_output_v1_destroy(output->xdg_output);
} else { } else {
wl_display_roundtrip(client->wl_display);
for (const auto &config : configs) { for (const auto &config : configs) {
client->bars.emplace_back(std::make_unique<Bar>(output.get(), config)); client->bars.emplace_back(std::make_unique<Bar>(output.get(), config));
Glib::RefPtr<Gdk::Screen> screen = client->bars.back()->window.get_screen(); Glib::RefPtr<Gdk::Screen> screen = client->bars.back()->window.get_screen();

View File

@ -7,12 +7,14 @@ waybar::IModule* waybar::Factory::makeModule(const std::string& name) const {
auto hash_pos = name.find('#'); auto hash_pos = name.find('#');
auto ref = name.substr(0, hash_pos); auto ref = name.substr(0, hash_pos);
auto id = hash_pos != std::string::npos ? name.substr(hash_pos + 1) : ""; auto id = hash_pos != std::string::npos ? name.substr(hash_pos + 1) : "";
#ifndef NO_FILESYSTEM
if (ref == "battery") { if (ref == "battery") {
return new waybar::modules::Battery(id, config_[name]); return new waybar::modules::Battery(id, config_[name]);
} }
#endif
#ifdef HAVE_SWAY #ifdef HAVE_SWAY
if (ref == "sway/mode") { if (ref == "sway/mode") {
return new waybar::modules::sway::Mode(id, bar_, config_[name]); return new waybar::modules::sway::Mode(id, config_[name]);
} }
if (ref == "sway/workspaces") { if (ref == "sway/workspaces") {
return new waybar::modules::sway::Workspaces(id, bar_, config_[name]); return new waybar::modules::sway::Workspaces(id, bar_, config_[name]);
@ -33,7 +35,7 @@ waybar::IModule* waybar::Factory::makeModule(const std::string& name) const {
if (ref == "clock") { if (ref == "clock") {
return new waybar::modules::Clock(id, config_[name]); return new waybar::modules::Clock(id, config_[name]);
} }
#ifdef HAVE_DBUSMENU #if defined(HAVE_DBUSMENU) && !defined(NO_FILESYSTEM)
if (ref == "tray") { if (ref == "tray") {
return new waybar::modules::SNI::Tray(id, bar_, config_[name]); return new waybar::modules::SNI::Tray(id, bar_, config_[name]);
} }

View File

@ -181,6 +181,7 @@ auto waybar::modules::Backlight::update() -> void {
const auto percent = best->get_max() == 0 ? 100 : best->get_actual() * 100 / best->get_max(); const auto percent = best->get_max() == 0 ? 100 : best->get_actual() * 100 / best->get_max();
label_.set_markup(fmt::format( label_.set_markup(fmt::format(
format_, fmt::arg("percent", std::to_string(percent)), fmt::arg("icon", getIcon(percent)))); format_, fmt::arg("percent", std::to_string(percent)), fmt::arg("icon", getIcon(percent))));
getState(percent);
} else { } else {
if (!previous_best_.has_value()) { if (!previous_best_.has_value()) {
return; return;

View File

@ -47,21 +47,21 @@ void waybar::modules::Battery::worker() {
void waybar::modules::Battery::getBatteries() { void waybar::modules::Battery::getBatteries() {
try { try {
for (auto const& node : fs::directory_iterator(data_dir_)) { for (auto& node : fs::directory_iterator(data_dir_)) {
if (!fs::is_directory(node)) { if (!fs::is_directory(node)) {
continue; continue;
} }
auto dir_name = node.path().filename(); auto dir_name = node.path().filename();
auto bat_defined = config_["bat"].isString(); auto bat_defined = config_["bat"].isString();
if (((bat_defined && dir_name == config_["bat"].asString()) || !bat_defined) && if (((bat_defined && dir_name == config_["bat"].asString()) || !bat_defined) &&
fs::exists(node / "capacity") && fs::exists(node / "uevent") && fs::exists(node.path() / "capacity") && fs::exists(node.path() / "uevent") &&
fs::exists(node / "status")) { fs::exists(node.path() / "status")) {
batteries_.push_back(node); batteries_.push_back(node.path());
} }
auto adap_defined = config_["adapter"].isString(); auto adap_defined = config_["adapter"].isString();
if (((adap_defined && dir_name == config_["adapter"].asString()) || !adap_defined) && if (((adap_defined && dir_name == config_["adapter"].asString()) || !adap_defined) &&
fs::exists(node / "online")) { fs::exists(node.path() / "online")) {
adapter_ = node; adapter_ = node.path();
} }
} }
} catch (fs::filesystem_error& e) { } catch (fs::filesystem_error& e) {
@ -75,76 +75,69 @@ void waybar::modules::Battery::getBatteries() {
} }
} }
const std::tuple<uint8_t, std::string> waybar::modules::Battery::getInfos() const { const std::tuple<uint8_t, uint32_t, std::string> waybar::modules::Battery::getInfos() const {
try { try {
uint16_t total = 0; uint16_t total = 0;
uint32_t total_current = 0;
std::string status = "Unknown"; std::string status = "Unknown";
for (auto const& bat : batteries_) { for (auto const& bat : batteries_) {
uint16_t capacity; uint16_t capacity;
uint32_t current_now;
std::string _status; std::string _status;
std::ifstream(bat / "capacity") >> capacity; std::ifstream(bat / "capacity") >> capacity;
std::ifstream(bat / "status") >> _status; std::ifstream(bat / "status") >> _status;
std::ifstream(bat / "current_now") >> current_now;
if (_status != "Unknown") { if (_status != "Unknown") {
status = _status; status = _status;
} }
total += capacity; 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(); uint16_t capacity = total / batteries_.size();
return {capacity, status}; return {capacity, total_current, status};
} catch (const std::exception& e) { } catch (const std::exception& e) {
std::cerr << e.what() << std::endl; 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()) { if (!adapter_.empty()) {
bool online; bool online;
std::ifstream(adapter_ / "online") >> online; std::ifstream(adapter_ / "online") >> online;
if (capacity == 100) { if (capacity == 100) {
return "Full"; return "Full";
} }
return online ? "Charging" : "Discharging"; if (online) {
return "Charging";
}
return "Discharging";
} }
return "Unknown"; return "Unknown";
} }
const std::string waybar::modules::Battery::getState(uint8_t capacity) const {
// Get current state
std::vector<std::pair<std::string, uint8_t>> states;
if (config_["states"].isObject()) {
for (auto it = config_["states"].begin(); it != config_["states"].end(); ++it) {
if (it->isUInt() && it.key().isString()) {
states.emplace_back(it.key().asString(), it->asUInt());
}
}
}
// Sort states
std::sort(states.begin(), states.end(), [](auto& a, auto& b) { return a.second < b.second; });
std::string valid_state;
for (auto const& state : states) {
if (capacity <= state.second && valid_state.empty()) {
label_.get_style_context()->add_class(state.first);
valid_state = state.first;
} else {
label_.get_style_context()->remove_class(state.first);
}
}
return valid_state;
}
auto waybar::modules::Battery::update() -> void { auto waybar::modules::Battery::update() -> void {
auto [capacity, status] = getInfos(); auto [capacity, current_now, status] = getInfos();
if (status == "Unknown") { if (status == "Unknown") {
status = getAdapterStatus(capacity); status = getAdapterStatus(capacity, current_now);
} }
if (tooltipEnabled()) { if (tooltipEnabled()) {
label_.set_tooltip_text(status); label_.set_tooltip_text(status);
} }
std::transform(status.begin(), status.end(), status.begin(), ::tolower); std::transform(status.begin(), status.end(), status.begin(), ::tolower);
auto format = format_; auto format = format_;
auto state = getState(capacity); auto state = getState(capacity, true);
label_.get_style_context()->remove_class(old_status_); if (!old_status_.empty()) {
label_.get_style_context()->remove_class(old_status_);
}
label_.get_style_context()->add_class(status); label_.get_style_context()->add_class(status);
old_status_ = status; old_status_ = status;
if (!state.empty() && config_["format-" + status + "-" + state].isString()) { if (!state.empty() && config_["format-" + status + "-" + state].isString()) {
@ -158,7 +151,7 @@ auto waybar::modules::Battery::update() -> void {
event_box_.hide(); event_box_.hide();
} else { } else {
event_box_.show(); event_box_.show();
label_.set_markup( label_.set_markup(fmt::format(
fmt::format(format, fmt::arg("capacity", capacity), fmt::arg("icon", getIcon(capacity)))); format, fmt::arg("capacity", capacity), fmt::arg("icon", getIcon(capacity, state))));
} }
} }

View File

@ -20,6 +20,7 @@ auto waybar::modules::Cpu::update() -> void {
label_.set_tooltip_text(tooltip); label_.set_tooltip_text(tooltip);
} }
label_.set_markup(fmt::format(format_, fmt::arg("load", cpu_load), fmt::arg("usage", cpu_usage))); label_.set_markup(fmt::format(format_, fmt::arg("load", cpu_load), fmt::arg("usage", cpu_usage)));
getState(cpu_usage);
} }
uint16_t waybar::modules::Cpu::getCpuLoad() { uint16_t waybar::modules::Cpu::getCpuLoad() {

View File

@ -79,6 +79,18 @@ void waybar::modules::Custom::refresh(int sig /*signal*/) {
} }
} }
bool waybar::modules::Custom::handleScroll(GdkEventScroll* e) {
auto ret = ALabel::handleScroll(e);
thread_.wake_up();
return ret;
}
bool waybar::modules::Custom::handleToggle(GdkEventButton* const& e) {
auto ret = ALabel::handleToggle(e);
thread_.wake_up();
return ret;
}
auto waybar::modules::Custom::update() -> void { auto waybar::modules::Custom::update() -> void {
// Hide label if output is empty // Hide label if output is empty
if (config_["exec"].isString() && (output_.out.empty() || output_.exit_code != 0)) { if (config_["exec"].isString() && (output_.out.empty() || output_.exit_code != 0)) {

View File

@ -50,13 +50,8 @@ bool waybar::modules::IdleInhibitor::handleToggle(GdkEventButton* const& e) {
waybar::Client::inst()->idle_inhibit_manager, bar_.surface); waybar::Client::inst()->idle_inhibit_manager, bar_.surface);
status_ = "activated"; status_ = "activated";
} }
if (config_["on-click"].isString() && e->button == 1) { click_param = status_;
pid_ = waybar::util::command::forkExec(config_["on-click"].asString());
}
} else {
ALabel::handleToggle(e);
} }
ALabel::handleToggle(e);
dp.emit();
return true; return true;
} }

View File

@ -16,6 +16,7 @@ auto waybar::modules::Memory::update() -> void {
parseMeminfo(); parseMeminfo();
if (memtotal_ > 0 && memfree_ >= 0) { if (memtotal_ > 0 && memfree_ >= 0) {
int used_ram_percentage = 100 * (memtotal_ - memfree_) / memtotal_; int used_ram_percentage = 100 * (memtotal_ - memfree_) / memtotal_;
getState(used_ram_percentage);
label_.set_markup(fmt::format(format_, used_ram_percentage)); label_.set_markup(fmt::format(format_, used_ram_percentage));
auto used_ram_gigabytes = (memtotal_ - memfree_) / std::pow(1024, 2); auto used_ram_gigabytes = (memtotal_ - memfree_) / std::pow(1024, 2);
if (tooltipEnabled()) { if (tooltipEnabled()) {

View File

@ -65,7 +65,7 @@ std::thread waybar::modules::MPD::event_listener() {
try { try {
if (connection_ == nullptr) { if (connection_ == nullptr) {
// Retry periodically if no connection // Retry periodically if no connection
update(); dp.emit();
std::this_thread::sleep_for(interval_); std::this_thread::sleep_for(interval_);
} else { } else {
waitForEvent(); waitForEvent();

View File

@ -1,33 +1,113 @@
#include "modules/network.hpp" #include "modules/network.hpp"
#include <sys/eventfd.h> #include <sys/eventfd.h>
#include <fstream>
#include <iostream>
namespace {
constexpr const char * NETSTAT_FILE = "/proc/net/netstat"; // std::ifstream does not take std::string_view as param
constexpr std::string_view BANDWIDTH_CATEGORY = "IpExt";
constexpr std::string_view BANDWIDTH_DOWN_TOTAL_KEY = "InOctets";
constexpr std::string_view BANDWIDTH_UP_TOTAL_KEY = "OutOctets";
std::ifstream netstat(NETSTAT_FILE);
std::optional<unsigned long long> read_netstat(std::string_view category, std::string_view key) {
if (!netstat) {
std::cerr << "Failed to open netstat file " << NETSTAT_FILE << '\n' << std::flush;
return {};
}
netstat.seekg(std::ios_base::beg);
// finding corresponding line (category)
// looks into the file for the first line starting by the 'category' string
auto starts_with = [](const std::string& str, std::string_view start) {
return start == std::string_view{str.data(), std::min(str.size(), start.size())};
};
std::string read;
while (std::getline(netstat, read) && !starts_with(read, category));
if (!starts_with(read, category)) {
std::cerr << "Category '" << category << "' not found in netstat file " << NETSTAT_FILE << '\n' << std::flush;
return {};
}
// finding corresponding column (key)
// looks into the fetched line for the first word (space separated) equal to 'key'
int index = 0;
auto r_it = read.begin();
auto k_it = key.begin();
while (k_it != key.end() && r_it != read.end()) {
if (*r_it != *k_it) {
r_it = std::find(r_it, read.end(), ' ');
if (r_it != read.end()) {
++r_it;
}
k_it = key.begin();
++index;
} else {
++r_it;
++k_it;
}
}
if (r_it == read.end() && k_it != key.end()) {
std::cerr << "Key '" << key << "' not found in category '" << category << "' of netstat file " << NETSTAT_FILE << '\n' << std::flush;
return {};
}
// finally accessing value
// accesses the line right under the fetched one
std::getline(netstat, read);
assert(starts_with(read, category));
std::istringstream iss(read);
while (index--) {
std::getline(iss, read, ' ');
}
unsigned long long value;
iss >> value;
return value;
}
}
waybar::modules::Network::Network(const std::string &id, const Json::Value &config) waybar::modules::Network::Network(const std::string &id, const Json::Value &config)
: ALabel(config, "{ifname}", 60), : ALabel(config, "{ifname}", 60),
family_(AF_INET), ifid_(-1),
last_ext_iface_(-1),
family_(config["family"] == "ipv6" ? AF_INET6 : AF_INET),
efd_(-1), efd_(-1),
ev_fd_(-1), ev_fd_(-1),
cidr_(-1), cidr_(-1),
signal_strength_dbm_(0), signal_strength_dbm_(0),
signal_strength_(0) { signal_strength_(0),
frequency_(0) {
label_.set_name("network"); label_.set_name("network");
if (!id.empty()) { if (!id.empty()) {
label_.get_style_context()->add_class(id); label_.get_style_context()->add_class(id);
} }
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) {
bandwidth_down_total_ = *down_octets;
} else {
bandwidth_down_total_ = 0;
}
if (up_octets) {
bandwidth_up_total_ = *up_octets;
} else {
bandwidth_up_total_ = 0;
}
createInfoSocket(); createInfoSocket();
createEventSocket(); createEventSocket();
if (config_["interface"].isString()) { auto default_iface = getPreferredIface();
ifid_ = if_nametoindex(config_["interface"].asCString()); if (default_iface != -1) {
ifname_ = config_["interface"].asString(); char ifname[IF_NAMESIZE];
if (ifid_ <= 0) { if_indextoname(default_iface, ifname);
throw std::runtime_error("Can't found network interface"); ifname_ = ifname;
} getInterfaceAddress();
} else {
ifid_ = getExternalInterface();
if (ifid_ > 0) {
char ifname[IF_NAMESIZE];
if_indextoname(ifid_, ifname);
ifname_ = ifname;
}
} }
dp.emit(); dp.emit();
worker(); worker();
@ -42,39 +122,38 @@ waybar::modules::Network::~Network() {
if (efd_ > -1) { if (efd_ > -1) {
close(efd_); close(efd_);
} }
if (info_sock_ != nullptr) { if (ev_sock_ != nullptr) {
nl_socket_drop_membership(info_sock_, RTMGRP_LINK); nl_socket_drop_membership(ev_sock_, RTNLGRP_LINK);
nl_socket_drop_membership(info_sock_, RTMGRP_IPV4_IFADDR); nl_socket_drop_membership(ev_sock_, RTNLGRP_IPV4_IFADDR);
nl_close(info_sock_); nl_socket_drop_membership(ev_sock_, RTNLGRP_IPV6_IFADDR);
nl_socket_free(info_sock_); nl_close(ev_sock_);
nl_socket_free(ev_sock_);
} }
if (sk_ != nullptr) { if (sock_ != nullptr) {
nl_close(sk_); nl_close(sock_);
nl_socket_free(sk_); nl_socket_free(sock_);
} }
} }
void waybar::modules::Network::createInfoSocket() { void waybar::modules::Network::createInfoSocket() {
info_sock_ = nl_socket_alloc(); ev_sock_ = nl_socket_alloc();
if (nl_connect(info_sock_, NETLINK_ROUTE) != 0) { 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"); throw std::runtime_error("Can't connect network socket");
} }
if (nl_socket_add_membership(info_sock_, RTMGRP_LINK) != 0) { nl_socket_add_membership(ev_sock_, RTNLGRP_LINK);
throw std::runtime_error("Can't add membership"); nl_socket_add_membership(ev_sock_, RTNLGRP_IPV4_IFADDR);
} nl_socket_add_membership(ev_sock_, RTNLGRP_IPV6_IFADDR);
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);
efd_ = epoll_create1(EPOLL_CLOEXEC); efd_ = epoll_create1(EPOLL_CLOEXEC);
if (efd_ < 0) { if (efd_ < 0) {
throw std::runtime_error("Can't create epoll"); throw std::runtime_error("Can't create epoll");
} }
{ {
ev_fd_ = eventfd(0, EFD_NONBLOCK); 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.events = EPOLLIN | EPOLLET;
event.data.fd = ev_fd_; event.data.fd = ev_fd_;
if (epoll_ctl(efd_, EPOLL_CTL_ADD, ev_fd_, &event) == -1) { if (epoll_ctl(efd_, EPOLL_CTL_ADD, ev_fd_, &event) == -1) {
@ -82,8 +161,9 @@ void waybar::modules::Network::createInfoSocket() {
} }
} }
{ {
auto fd = nl_socket_get_fd(info_sock_); auto fd = nl_socket_get_fd(ev_sock_);
struct epoll_event event = {0}; struct epoll_event event;
memset(&event, 0, sizeof(event));
event.events = EPOLLIN | EPOLLET | EPOLLRDHUP; event.events = EPOLLIN | EPOLLET | EPOLLRDHUP;
event.data.fd = fd; event.data.fd = fd;
if (epoll_ctl(efd_, EPOLL_CTL_ADD, fd, &event) == -1) { if (epoll_ctl(efd_, EPOLL_CTL_ADD, fd, &event) == -1) {
@ -93,14 +173,14 @@ void waybar::modules::Network::createInfoSocket() {
} }
void waybar::modules::Network::createEventSocket() { void waybar::modules::Network::createEventSocket() {
sk_ = nl_socket_alloc(); sock_ = nl_socket_alloc();
if (genl_connect(sk_) != 0) { if (genl_connect(sock_) != 0) {
throw std::runtime_error("Can't connect to netlink socket"); 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"); 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) { if (nl80211_id_ < 0) {
throw std::runtime_error("Can't resolve nl80211 interface"); throw std::runtime_error("Can't resolve nl80211 interface");
} }
@ -108,9 +188,12 @@ void waybar::modules::Network::createEventSocket() {
void waybar::modules::Network::worker() { void waybar::modules::Network::worker() {
thread_timer_ = [this] { thread_timer_ = [this] {
if (ifid_ > 0) { {
getInfo(); std::lock_guard<std::mutex> lock(mutex_);
dp.emit(); if (ifid_ > 0) {
getInfo();
dp.emit();
}
} }
thread_timer_.sleep_for(interval_); thread_timer_.sleep_for(interval_);
}; };
@ -119,26 +202,36 @@ void waybar::modules::Network::worker() {
int ec = epoll_wait(efd_, events.data(), EPOLL_MAX, -1); int ec = epoll_wait(efd_, events.data(), EPOLL_MAX, -1);
if (ec > 0) { if (ec > 0) {
for (auto i = 0; i < ec; i++) { for (auto i = 0; i < ec; i++) {
if (events[i].data.fd == nl_socket_get_fd(info_sock_)) { if (events[i].data.fd == nl_socket_get_fd(ev_sock_)) {
nl_recvmsgs_default(info_sock_); nl_recvmsgs_default(ev_sock_);
} else { } else {
thread_.stop(); thread_.stop();
break; break;
} }
} }
} else if (ec == -1) {
thread_.stop();
} }
}; };
} }
auto waybar::modules::Network::update() -> void { auto waybar::modules::Network::update() -> void {
std::string connectiontype; std::string connectiontype;
std::string tooltip_format = ""; std::string tooltip_format;
if (config_["tooltip-format"].isString()) { std::lock_guard<std::mutex> lock(mutex_);
tooltip_format = config_["tooltip-format"].asString(); auto down_octets = read_netstat(BANDWIDTH_CATEGORY, BANDWIDTH_DOWN_TOTAL_KEY);
auto up_octets = read_netstat(BANDWIDTH_CATEGORY, BANDWIDTH_UP_TOTAL_KEY);
unsigned long long bandwidth_down = 0;
if (down_octets) {
bandwidth_down = *down_octets - bandwidth_down_total_;
bandwidth_down_total_ = *down_octets;
} }
if (ifid_ <= 0 || ipaddr_.empty()) {
unsigned long long bandwidth_up = 0;
if (up_octets) {
bandwidth_up = *up_octets - bandwidth_up_total_;
bandwidth_up_total_ = *up_octets;
}
if (ifid_ <= 0 || !linked_) {
if (config_["format-disconnected"].isString()) { if (config_["format-disconnected"].isString()) {
default_format_ = config_["format-disconnected"].asString(); default_format_ = config_["format-disconnected"].asString();
} }
@ -156,6 +249,14 @@ auto waybar::modules::Network::update() -> void {
tooltip_format = config_["tooltip-format-ethernet"].asString(); tooltip_format = config_["tooltip-format-ethernet"].asString();
} }
connectiontype = "ethernet"; 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 { } else {
if (config_["format-wifi"].isString()) { if (config_["format-wifi"].isString()) {
default_format_ = config_["format-wifi"].asString(); default_format_ = config_["format-wifi"].asString();
@ -170,6 +271,26 @@ auto waybar::modules::Network::update() -> void {
if (!alt_) { if (!alt_) {
format_ = default_format_; format_ = default_format_;
} }
getState(signal_strength_);
auto pow_format = [](unsigned long long value, const std::string& unit) {
if (value > 2000ull * 1000ull * 1000ull) { // > 2G
auto go = value / (1000 * 1000 * 1000);
return std::to_string(go) + "." + std::to_string((value - go * 1000 * 1000 * 1000) / (100 * 1000 * 1000)) + "G" + unit;
} else if (value > 2000ull * 1000ull) { // > 2M
auto mo = value / (1000 * 1000);
return std::to_string(mo) + "." + std::to_string((value - mo * 1000 * 1000) / (100 * 1000)) + "M" + unit;
} else if (value > 2000ull) { // > 2k
auto ko = value / 1000;
return std::to_string(ko) + "." + std::to_string((value - ko * 1000) / 100) + "k" + unit;
} else {
return std::to_string(value) + unit;
}
};
auto text = fmt::format(format_, auto text = fmt::format(format_,
fmt::arg("essid", essid_), fmt::arg("essid", essid_),
fmt::arg("signaldBm", signal_strength_dbm_), fmt::arg("signaldBm", signal_strength_dbm_),
@ -178,9 +299,19 @@ auto waybar::modules::Network::update() -> void {
fmt::arg("netmask", netmask_), fmt::arg("netmask", netmask_),
fmt::arg("ipaddr", ipaddr_), fmt::arg("ipaddr", ipaddr_),
fmt::arg("cidr", cidr_), fmt::arg("cidr", cidr_),
fmt::arg("icon", getIcon(signal_strength_, connectiontype))); fmt::arg("frequency", frequency_),
label_.set_markup(text); fmt::arg("icon", getIcon(signal_strength_, connectiontype)),
fmt::arg("bandwidthDownBits", pow_format(bandwidth_down * 8ull / interval_.count(), "b/s")),
fmt::arg("bandwidthUpBits", pow_format(bandwidth_up * 8ull / interval_.count(), "b/s")),
fmt::arg("bandwidthDownOctets", pow_format(bandwidth_down / interval_.count(), "o/s")),
fmt::arg("bandwidthUpOctets", pow_format(bandwidth_up / interval_.count(), "o/s")));
if (text != label_.get_label()) {
label_.set_markup(text);
}
if (tooltipEnabled()) { if (tooltipEnabled()) {
if (tooltip_format.empty() && config_["tooltip-format"].isString()) {
tooltip_format = config_["tooltip-format"].asString();
}
if (!tooltip_format.empty()) { if (!tooltip_format.empty()) {
auto tooltip_text = fmt::format(tooltip_format, auto tooltip_text = fmt::format(tooltip_format,
fmt::arg("essid", essid_), fmt::arg("essid", essid_),
@ -190,29 +321,21 @@ auto waybar::modules::Network::update() -> void {
fmt::arg("netmask", netmask_), fmt::arg("netmask", netmask_),
fmt::arg("ipaddr", ipaddr_), fmt::arg("ipaddr", ipaddr_),
fmt::arg("cidr", cidr_), fmt::arg("cidr", cidr_),
fmt::arg("icon", getIcon(signal_strength_, connectiontype))); fmt::arg("frequency", frequency_),
label_.set_tooltip_text(tooltip_text); fmt::arg("icon", getIcon(signal_strength_, connectiontype)),
} else { fmt::arg("bandwidthDownBits", pow_format(bandwidth_down * 8ull / interval_.count(), "b/s")),
fmt::arg("bandwidthUpBits", pow_format(bandwidth_up * 8ull / interval_.count(), "b/s")),
fmt::arg("bandwidthDownOctets", pow_format(bandwidth_down / interval_.count(), "o/s")),
fmt::arg("bandwidthUpOctets", pow_format(bandwidth_up / interval_.count(), "o/s")));
if (label_.get_tooltip_text() != text) {
label_.set_tooltip_text(tooltip_text);
}
} else if (label_.get_tooltip_text() != text) {
label_.set_tooltip_text(text); label_.set_tooltip_text(text);
} }
} }
} }
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 // Based on https://gist.github.com/Yawning/c70d804d4b8ae78cc698
int waybar::modules::Network::getExternalInterface() { int waybar::modules::Network::getExternalInterface() {
static const uint32_t route_buffer_size = 8192; static const uint32_t route_buffer_size = 8192;
@ -332,6 +455,7 @@ int waybar::modules::Network::getExternalInterface() {
} while (true); } while (true);
out: out:
last_ext_iface_ = ifidx;
return ifidx; return ifidx;
} }
@ -342,26 +466,33 @@ void waybar::modules::Network::getInterfaceAddress() {
netmask_.clear(); netmask_.clear();
cidr_ = 0; cidr_ = 0;
int success = getifaddrs(&ifaddr); int success = getifaddrs(&ifaddr);
if (success == 0) { if (success != 0) {
ifa = ifaddr; return;
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);
} }
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<struct sockaddr_in *>(ifa->ifa_addr)->sin_addr,
ipaddr,
INET6_ADDRSTRLEN);
char netmask[INET6_ADDRSTRLEN];
auto net_addr = reinterpret_cast<struct sockaddr_in *>(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) { int waybar::modules::Network::netlinkRequest(void *req, uint32_t reqlen, uint32_t groups) {
@ -369,8 +500,13 @@ int waybar::modules::Network::netlinkRequest(void *req, uint32_t reqlen, uint32_
sa.nl_family = AF_NETLINK; sa.nl_family = AF_NETLINK;
sa.nl_groups = groups; sa.nl_groups = groups;
struct iovec iov = {req, reqlen}; struct iovec iov = {req, reqlen};
struct msghdr msg = {&sa, sizeof(sa), &iov, 1, nullptr, 0, 0}; struct msghdr msg = {
return sendmsg(nl_socket_get_fd(info_sock_), &msg, 0); .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) { int waybar::modules::Network::netlinkResponse(void *resp, uint32_t resplen, uint32_t groups) {
@ -378,56 +514,125 @@ int waybar::modules::Network::netlinkResponse(void *resp, uint32_t resplen, uint
sa.nl_family = AF_NETLINK; sa.nl_family = AF_NETLINK;
sa.nl_groups = groups; sa.nl_groups = groups;
struct iovec iov = {resp, resplen}; struct iovec iov = {resp, resplen};
struct msghdr msg = {&sa, sizeof(sa), &iov, 1, nullptr, 0, 0}; struct msghdr msg = {
auto ret = recvmsg(nl_socket_get_fd(info_sock_), &msg, 0); .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) { if (msg.msg_flags & MSG_TRUNC) {
return -1; return -1;
} }
return ret; return ret;
} }
int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { bool waybar::modules::Network::checkInterface(struct ifinfomsg *rtif, std::string name) {
int ret = 0; if (config_["interface"].isString()) {
auto net = static_cast<waybar::modules::Network *>(data); return config_["interface"].asString() == name ||
bool need_update = false; wildcardMatch(config_["interface"].asString(), name);
for (nlmsghdr *nh = nlmsg_hdr(msg); NLMSG_OK(nh, ret); nh = NLMSG_NEXT(nh, ret)) { }
if (nh->nlmsg_type == RTM_NEWADDR) { auto external_iface = getExternalInterface();
need_update = true; if (external_iface == -1) {
} // Try with lastest working external iface
if (nh->nlmsg_type < RTM_NEWADDR) { return last_ext_iface_ == rtif->ifi_index;
auto rtif = static_cast<struct ifinfomsg *>(NLMSG_DATA(nh)); }
if (rtif->ifi_index == static_cast<int>(net->ifid_)) { return external_iface == rtif->ifi_index;
need_update = true; }
if (!(rtif->ifi_flags & IFF_RUNNING)) {
net->disconnected(); int waybar::modules::Network::getPreferredIface() {
net->dp.emit(); if (config_["interface"].isString()) {
return NL_SKIP; 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()) { ifid_ = getExternalInterface();
for (uint8_t i = 0; i < MAX_RETRY; i += 1) { if (ifid_ > 0) {
net->ifid_ = net->getExternalInterface(); char ifname[IF_NAMESIZE];
if (net->ifid_ > 0) { if_indextoname(ifid_, ifname);
break; ifname_ = ifname;
} return ifid_;
// Need to wait before get external interface }
net->thread_.sleep_for(std::chrono::seconds(1)); return -1;
} }
if (net->ifid_ > 0) {
char ifname[IF_NAMESIZE]; int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) {
if_indextoname(net->ifid_, ifname); auto net = static_cast<waybar::modules::Network *>(data);
auto nh = nlmsg_hdr(msg);
std::lock_guard<std::mutex> lock(net->mutex_);
if (nh->nlmsg_type == RTM_NEWADDR) {
auto rtif = static_cast<struct ifinfomsg *>(NLMSG_DATA(nh));
char ifname[IF_NAMESIZE];
if_indextoname(rtif->ifi_index, ifname);
// Auto detected network can also be assigned here
if (net->ifid_ == -1 && net->checkInterface(rtif, ifname)) {
net->linked_ = true;
net->ifname_ = ifname; net->ifname_ = ifname;
need_update = true; net->ifid_ = rtif->ifi_index;
} }
} // Check for valid interface
if (need_update) { if (rtif->ifi_index == net->ifid_) {
if (net->ifid_ > 0) { // Get Iface and WIFI info
net->getInfo(); net->getInterfaceAddress();
net->thread_timer_.wake_up();
}
} else if (nh->nlmsg_type == RTM_DELADDR) {
auto rtif = static_cast<struct ifinfomsg *>(NLMSG_DATA(nh));
// Check for valid interface
if (rtif->ifi_index == 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<struct ifinfomsg *>(NLMSG_DATA(nh));
char ifname[IF_NAMESIZE];
if_indextoname(rtif->ifi_index, ifname);
// Check for valid interface
if (rtif->ifi_flags & IFF_RUNNING && net->checkInterface(rtif, ifname)) {
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;
net->frequency_ = 0;
// Check for a new interface and get info
auto new_iface = net->getPreferredIface();
if (new_iface != -1) {
net->getInterfaceAddress();
net->thread_timer_.wake_up();
} else {
net->dp.emit();
}
} }
net->dp.emit();
} }
return NL_SKIP; return NL_SKIP;
} }
@ -463,7 +668,7 @@ int waybar::modules::Network::handleScan(struct nl_msg *msg, void *data) {
} }
net->parseEssid(bss); net->parseEssid(bss);
net->parseSignal(bss); net->parseSignal(bss);
// TODO(someone): parse quality net->parseFreq(bss);
return NL_SKIP; return NL_SKIP;
} }
@ -503,6 +708,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) { bool waybar::modules::Network::associatedOrJoined(struct nlattr **bss) {
if (bss[NL80211_BSS_STATUS] == nullptr) { if (bss[NL80211_BSS_STATUS] == nullptr) {
return false; return false;
@ -519,7 +731,6 @@ bool waybar::modules::Network::associatedOrJoined(struct nlattr **bss) {
} }
auto waybar::modules::Network::getInfo() -> void { auto waybar::modules::Network::getInfo() -> void {
getInterfaceAddress();
struct nl_msg *nl_msg = nlmsg_alloc(); struct nl_msg *nl_msg = nlmsg_alloc();
if (nl_msg == nullptr) { if (nl_msg == nullptr) {
return; return;
@ -531,5 +742,44 @@ auto waybar::modules::Network::getInfo() -> void {
nlmsg_free(nl_msg); nlmsg_free(nl_msg);
return; 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;
} }

View File

@ -39,7 +39,7 @@ waybar::modules::Pulseaudio::Pulseaudio(const std::string &id, const Json::Value
// events are configured // events are configured
if (!config["on-scroll-up"].isString() && !config["on-scroll-down"].isString()) { if (!config["on-scroll-up"].isString() && !config["on-scroll-down"].isString()) {
event_box_.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK); 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 // 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_) { if (scrolling_) {
return false; 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; scrolling_ = true;
if (e->direction == GDK_SCROLL_UP) { if (e->direction == GDK_SCROLL_UP) {
direction_up = true; 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 (direction_up) {
if (volume_ + 1 < 100) { if (volume_ + 1 < 100) {
pa_cvolume_inc(&pa_volume, change); 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->volume_ = std::round(volume * 100.0F);
pa->muted_ = i->mute != 0; pa->muted_ = i->mute != 0;
pa->desc_ = i->description; pa->desc_ = i->description;
pa->monitor_ = i->monitor_source_name;
pa->port_name_ = i->active_port != nullptr ? i->active_port->name : "Unknown"; pa->port_name_ = i->active_port != nullptr ? i->active_port->name : "Unknown";
pa->dp.emit(); pa->dp.emit();
} }
@ -192,7 +198,7 @@ auto waybar::modules::Pulseaudio::update() -> void {
label_.get_style_context()->add_class("muted"); label_.get_style_context()->add_class("muted");
} else { } else {
label_.get_style_context()->remove_class("muted"); 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 = format =
config_["format-bluetooth"].isString() ? config_["format-bluetooth"].asString() : format; config_["format-bluetooth"].isString() ? config_["format-bluetooth"].asString() : format;
label_.get_style_context()->add_class("bluetooth"); label_.get_style_context()->add_class("bluetooth");
@ -202,6 +208,7 @@ auto waybar::modules::Pulseaudio::update() -> void {
} }
label_.set_markup(fmt::format( label_.set_markup(fmt::format(
format, fmt::arg("volume", volume_), fmt::arg("icon", getIcon(volume_, getPortIcon())))); format, fmt::arg("volume", volume_), fmt::arg("icon", getIcon(volume_, getPortIcon()))));
getState(volume_);
if (tooltipEnabled()) { if (tooltipEnabled()) {
label_.set_tooltip_text(desc_); label_.set_tooltip_text(desc_);
} }

View File

@ -130,8 +130,13 @@ std::tuple<std::string, std::string> Host::getBusNameAndObjectPath(const std::st
void Host::addRegisteredItem(std::string service) { void Host::addRegisteredItem(std::string service) {
auto [bus_name, object_path] = getBusNameAndObjectPath(service); auto [bus_name, object_path] = getBusNameAndObjectPath(service);
items_.emplace_back(new Item(bus_name, object_path, config_)); auto it = std::find_if(items_.begin(), items_.end(), [&bus_name, &object_path](const auto& item) {
on_add_(items_.back()); 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());
}
} }
} } // namespace waybar::modules::SNI

View File

@ -276,39 +276,38 @@ Glib::RefPtr<Gdk::Pixbuf> Item::getIconByName(const std::string& name, int reque
name.c_str(), tmp_size, Gtk::IconLookupFlags::ICON_LOOKUP_FORCE_SIZE); name.c_str(), tmp_size, Gtk::IconLookupFlags::ICON_LOOKUP_FORCE_SIZE);
} }
void Item::onMenuDestroyed(Item* self) { void Item::onMenuDestroyed(Item* self, GObject* old_menu_pointer) {
self->gtk_menu = nullptr; if (old_menu_pointer == reinterpret_cast<GObject*>(self->dbus_menu)) {
self->dbus_menu = nullptr; self->gtk_menu = nullptr;
self->dbus_menu = nullptr;
}
} }
bool Item::makeMenu(GdkEventButton* const& ev) { void Item::makeMenu(GdkEventButton* const& ev) {
if (gtk_menu == nullptr) { if (gtk_menu == nullptr && !menu.empty()) {
if (!menu.empty()) { dbus_menu = dbusmenu_gtkmenu_new(bus_name.data(), menu.data());
dbus_menu = dbusmenu_gtkmenu_new(bus_name.data(), menu.data()); if (dbus_menu != nullptr) {
if (dbus_menu != nullptr) { g_object_ref_sink(G_OBJECT(dbus_menu));
g_object_ref_sink(G_OBJECT(dbus_menu)); g_object_weak_ref(G_OBJECT(dbus_menu), (GWeakNotify)onMenuDestroyed, this);
g_object_weak_ref(G_OBJECT(dbus_menu), (GWeakNotify)onMenuDestroyed, this); gtk_menu = Glib::wrap(GTK_MENU(dbus_menu));
gtk_menu = Glib::wrap(GTK_MENU(dbus_menu)); gtk_menu->attach_to_widget(event_box);
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<GdkEvent*>(ev));
#else
gtk_menu->popup(ev->button, ev->time);
#endif
return true;
}
return false;
} }
bool Item::handleClick(GdkEventButton* const& ev) { bool Item::handleClick(GdkEventButton* const& ev) {
auto parameters = Glib::VariantContainerBase::create_tuple( auto parameters = Glib::VariantContainerBase::create_tuple(
{Glib::Variant<int>::create(ev->x), Glib::Variant<int>::create(ev->y)}); {Glib::Variant<int>::create(ev->x), Glib::Variant<int>::create(ev->y)});
if ((ev->button == 1 && item_is_menu) || ev->button == 3) { 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<GdkEvent*>(ev));
#else
gtk_menu->popup(ev->button, ev->time);
#endif
return true;
} else {
proxy_->call("ContextMenu", parameters); proxy_->call("ContextMenu", parameters);
return true; return true;
} }

View File

@ -14,11 +14,6 @@ Watcher::Watcher()
watcher_(sn_watcher_skeleton_new()) {} watcher_(sn_watcher_skeleton_new()) {}
Watcher::~Watcher() { Watcher::~Watcher() {
if (bus_name_id_ > 0) {
g_bus_unown_name(bus_name_id_);
bus_name_id_ = 0;
}
if (hosts_ != nullptr) { if (hosts_ != nullptr) {
g_slist_free_full(hosts_, gfWatchFree); g_slist_free_full(hosts_, gfWatchFree);
hosts_ = nullptr; hosts_ = nullptr;
@ -28,7 +23,8 @@ Watcher::~Watcher() {
g_slist_free_full(items_, gfWatchFree); g_slist_free_full(items_, gfWatchFree);
items_ = nullptr; 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<Gio::DBus::Connection>& conn, Glib::ustring name) { void Watcher::busAcquired(const Glib::RefPtr<Gio::DBus::Connection>& conn, Glib::ustring name) {
@ -36,7 +32,10 @@ void Watcher::busAcquired(const Glib::RefPtr<Gio::DBus::Connection>& conn, Glib:
g_dbus_interface_skeleton_export( g_dbus_interface_skeleton_export(
G_DBUS_INTERFACE_SKELETON(watcher_), conn->gobj(), "/StatusNotifierWatcher", &error); G_DBUS_INTERFACE_SKELETON(watcher_), conn->gobj(), "/StatusNotifierWatcher", &error);
if (error != nullptr) { if (error != nullptr) {
std::cerr << error->message << std::endl; // Don't print an error when a watcher is already present
if (error->code != 2) {
std::cerr << error->message << std::endl;
}
g_error_free(error); g_error_free(error);
return; return;
} }

View File

@ -104,9 +104,7 @@ struct Ipc::ipc_response Ipc::recv(int fd) {
} }
total += res; total += res;
} }
std::lock_guard<std::mutex> lock(mutex_parser_); return {data32[0], data32[1], &payload.front()};
auto parsed = parser_.parse(&payload.front());
return {data32[0], data32[1], parsed};
} }
struct Ipc::ipc_response Ipc::send(int fd, uint32_t type, const std::string& payload) { struct Ipc::ipc_response Ipc::send(int fd, uint32_t type, const std::string& payload) {
@ -133,15 +131,13 @@ void Ipc::sendCmd(uint32_t type, const std::string& payload) {
} }
void Ipc::subscribe(const std::string& payload) { void Ipc::subscribe(const std::string& payload) {
std::lock_guard<std::mutex> lock(mutex_event_);
auto res = Ipc::send(fd_event_, IPC_SUBSCRIBE, payload); auto res = Ipc::send(fd_event_, IPC_SUBSCRIBE, payload);
if (!res.payload["success"].asBool()) { if (res.payload != "{\"success\": true}") {
throw std::runtime_error("Unable to subscribe ipc event"); throw std::runtime_error("Unable to subscribe ipc event");
} }
} }
void Ipc::handleEvent() { void Ipc::handleEvent() {
std::lock_guard<std::mutex> lock(mutex_event_);
const auto res = Ipc::recv(fd_event_); const auto res = Ipc::recv(fd_event_);
signal_event.emit(res); signal_event.emit(res);
} }

View File

@ -2,8 +2,7 @@
namespace waybar::modules::sway { namespace waybar::modules::sway {
Mode::Mode(const std::string& id, const Bar& bar, const Json::Value& config) Mode::Mode(const std::string& id, const Json::Value& config) : ALabel(config, "{}") {
: ALabel(config, "{}"), bar_(bar) {
label_.set_name("mode"); label_.set_name("mode");
if (!id.empty()) { if (!id.empty()) {
label_.get_style_context()->add_class(id); label_.get_style_context()->add_class(id);
@ -15,13 +14,18 @@ Mode::Mode(const std::string& id, const Bar& bar, const Json::Value& config)
dp.emit(); dp.emit();
} }
void Mode::onEvent(const struct Ipc::ipc_response &res) { void Mode::onEvent(const struct Ipc::ipc_response& res) {
if (res.payload["change"] != "default") { try {
mode_ = res.payload["change"].asString(); auto payload = parser_.parse(res.payload);
} else { if (payload["change"] != "default") {
mode_.clear(); mode_ = payload["change"].asString();
} else {
mode_.clear();
}
dp.emit();
} catch (const std::exception& e) {
std::cerr << "Mode: " << e.what() << std::endl;
} }
dp.emit();
} }
void Mode::worker() { void Mode::worker() {

View File

@ -21,47 +21,41 @@ Window::Window(const std::string& id, const Bar& bar, const Json::Value& config)
worker(); worker();
} }
void Window::onEvent(const struct Ipc::ipc_response& res) { void Window::onEvent(const struct Ipc::ipc_response& res) { getTree(); }
auto data = res.payload;
// Check for waybar prevents flicker when hovering window module
if ((data["change"] == "focus" || data["change"] == "title") &&
data["container"]["focused"].asBool() && data["container"]["name"].asString() != "waybar") {
window_ = Glib::Markup::escape_text(data["container"]["name"].asString());
windowId_ = data["container"]["id"].asInt();
dp.emit();
getTree();
} else if ((data["change"] == "close" && data["container"]["focused"].asBool() &&
windowId_ == data["container"]["id"].asInt()) ||
(data["change"] == "focus" && data["current"]["focus"].isArray() &&
data["current"]["focus"].empty())) {
window_.clear();
windowId_ = -1;
dp.emit();
getTree();
}
}
void Window::onCmd(const struct Ipc::ipc_response& res) { void Window::onCmd(const struct Ipc::ipc_response& res) {
auto [nb, id, name, app_id] = getFocusedNode(res.payload); try {
if (!app_id_.empty()) { std::lock_guard<std::mutex> lock(mutex_);
bar_.window.get_style_context()->remove_class(app_id_); auto payload = parser_.parse(res.payload);
} auto [nb, id, name, app_id] = getFocusedNode(payload);
if (nb == 0) { if (!app_id_.empty()) {
bar_.window.get_style_context()->add_class("empty"); bar_.window.get_style_context()->remove_class(app_id_);
} else if (nb == 1) {
bar_.window.get_style_context()->add_class("solo");
if (!app_id.empty()) {
bar_.window.get_style_context()->add_class(app_id);
} }
} else { if (nb == 0) {
bar_.window.get_style_context()->remove_class("solo"); bar_.window.get_style_context()->remove_class("solo");
bar_.window.get_style_context()->remove_class("empty"); if (!bar_.window.get_style_context()->has_class("empty")) {
} bar_.window.get_style_context()->add_class("empty");
app_id_ = app_id; }
if (windowId_ != id || window_ != name) { } else if (nb == 1) {
windowId_ = id; bar_.window.get_style_context()->remove_class("empty");
window_ = name; if (!bar_.window.get_style_context()->has_class("solo")) {
dp.emit(); 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 {
bar_.window.get_style_context()->remove_class("solo");
bar_.window.get_style_context()->remove_class("empty");
}
app_id_ = app_id;
if (windowId_ != id || window_ != name) {
windowId_ = id;
window_ = name;
dp.emit();
}
} catch (const std::exception& e) {
std::cerr << "Window: " << e.what() << std::endl;
} }
} }
@ -112,4 +106,4 @@ void Window::getTree() {
} }
} }
} // namespace waybar::modules::sway } // namespace waybar::modules::sway

View File

@ -15,26 +15,42 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value
ipc_.signal_event.connect(sigc::mem_fun(*this, &Workspaces::onEvent)); ipc_.signal_event.connect(sigc::mem_fun(*this, &Workspaces::onEvent));
ipc_.signal_cmd.connect(sigc::mem_fun(*this, &Workspaces::onCmd)); ipc_.signal_cmd.connect(sigc::mem_fun(*this, &Workspaces::onCmd));
ipc_.sendCmd(IPC_GET_WORKSPACES); ipc_.sendCmd(IPC_GET_WORKSPACES);
if (!config["disable-bar-scroll"].asBool()) {
auto &window = const_cast<Bar&>(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 // Launch worker
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) { void Workspaces::onCmd(const struct Ipc::ipc_response &res) {
if (res.type == IPC_GET_WORKSPACES) { if (res.type == IPC_GET_WORKSPACES) {
if (res.payload.isArray()) { try {
std::lock_guard<std::mutex> lock(mutex_); auto payload = parser_.parse(res.payload);
workspaces_.clear(); if (payload.isArray()) {
std::copy_if(res.payload.begin(), std::lock_guard<std::mutex> lock(mutex_);
res.payload.end(), workspaces_.clear();
std::back_inserter(workspaces_), std::copy_if(payload.begin(),
[&](const auto &workspace) { payload.end(),
return !config_["all-outputs"].asBool() std::back_inserter(workspaces_),
? workspace["output"].asString() == bar_.output->name [&](const auto &workspace) {
: true; return !config_["all-outputs"].asBool()
}); ? workspace["output"].asString() == bar_.output->name
dp.emit(); : true;
});
dp.emit();
}
} catch (const std::exception &e) {
std::cerr << "Workspaces: " << e.what() << std::endl;
} }
} else { } else {
if (scrolling_) { if (scrolling_) {
@ -189,13 +205,17 @@ bool Workspaces::handleScroll(GdkEventScroll *e) {
return false; 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; return true;
} }
const std::string Workspaces::getCycleWorkspace(std::vector<Json::Value>::iterator it, const std::string Workspaces::getCycleWorkspace(std::vector<Json::Value>::iterator it,
bool prev) const { bool prev) const {
if (prev && it == workspaces_.begin()) { if (prev && it == workspaces_.begin() && !config_["disable-scroll-wraparound"].asBool()) {
return (*(--workspaces_.end()))["name"].asString(); return (*(--workspaces_.end()))["name"].asString();
} }
if (prev && it != workspaces_.begin()) if (prev && it != workspaces_.begin())
@ -203,7 +223,11 @@ const std::string Workspaces::getCycleWorkspace(std::vector<Json::Value>::iterat
else if (!prev && it != workspaces_.end()) else if (!prev && it != workspaces_.end())
++it; ++it;
if (!prev && it == workspaces_.end()) { if (!prev && it == workspaces_.end()) {
return (*(++workspaces_.begin()))["name"].asString(); if (config_["disable-scroll-wraparound"].asBool()) {
--it;
} else {
return (*(workspaces_.begin()))["name"].asString();
}
} }
return (*it)["name"].asString(); return (*it)["name"].asString();
} }
@ -230,4 +254,4 @@ void Workspaces::onButtonReady(const Json::Value &node, Gtk::Button &button) {
Workspaces::operator Gtk::Widget &() { return box_; } Workspaces::operator Gtk::Widget &() { return box_; }
} // namespace waybar::modules::sway } // namespace waybar::modules::sway

View File

@ -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; auto zone = config_["thermal-zone"].isInt() ? config_["thermal-zone"].asInt() : 0;
file_path_ = fmt::format("/sys/class/thermal/thermal_zone{}/temp", zone); file_path_ = fmt::format("/sys/class/thermal/thermal_zone{}/temp", zone);
} }
#ifdef FILESYSTEM_EXPERIMENTAL std::ifstream temp(file_path_);
if (!std::experimental::filesystem::exists(file_path_)) { if (!temp.is_open()) {
#else
if (!std::filesystem::exists(file_path_)) {
#endif
throw std::runtime_error("Can't open " + file_path_); throw std::runtime_error("Can't open " + file_path_);
} }
label_.set_name("temperature"); label_.set_name("temperature");
@ -35,8 +32,11 @@ auto waybar::modules::Temperature::update() -> void {
} else { } else {
label_.get_style_context()->remove_class("critical"); label_.get_style_context()->remove_class("critical");
} }
label_.set_markup(fmt::format( auto max_temp = config_["critical-threshold"].isInt() ? config_["critical-threshold"].asInt() : 0;
format, fmt::arg("temperatureC", temperature_c), fmt::arg("temperatureF", temperature_f))); 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<uint16_t, uint16_t> waybar::modules::Temperature::getTemperature() { std::tuple<uint16_t, uint16_t> waybar::modules::Temperature::getTemperature() {