Compare commits

..

119 Commits
0.6.5 ... 0.7.0

Author SHA1 Message Date
9c8e39c30c chore: 0.7.0 2019-06-22 18:17:16 +02:00
5b270dae0d refactor: AModule (#387)
refactor: AModule
2019-06-22 18:15:50 +02:00
c621afb0c4 Merge branch 'master' into clean 2019-06-22 18:15:34 +02:00
bcf4725349 Cleanup on clean branch (#391)
Cleanup on clean branch
2019-06-18 09:43:34 +02:00
12b30ca25f AModule::getScrollDir: convert reset if-else into switch 2019-06-17 20:42:19 +02:00
86d6668ed4 AModule::getScrollDir: convert if-else chain into switch statement 2019-06-17 20:40:13 +02:00
7c85aec8e0 AModule::getScrollDir: get deltas in a more C++ way 2019-06-17 20:29:37 +02:00
2c038d1977 AModule::getScrollDir: move dir inside the only scope it is relevant 2019-06-17 20:09:53 +02:00
ff9d598c16 fix: add proper mutex 2019-06-17 11:39:45 +02:00
71a9a75aad refactor: remove fix workaround 2019-06-16 15:14:31 +02:00
05f796158b fix: typo 2019-06-16 15:13:40 +02:00
1d2dd953e7 revert: default config 2019-06-16 15:09:26 +02:00
527144a440 refactor(pulseaudio): proper scroll override 2019-06-16 15:08:08 +02:00
cda6282277 Merge pull request #389 from Synthetica9/x-scroll
AModule: handle X axis scrolling
2019-06-16 14:54:53 +02:00
7f13478396 AModule: handle X axis scrolling 2019-06-16 13:17:34 +02:00
90a9c0e25f refactor: get rid of some mutex 2019-06-15 15:01:03 +02:00
340ec7be91 refactor: AModule 2019-06-15 14:57:52 +02:00
e7eef6b493 Merge pull request #385 from nenad/patch/add-total-memory
Expose total memory to the formatting directive
2019-06-15 11:16:30 +02:00
1b7068e61d Expose total memory to the formatting directive 2019-06-14 22:48:16 +02:00
dabe2bebbb feat(sway/window): handle floating nodes 2019-06-14 11:27:40 +02:00
486b5a5d38 fix(sway/window): check output recursively 2019-06-14 10:57:22 +02:00
11bbc3b24d Fix twitchy scrolling on touchpads (#381)
Fix twitchy scrolling on touchpads
2019-06-14 10:38:25 +02:00
7f74de977c chore: 0.6.9 2019-06-14 10:27:41 +02:00
028b184f7b fix(workspaces): persistant class with empty outputs 2019-06-12 09:50:33 +02:00
564fdcb369 fix(custom): exit status 2019-06-11 22:09:47 +02:00
396f7d4525 Workspaces: implement horizontal continuous scrolling 2019-06-11 18:44:54 +02:00
3c9b533997 fix(window): avoid hexpand 2019-06-11 17:57:17 +02:00
ae397c8fa2 ALabel: add smooth-scrolling-threshold 2019-06-11 17:56:10 +02:00
ec75be0bc3 fix(Tray): click behaviour 2019-06-11 17:53:16 +02:00
ed4521d113 Workspaces: fix twitchy scrolling on touchpads
Previously, any and all scroll events were interpreted as reason to switch
workspaces. This resulted in twitchy behaviour, where the scrolling was
practically unusable.

Now, we pool all scroll values, and only scroll if the value is larger than the
new config option "smooth-scrolling-threshold". If this option is not set, the
behaviour is unchanged.
2019-06-11 17:22:24 +02:00
c2e9ed6091 feat(workspaces): add class to persistant workspaces 2019-06-11 14:08:48 +02:00
a37b4687ff Revert "refactor(window): we don't need to subscribe workspace events"
This reverts commit 648eecdd83.
2019-06-11 14:06:31 +02:00
ee29a35aa5 Update README.md 2019-06-11 13:55:35 +02:00
0be8e200ce adds the wl output name as a css class (#373)
adds the wl output name as a css class
2019-06-10 12:15:05 +02:00
46e5dd93d4 adds the wl output name as a css class
now you can have a custom styling for each bar
2019-06-08 11:04:34 -07:00
2ee4a51546 chore: 0.6.8 2019-06-08 18:33:17 +02:00
91996a85c1 Merge pull request #372 from rianadon/patch-1
Increase specificity of media stylings
2019-06-08 18:28:42 +02:00
460d25ac45 Increase specificity of media stylings
Because of CSS specificity rules, the `#custom-media` style will always override the `custom-spotify` and `custom-vlc` styles, so the background of the media element is always green rather than sometimes orange when VLC is running. I added `#custom-media` in front of each of the class selectors to increase their specificity so they take precedence.
2019-06-07 22:18:06 -07:00
648eecdd83 refactor(window): we don't need to subscribe workspace events 2019-06-07 15:08:33 +02:00
f04ff38567 Merge pull request #370 from toke/bugfix/upstream-369
Prevent zombie apocalypse by ignoring SIGCHLD
2019-06-06 12:07:03 +02:00
d20a586734 Prevent zombie apocalypse by ignoring SIGCHLD
Fixes Issue #369
2019-06-05 14:35:25 +02:00
1962caf144 refactor(window): gtk stuff in update method 2019-06-04 17:34:00 +02:00
9dbf057f58 fix(custom): hide on empty format 2019-06-03 09:50:35 +02:00
918146c16b style: prefer background-color property 2019-05-31 17:20:14 +02:00
0b01b35c76 refactor(pulseaudio): only watch changes 2019-05-31 16:21:01 +02:00
f3fb955d75 chore: 0.6.7 2019-05-31 12:15:01 +02:00
fcf2d18a01 refactor: destroy threads first 2019-05-29 17:53:22 +02:00
b05d4cd413 fix(network): retry around all getExternalInterface 2019-05-29 16:17:40 +02:00
9b89fc6470 refactor: disable bar scroll by default 2019-05-28 16:11:33 +02:00
c06725aa69 fix(network): better disconnect handler 2019-05-28 11:21:59 +02:00
5ae5821929 refactor(network): re-add MAX_RETRY in order to detect external interface 2019-05-28 11:08:48 +02:00
74e40432e5 fix(network): linked state 2019-05-28 09:58:48 +02:00
6e69af8967 refactor(custon): avoid useless logic 2019-05-27 00:08:16 +02:00
be2fa743eb refactor(custon): hide on empty text 2019-05-27 00:05:29 +02:00
5fdb122829 Create FUNDING.yml 2019-05-26 23:55:46 +02:00
6e73c6db61 refactor(network): remove last_ext_iface_ 2019-05-26 23:16:09 +02:00
253366baf4 refactor(network): remove useless assignment 2019-05-26 22:40:29 +02:00
ecec02c8be refactor(network): better events handler 2019-05-26 22:36:26 +02:00
070619fa34 revert: restore idle fix 2019-05-26 20:09:05 +02:00
d4ace4b4d8 fix(network): subscribe only wanted family 2019-05-26 20:06:27 +02:00
5fd92b3c28 fix(network): don't check IFF_UP 2019-05-26 19:53:10 +02:00
c0a39f34cd refactor(network): don't clear ipaddr 2019-05-25 18:02:36 +02:00
2a9fa1a4b9 refactor(bar): onRealize, onMap 2019-05-25 17:50:45 +02:00
07147878a9 refactor(network): code cleaning 2019-05-24 09:49:56 +02:00
ffadd5c1a7 refactor: avoid useless class vars 2019-05-24 09:49:09 +02:00
2b34f3a30f refactor: parse args before app creation 2019-05-23 10:13:49 +02:00
85d60f95c4 refactor(network): const methods 2019-05-22 22:20:50 +02:00
755d38d4d6 fix(custom): bad alloc 2019-05-22 19:46:56 +02:00
b673279a43 style: remove non wanted comment 2019-05-22 12:22:56 +02:00
9e1200ae32 refactor: also pass id to custom modules 2019-05-22 12:20:13 +02:00
e999cca7a6 style: don't specify included namespaces 2019-05-22 12:15:59 +02:00
d24d85bebf refactor: move label name and id to label contructor 2019-05-22 12:06:24 +02:00
97bd637f5d refactor(clock): avoid usless time_point_cast 2019-05-22 11:51:33 +02:00
23d4a811db refactor(clock): avoid usless duration_cast 2019-05-22 11:48:02 +02:00
bf5c00ff2a chore: 0.6.6 2019-05-22 10:16:14 +02:00
14ace24a26 style(battery): format 2019-05-22 10:09:05 +02:00
2fa581c7ea fix(battery): multiple paths 2019-05-22 10:06:54 +02:00
6ff013e25b Estimate of time remaining until empty|full on tooltip (#331)
Estimate of time remaining until empty|full on tooltip
2019-05-22 09:44:55 +02:00
cf3cb4c61f feat(Battery) Format argument for time to full|empty 2019-05-21 13:44:05 -04:00
00ada46dfc feat(Battery) Time remaining on tooltip 2019-05-21 13:36:14 -04:00
2db81a6107 fix(Battery) "current" unused and removed 2019-05-21 13:35:39 -04:00
3e76984ce7 chore: update network config 2019-05-21 17:44:46 +02:00
48a58cd979 fix(network): switch between ifaces upon disconnection 2019-05-21 17:38:47 +02:00
296b448d06 chore: update pulseaudio config 2019-05-21 14:58:03 +02:00
7a3febf6a7 fix(pulseaudio): default source format 2019-05-21 14:55:17 +02:00
bb4af295bc feat(pulseaudio): source info 2019-05-21 14:53:31 +02:00
cf7663153d fix(pulseaudio): allow to scroll to 0 2019-05-21 09:29:39 +02:00
12a251c3a4 fix(mode): escape text 2019-05-20 20:51:19 +02:00
4accdd4524 fix(Workspace): ordering 2019-05-20 20:46:59 +02:00
032ad925af Merge pull request #339 from alebastr/master
Tray error handling and logging improvements
2019-05-20 20:34:29 +02:00
50bfe78aed refactor(tray): improve error handling and add debug logs 2019-05-20 08:00:07 -07:00
afd291a566 fix(tray): Fix glib assertion when old property value is missing
xembedsniproxy sets WindowId as 'i' instead of 'u' and DBus::Proxy
ignores incorrectly typed value.
2019-05-20 07:16:08 -07:00
316a9be656 refactor(tray): Use spdlog for SNI::Item error messages 2019-05-20 07:16:08 -07:00
cdb347aaca Add --log-level command line option 2019-05-20 06:48:44 -07:00
ed240ac105 feat: add warning about tray requirements 2019-05-20 15:21:13 +02:00
232073ca17 Fix clock is always a second off (#333)
Fix clock is always a second off
2019-05-20 14:45:34 +02:00
5314b74dae fix: remove workaround 2019-05-20 14:39:49 +02:00
e3879559a2 Merge pull request #330 from Organic-Code/master
Adding sway/workspaces:persistant_workspaces
2019-05-20 14:33:31 +02:00
0ec8774a08 Fixing: missing argument for fmt, workspace order
Persistant workspaces would reorder upon their first creation
2019-05-20 08:23:42 -04:00
071b4928dc fix(workspaces): order 2019-05-20 13:31:02 +02:00
7c4d75d428 feat: create new workspace on the wanted output 2019-05-20 13:21:22 +02:00
67593b8c0f Merge pull request #332 from Organic-Code/enhancement/spdlog
Adding spdlog
2019-05-20 12:07:58 +02:00
cff39bc7cf fix: remove watcher_id fixme comment as fixed on master 2019-05-20 11:56:55 +02:00
f2edc8f965 feat(Watcher): define watcher_id 2019-05-20 11:47:52 +02:00
7b11283b73 Allow formatting memory with used and available memory (#334)
Allow formatting memory with used and available memory
2019-05-20 11:30:19 +02:00
03e43fb31d refactor: remove wlroots dependency 2019-05-20 09:49:54 +02:00
913d0f7ad0 backlight: fix for percentage output for amdgpu_bl0 device (#336)
backlight: fix for percentage output for amdgpu_bl0 device
2019-05-20 09:40:03 +02:00
5feb478611 Merge branch 'master' into amd_fix 2019-05-19 22:40:47 +03:00
6bf64cd04d Allow formatting memory with used and available memory 2019-05-19 16:34:42 +01:00
5e43b4f587 Fix clock is always a second off 2019-05-19 13:30:19 +01:00
e8dd1e2d2c Adding missing ; and _
I'll admit I don't have libmpd on my computer
2019-05-18 20:10:42 -04:00
51be97f9aa Adding spdlog 2019-05-18 19:44:45 -04:00
a00f812cd1 Typo 2019-05-18 18:21:01 -04:00
863e0babd8 Adding break when sorted_workspaces is filled 2019-05-18 12:09:30 -04:00
8ba3052dd1 Adding comments & fixing code style 2019-05-18 12:04:09 -04:00
1a76aa0c8c Improving ordering 2019-05-18 11:58:01 -04:00
85f177a213 Adding sway/workspaces:persistant_workspaces in config file
c.f. https://github.com/Alexays/Waybar/issues/210
2019-05-18 10:58:55 -04:00
f743882baa Merge branch 'master' into amd_fix 2019-05-17 22:33:02 +03:00
9fa0eb7068 more elegant amd fix 2019-05-17 22:30:45 +03:00
56 changed files with 1233 additions and 968 deletions

3
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,3 @@
# These are supported funding model platforms
custom: https://paypal.me/ARouillard

View File

@ -17,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 --depth=1 https://github.com/swaywm/wlroots subprojects/wlroots && meson build && ninja -C build" - docker run waybar /bin/sh -c "cd /root && meson build && ninja -C build"

View File

@ -2,7 +2,8 @@
> Highly customizable Wayland bar for Sway and Wlroots based compositors.<br> > Highly customizable Wayland bar for Sway and Wlroots based compositors.<br>
> Available in Arch [community](https://www.archlinux.org/packages/community/x86_64/waybar/) or > Available in Arch [community](https://www.archlinux.org/packages/community/x86_64/waybar/) or
[AUR](https://aur.archlinux.org/packages/waybar-git/) and [openSUSE](https://build.opensuse.org/package/show/X11:Wayland/waybar) [AUR](https://aur.archlinux.org/packages/waybar-git/) and [openSUSE](https://build.opensuse.org/package/show/X11:Wayland/waybar)<br>
> *Waybar [examples](https://github.com/Alexays/Waybar/wiki/Examples)*
**Current features** **Current features**
- Sway (Workspaces, Binding mode, Focused window name) - Sway (Workspaces, Binding mode, Focused window name)
@ -42,6 +43,7 @@ $ waybar
gtkmm3 gtkmm3
jsoncpp jsoncpp
libinput libinput
libsigc++ libsigc++
fmt fmt
wayland wayland

View File

@ -1,40 +1,30 @@
#pragma once #pragma once
#include <glibmm/markup.h> #include <glibmm/markup.h>
#include <gtkmm/eventbox.h>
#include <gtkmm/label.h> #include <gtkmm/label.h>
#include <json/json.h> #include <json/json.h>
#include "IModule.hpp" #include "AModule.hpp"
namespace waybar { namespace waybar {
class ALabel : public IModule { class ALabel : public AModule {
public: public:
ALabel(const Json::Value &, const std::string &format, uint16_t interval = 0); ALabel(const Json::Value &, const std::string &, const std::string &, const std::string &format,
virtual ~ALabel(); uint16_t interval = 0);
virtual ~ALabel() = default;
virtual auto update() -> void; virtual auto update() -> void;
virtual std::string getIcon(uint16_t, const std::string &alt = "", uint16_t max = 0); virtual std::string getIcon(uint16_t, const std::string &alt = "", uint16_t max = 0);
virtual operator Gtk::Widget &();
protected: protected:
bool tooltipEnabled();
Gtk::EventBox event_box_;
Gtk::Label label_; Gtk::Label label_;
const Json::Value & config_;
std::string format_; std::string format_;
std::string click_param; std::string click_param;
std::mutex mutex_;
const std::chrono::seconds interval_; const std::chrono::seconds interval_;
bool alt_ = false; bool alt_ = false;
std::string default_format_; std::string default_format_;
virtual bool handleToggle(GdkEventButton *const &ev); virtual bool handleToggle(GdkEventButton *const &e);
virtual bool handleScroll(GdkEventScroll *);
virtual std::string getState(uint8_t value, bool lesser = false); virtual std::string getState(uint8_t value, bool lesser = false);
private:
std::vector<int> pid_;
}; };
} // namespace waybar } // namespace waybar

40
include/AModule.hpp Normal file
View File

@ -0,0 +1,40 @@
#pragma once
#include <glibmm/dispatcher.h>
#include <glibmm/markup.h>
#include <gtkmm/eventbox.h>
#include <json/json.h>
#include "IModule.hpp"
namespace waybar {
class AModule : public IModule {
public:
AModule(const Json::Value &, const std::string &, const std::string &,
bool enable_click = false, bool enable_scroll = false);
virtual ~AModule();
virtual auto update() -> void;
virtual operator Gtk::Widget &();
Glib::Dispatcher dp;
protected:
enum SCROLL_DIR { NONE, UP, DOWN, LEFT, RIGHT };
SCROLL_DIR getScrollDir(GdkEventScroll *e);
bool tooltipEnabled();
const Json::Value &config_;
Gtk::EventBox event_box_;
std::string click_param_;
virtual bool handleToggle(GdkEventButton *const &ev);
virtual bool handleScroll(GdkEventScroll *);
private:
std::vector<int> pid_;
gdouble distance_scrolled_y_;
gdouble distance_scrolled_x_;
};
} // namespace waybar

View File

@ -1,7 +1,5 @@
#pragma once #pragma once
#include <glibmm/dispatcher.h>
#include <gtkmm/box.h>
#include <gtkmm/widget.h> #include <gtkmm/widget.h>
namespace waybar { namespace waybar {
@ -11,7 +9,6 @@ class IModule {
virtual ~IModule() = default; virtual ~IModule() = default;
virtual auto update() -> void = 0; virtual auto update() -> void = 0;
virtual operator Gtk::Widget &() = 0; virtual operator Gtk::Widget &() = 0;
Glib::Dispatcher dp; // Hmmm Maybe I should create an abstract class ?
}; };
} // namespace waybar } // namespace waybar

View File

@ -4,8 +4,9 @@
#include <gtkmm/cssprovider.h> #include <gtkmm/cssprovider.h>
#include <gtkmm/main.h> #include <gtkmm/main.h>
#include <gtkmm/window.h> #include <gtkmm/window.h>
#include <gtkmm/box.h>
#include <json/json.h> #include <json/json.h>
#include "IModule.hpp" #include "AModule.hpp"
#include "idle-inhibit-unstable-v1-client-protocol.h" #include "idle-inhibit-unstable-v1-client-protocol.h"
#include "wlr-layer-shell-unstable-v1-client-protocol.h" #include "wlr-layer-shell-unstable-v1-client-protocol.h"
#include "xdg-output-unstable-v1-client-protocol.h" #include "xdg-output-unstable-v1-client-protocol.h"
@ -38,13 +39,13 @@ class Bar {
bool vertical = false; bool vertical = false;
private: private:
static inline const std::string MIN_HEIGHT_MSG = static constexpr const char *MIN_HEIGHT_MSG =
"Requested height: {} exceeds the minimum height: {} required by the modules"; "Requested height: {} exceeds the minimum height: {} required by the modules";
static inline const std::string MIN_WIDTH_MSG = static constexpr const char *MIN_WIDTH_MSG =
"Requested width: {} exceeds the minimum width: {} required by the modules"; "Requested width: {} exceeds the minimum width: {} required by the modules";
static inline const std::string BAR_SIZE_MSG = static constexpr const char *BAR_SIZE_MSG =
"Bar configured (width: {}, height: {}) for output: {}"; "Bar configured (width: {}, height: {}) for output: {}";
static inline const std::string SIZE_DEFINED = static constexpr const char *SIZE_DEFINED =
"{} size is defined in the config file so it will stay like that"; "{} size is defined in the config file so it will stay like that";
static void layerSurfaceHandleConfigure(void *, struct zwlr_layer_surface_v1 *, uint32_t, static void layerSurfaceHandleConfigure(void *, struct zwlr_layer_surface_v1 *, uint32_t,
uint32_t, uint32_t); uint32_t, uint32_t);
@ -52,6 +53,8 @@ class Bar {
void destroyOutput(); void destroyOutput();
void onConfigure(GdkEventConfigure *ev); void onConfigure(GdkEventConfigure *ev);
void onRealize();
void onMap(GdkEventAny *ev);
void setMarginsAndZone(uint32_t height, uint32_t width); 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 &);
@ -66,13 +69,14 @@ class Bar {
} margins_; } margins_;
uint32_t width_ = 0; uint32_t width_ = 0;
uint32_t height_ = 1; uint32_t height_ = 1;
uint8_t anchor_;
Gtk::Box left_; Gtk::Box left_;
Gtk::Box center_; Gtk::Box center_;
Gtk::Box right_; Gtk::Box right_;
Gtk::Box box_; Gtk::Box box_;
std::vector<std::unique_ptr<waybar::IModule>> modules_left_; std::vector<std::unique_ptr<waybar::AModule>> modules_left_;
std::vector<std::unique_ptr<waybar::IModule>> modules_center_; std::vector<std::unique_ptr<waybar::AModule>> modules_center_;
std::vector<std::unique_ptr<waybar::IModule>> modules_right_; std::vector<std::unique_ptr<waybar::AModule>> modules_right_;
}; };
} // namespace waybar } // namespace waybar

View File

@ -26,14 +26,15 @@ class Client {
private: private:
Client() = default; Client() = default;
void setupConfigs(const std::string &config, const std::string &style); std::tuple<const std::string, const std::string> getConfigs(const std::string &config,
const std::string &style) const;
void bindInterfaces(); void bindInterfaces();
const std::string getValidPath(const std::vector<std::string> &paths); const std::string getValidPath(const std::vector<std::string> &paths) const;
void handleOutput(std::unique_ptr<struct waybar_output> &output); void handleOutput(std::unique_ptr<struct waybar_output> &output);
bool isValidOutput(const Json::Value &config, std::unique_ptr<struct waybar_output> &output); bool isValidOutput(const Json::Value &config, std::unique_ptr<struct waybar_output> &output);
auto setupConfig() -> void; auto setupConfig(const std::string &config_file) -> void;
auto setupCss() -> void; auto setupCss(const std::string &css_file) -> void;
std::unique_ptr<struct waybar_output>& getOutput(uint32_t wl_name); std::unique_ptr<struct waybar_output> &getOutput(uint32_t wl_name);
std::vector<Json::Value> getOutputConfigs(std::unique_ptr<struct waybar_output> &output); std::vector<Json::Value> getOutputConfigs(std::unique_ptr<struct waybar_output> &output);
static void handleGlobal(void *data, struct wl_registry *registry, uint32_t name, static void handleGlobal(void *data, struct wl_registry *registry, uint32_t name,
@ -46,8 +47,6 @@ class Client {
static void handleDescription(void *, struct zxdg_output_v1 *, const char *); static void handleDescription(void *, struct zxdg_output_v1 *, const char *);
Json::Value config_; Json::Value config_;
std::string css_file_;
std::string config_file_;
Glib::RefPtr<Gtk::StyleContext> style_context_; Glib::RefPtr<Gtk::StyleContext> style_context_;
Glib::RefPtr<Gtk::CssProvider> css_provider_; Glib::RefPtr<Gtk::CssProvider> css_provider_;
std::vector<std::unique_ptr<struct waybar_output>> outputs_; std::vector<std::unique_ptr<struct waybar_output>> outputs_;

View File

@ -37,7 +37,7 @@ namespace waybar {
class Factory { class Factory {
public: public:
Factory(const Bar& bar, const Json::Value& config); Factory(const Bar& bar, const Json::Value& config);
IModule* makeModule(const std::string& name) const; AModule* makeModule(const std::string& name) const;
private: private:
const Bar& bar_; const Bar& bar_;

View File

@ -47,7 +47,6 @@ class Backlight : public ALabel {
template <class ForwardIt, class Inserter> template <class ForwardIt, class Inserter>
static void enumerate_devices(ForwardIt first, ForwardIt last, Inserter inserter, udev *udev); static void enumerate_devices(ForwardIt first, ForwardIt last, Inserter inserter, udev *udev);
const std::string name_;
const std::string preferred_device_; const std::string preferred_device_;
static constexpr int EPOLL_MAX_EVENTS = 16; static constexpr int EPOLL_MAX_EVENTS = 16;
@ -57,6 +56,6 @@ class Backlight : public ALabel {
std::mutex udev_thread_mutex_; std::mutex udev_thread_mutex_;
std::vector<BacklightDev> devices_; std::vector<BacklightDev> devices_;
// thread must destruct before shared data // thread must destruct before shared data
waybar::util::SleeperThread udev_thread_; util::SleeperThread udev_thread_;
}; };
} // namespace waybar::modules } // namespace waybar::modules

View File

@ -9,7 +9,8 @@
#include <sys/inotify.h> #include <sys/inotify.h>
#include <algorithm> #include <algorithm>
#include <fstream> #include <fstream>
#include <iostream> #include <string>
#include <vector>
#include "ALabel.hpp" #include "ALabel.hpp"
#include "util/sleeper_thread.hpp" #include "util/sleeper_thread.hpp"
@ -32,16 +33,18 @@ class Battery : public ALabel {
void getBatteries(); void getBatteries();
void worker(); void worker();
const std::string getAdapterStatus(uint8_t capacity, uint32_t current_now) const; const std::string getAdapterStatus(uint8_t capacity) const;
const std::tuple<uint8_t, uint32_t, std::string> getInfos() const; const std::tuple<uint8_t, float, std::string> getInfos() const;
const std::string formatTimeRemaining(float hoursRemaining);
util::SleeperThread thread_;
util::SleeperThread thread_timer_;
std::vector<fs::path> batteries_; std::vector<fs::path> batteries_;
fs::path adapter_; fs::path adapter_;
int fd_; int fd_;
std::vector<int> wds_; std::vector<int> wds_;
std::string old_status_; std::string old_status_;
util::SleeperThread thread_;
util::SleeperThread thread_timer_;
}; };
} // namespace waybar::modules } // namespace waybar::modules

View File

@ -14,7 +14,7 @@ class Clock : public ALabel {
auto update() -> void; auto update() -> void;
private: private:
waybar::util::SleeperThread thread_; util::SleeperThread thread_;
}; };
} // namespace waybar::modules } // namespace waybar::modules

View File

@ -2,9 +2,11 @@
#include <fmt/format.h> #include <fmt/format.h>
#include <sys/sysinfo.h> #include <sys/sysinfo.h>
#include <cstdint>
#include <fstream> #include <fstream>
#include <iostream>
#include <numeric> #include <numeric>
#include <string>
#include <utility>
#include <vector> #include <vector>
#include "ALabel.hpp" #include "ALabel.hpp"
#include "util/sleeper_thread.hpp" #include "util/sleeper_thread.hpp"
@ -24,7 +26,8 @@ class Cpu : public ALabel {
std::vector<std::tuple<size_t, size_t>> parseCpuinfo(); std::vector<std::tuple<size_t, size_t>> parseCpuinfo();
std::vector<std::tuple<size_t, size_t>> prev_times_; std::vector<std::tuple<size_t, size_t>> prev_times_;
waybar::util::SleeperThread thread_;
util::SleeperThread thread_;
}; };
} // namespace waybar::modules } // namespace waybar::modules

View File

@ -2,7 +2,7 @@
#include <fmt/format.h> #include <fmt/format.h>
#include <csignal> #include <csignal>
#include <iostream> #include <string>
#include "ALabel.hpp" #include "ALabel.hpp"
#include "util/command.hpp" #include "util/command.hpp"
#include "util/json.hpp" #include "util/json.hpp"
@ -12,7 +12,7 @@ namespace waybar::modules {
class Custom : public ALabel { class Custom : public ALabel {
public: public:
Custom(const std::string&, const Json::Value&); Custom(const std::string&, const std::string&, const Json::Value&);
~Custom(); ~Custom();
auto update() -> void; auto update() -> void;
void refresh(int /*signal*/); void refresh(int /*signal*/);
@ -31,11 +31,12 @@ class Custom : public ALabel {
std::string tooltip_; std::string tooltip_;
std::vector<std::string> class_; std::vector<std::string> class_;
int percentage_; int percentage_;
waybar::util::SleeperThread thread_;
waybar::util::command::res output_;
waybar::util::JsonParser parser_;
FILE* fp_; FILE* fp_;
int pid_; int pid_;
util::command::res output_;
util::JsonParser parser_;
util::SleeperThread thread_;
}; };
} // namespace waybar::modules } // namespace waybar::modules

View File

@ -15,10 +15,12 @@ class Memory : public ALabel {
private: private:
static inline const std::string data_dir_ = "/proc/meminfo"; static inline const std::string data_dir_ = "/proc/meminfo";
void parseMeminfo();
unsigned long memtotal_; unsigned long memtotal_;
unsigned long memfree_; unsigned long memfree_;
void parseMeminfo();
waybar::util::SleeperThread thread_; util::SleeperThread thread_;
}; };
} // namespace waybar::modules } // namespace waybar::modules

View File

@ -30,23 +30,23 @@ class Network : public ALabel {
void worker(); void worker();
void createInfoSocket(); void createInfoSocket();
void createEventSocket(); void createEventSocket();
int getExternalInterface(); int getExternalInterface(int skip_idx = -1) const;
void getInterfaceAddress(); void getInterfaceAddress();
int netlinkRequest(void*, uint32_t, uint32_t groups = 0); int netlinkRequest(void*, uint32_t, uint32_t groups = 0) const;
int netlinkResponse(void*, uint32_t, uint32_t groups = 0); int netlinkResponse(void*, uint32_t, uint32_t groups = 0) const;
void parseEssid(struct nlattr**); void parseEssid(struct nlattr**);
void parseSignal(struct nlattr**); void parseSignal(struct nlattr**);
void parseFreq(struct nlattr**); void parseFreq(struct nlattr**);
bool associatedOrJoined(struct nlattr**); bool associatedOrJoined(struct nlattr**);
bool checkInterface(struct ifinfomsg *rtif, std::string name); bool checkInterface(struct ifinfomsg* rtif, std::string name);
int getPreferredIface(); int getPreferredIface(int skip_idx = -1) const;
auto getInfo() -> void; auto getInfo() -> void;
bool wildcardMatch(const std::string& pattern, const std::string& text); void checkNewInterface(struct ifinfomsg* rtif);
const std::string getNetworkState() const;
void clearIface();
bool wildcardMatch(const std::string& pattern, const std::string& text) const;
waybar::util::SleeperThread thread_;
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* sock_ = nullptr; struct nl_sock* sock_ = nullptr;
@ -59,15 +59,18 @@ class Network : public ALabel {
unsigned long long bandwidth_down_total_; unsigned long long bandwidth_down_total_;
unsigned long long bandwidth_up_total_; unsigned long long bandwidth_up_total_;
std::string state_;
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_; uint32_t frequency_;
util::SleeperThread thread_;
util::SleeperThread thread_timer_;
}; };
} // namespace waybar::modules } // namespace waybar::modules

View File

@ -18,15 +18,17 @@ class Pulseaudio : public ALabel {
static void subscribeCb(pa_context*, pa_subscription_event_type_t, uint32_t, void*); static void subscribeCb(pa_context*, pa_subscription_event_type_t, uint32_t, void*);
static void contextStateCb(pa_context*, void*); static void contextStateCb(pa_context*, void*);
static void sinkInfoCb(pa_context*, const pa_sink_info*, int, void*); static void sinkInfoCb(pa_context*, const pa_sink_info*, int, void*);
static void sourceInfoCb(pa_context*, const pa_source_info* i, int, void* data);
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 handleVolume(GdkEventScroll* e);
bool handleScroll(GdkEventScroll* e);
const std::string getPortIcon() const; const std::string getPortIcon() const;
pa_threaded_mainloop* mainloop_; pa_threaded_mainloop* mainloop_;
pa_mainloop_api* mainloop_api_; pa_mainloop_api* mainloop_api_;
pa_context* context_; pa_context* context_;
// SINK
uint32_t sink_idx_{0}; uint32_t sink_idx_{0};
uint16_t volume_; uint16_t volume_;
pa_cvolume pa_volume_; pa_cvolume pa_volume_;
@ -34,7 +36,12 @@ class Pulseaudio : public ALabel {
std::string port_name_; std::string port_name_;
std::string desc_; std::string desc_;
std::string monitor_; std::string monitor_;
bool scrolling_; // SOURCE
uint32_t source_idx_{0};
uint16_t source_volume_;
bool source_muted_;
std::string source_port_name_;
std::string source_desc_;
}; };
} // namespace waybar::modules } // namespace waybar::modules

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
#include <fmt/format.h> #include <fmt/format.h>
#include "IModule.hpp" #include "AModule.hpp"
#include "bar.hpp" #include "bar.hpp"
#include "modules/sni/host.hpp" #include "modules/sni/host.hpp"
#include "modules/sni/watcher.hpp" #include "modules/sni/watcher.hpp"
@ -9,19 +9,17 @@
namespace waybar::modules::SNI { namespace waybar::modules::SNI {
class Tray : public IModule { class Tray : public AModule {
public: public:
Tray(const std::string&, const Bar&, const Json::Value&); Tray(const std::string&, const Bar&, const Json::Value&);
~Tray() = default; ~Tray() = default;
auto update() -> void; auto update() -> void;
operator Gtk::Widget&();
private: private:
void onAdd(std::unique_ptr<Item>& item); void onAdd(std::unique_ptr<Item>& item);
void onRemove(std::unique_ptr<Item>& item); void onRemove(std::unique_ptr<Item>& item);
static inline std::size_t nb_hosts_ = 0; static inline std::size_t nb_hosts_ = 0;
const Json::Value& config_;
Gtk::Box box_; Gtk::Box box_;
SNI::Watcher watcher_; SNI::Watcher watcher_;
SNI::Host host_; SNI::Host host_;

View File

@ -8,7 +8,7 @@ namespace waybar::modules::SNI {
class Watcher { class Watcher {
public: public:
Watcher(); Watcher(std::size_t id);
~Watcher(); ~Watcher();
private: private:

View File

@ -5,7 +5,6 @@
#include <sys/un.h> #include <sys/un.h>
#include <unistd.h> #include <unistd.h>
#include <cstring> #include <cstring>
#include <iostream>
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include "ipc.hpp" #include "ipc.hpp"

View File

@ -5,8 +5,8 @@
#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/sleeper_thread.hpp"
#include "util/json.hpp" #include "util/json.hpp"
#include "util/sleeper_thread.hpp"
namespace waybar::modules::sway { namespace waybar::modules::sway {
@ -20,10 +20,12 @@ class Mode : public ALabel, public sigc::trackable {
void onEvent(const struct Ipc::ipc_response&); void onEvent(const struct Ipc::ipc_response&);
void worker(); void worker();
waybar::util::SleeperThread thread_;
Ipc ipc_;
std::string mode_; std::string mode_;
util::JsonParser parser_; util::JsonParser parser_;
std::mutex mutex_;
util::SleeperThread thread_;
Ipc ipc_;
}; };
} // namespace waybar::modules::sway } // namespace waybar::modules::sway

View File

@ -21,17 +21,21 @@ class Window : public ALabel, public sigc::trackable {
void onEvent(const struct Ipc::ipc_response&); void onEvent(const struct Ipc::ipc_response&);
void onCmd(const struct Ipc::ipc_response&); void onCmd(const struct Ipc::ipc_response&);
void worker(); void worker();
std::tuple<std::size_t, int, std::string, std::string> getFocusedNode(const Json::Value& nodes); std::tuple<std::size_t, int, std::string, std::string> getFocusedNode(const Json::Value& nodes,
std::string& output);
void getTree(); void getTree();
const Bar& bar_; const Bar& bar_;
waybar::util::SleeperThread thread_;
Ipc ipc_;
std::mutex mutex_;
std::string window_; std::string window_;
int windowId_; int windowId_;
std::string app_id_; std::string app_id_;
std::string old_app_id_;
std::size_t app_nb_;
util::JsonParser parser_; util::JsonParser parser_;
std::mutex mutex_;
util::SleeperThread thread_;
Ipc ipc_;
}; };
} // namespace waybar::modules::sway } // namespace waybar::modules::sway

View File

@ -3,7 +3,7 @@
#include <fmt/format.h> #include <fmt/format.h>
#include <gtkmm/button.h> #include <gtkmm/button.h>
#include <gtkmm/label.h> #include <gtkmm/label.h>
#include "IModule.hpp" #include "AModule.hpp"
#include "bar.hpp" #include "bar.hpp"
#include "client.hpp" #include "client.hpp"
#include "modules/sway/ipc/client.hpp" #include "modules/sway/ipc/client.hpp"
@ -12,12 +12,11 @@
namespace waybar::modules::sway { namespace waybar::modules::sway {
class Workspaces : public IModule, public sigc::trackable { class Workspaces : public AModule, 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;
auto update() -> void; auto update() -> void;
operator Gtk::Widget&();
private: private:
void onCmd(const struct Ipc::ipc_response&); void onCmd(const struct Ipc::ipc_response&);
@ -33,15 +32,15 @@ class Workspaces : public IModule, public sigc::trackable {
bool handleScroll(GdkEventScroll*); bool handleScroll(GdkEventScroll*);
const Bar& bar_; const Bar& bar_;
const Json::Value& config_;
std::vector<Json::Value> workspaces_; std::vector<Json::Value> workspaces_;
waybar::util::SleeperThread thread_; std::vector<std::string> workspaces_order_;
std::mutex mutex_;
Gtk::Box box_; Gtk::Box box_;
Ipc ipc_;
util::JsonParser parser_; util::JsonParser parser_;
bool scrolling_;
std::unordered_map<std::string, Gtk::Button> buttons_; std::unordered_map<std::string, Gtk::Button> buttons_;
std::mutex mutex_;
util::SleeperThread thread_;
Ipc ipc_;
}; };
} // namespace waybar::modules::sway } // namespace waybar::modules::sway

View File

@ -18,7 +18,7 @@ class Temperature : public ALabel {
bool isCritical(uint16_t); bool isCritical(uint16_t);
std::string file_path_; std::string file_path_;
waybar::util::SleeperThread thread_; util::SleeperThread thread_;
}; };
} // namespace waybar::modules } // namespace waybar::modules

View File

@ -33,7 +33,7 @@ inline int close(FILE* fp, pid_t pid) {
fclose(fp); fclose(fp);
while (waitpid(pid, &stat, 0) == -1) { while (waitpid(pid, &stat, 0) == -1) {
if (errno != EINTR) { if (errno != EINTR) {
stat = -1; stat = 0;
break; break;
} }
} }
@ -90,6 +90,8 @@ inline int32_t forkExec(std::string cmd) {
setpgid(pid, pid); setpgid(pid, pid);
execl("/bin/sh", "sh", "-c", cmd.c_str(), (char*)0); execl("/bin/sh", "sh", "-c", cmd.c_str(), (char*)0);
exit(0); exit(0);
} else {
signal(SIGCHLD,SIG_IGN);
} }
return pid; return pid;

View File

@ -14,7 +14,10 @@ class SleeperThread {
SleeperThread(std::function<void()> func) SleeperThread(std::function<void()> func)
: thread_{[this, func] { : thread_{[this, func] {
while (do_run_) func(); while (do_run_) {
signal_ = false;
func();
}
}} {} }} {}
SleeperThread& operator=(std::function<void()> func) { SleeperThread& operator=(std::function<void()> func) {
@ -42,7 +45,10 @@ class SleeperThread {
} }
auto wake_up() { auto wake_up() {
{
std::lock_guard<std::mutex> lck(mutex_);
signal_ = true; signal_ = true;
}
condvar_.notify_all(); condvar_.notify_all();
} }

View File

@ -1,6 +1,6 @@
project( project(
'waybar', 'cpp', 'c', 'waybar', 'cpp', 'c',
version: '0.6.5', version: '0.7.0',
license: 'MIT', license: 'MIT',
default_options : [ default_options : [
'cpp_std=c++17', 'cpp_std=c++17',
@ -48,10 +48,10 @@ add_global_link_arguments(cpp_link_args, language : 'cpp')
thread_dep = dependency('threads') thread_dep = dependency('threads')
libinput = dependency('libinput') libinput = dependency('libinput')
fmt = dependency('fmt', version : ['>=5.3.0'], fallback : ['fmt', 'fmt_dep']) fmt = dependency('fmt', version : ['>=5.3.0'], fallback : ['fmt', 'fmt_dep'])
spdlog = dependency('spdlog', version : ['>=1.3.1'], fallback : ['spdlog', 'spdlog_dep'])
wayland_client = dependency('wayland-client') wayland_client = dependency('wayland-client')
wayland_cursor = dependency('wayland-cursor') wayland_cursor = dependency('wayland-cursor')
wayland_protos = dependency('wayland-protocols') wayland_protos = dependency('wayland-protocols')
wlroots = dependency('wlroots', fallback: ['wlroots', 'wlroots'])
gtkmm = dependency('gtkmm-3.0') gtkmm = dependency('gtkmm-3.0')
dbusmenu_gtk = dependency('dbusmenu-gtk3-0.4', required: get_option('dbusmenu-gtk')) dbusmenu_gtk = dependency('dbusmenu-gtk3-0.4', required: get_option('dbusmenu-gtk'))
giounix = dependency('gio-unix-2.0', required: get_option('dbusmenu-gtk')) giounix = dependency('gio-unix-2.0', required: get_option('dbusmenu-gtk'))
@ -65,6 +65,7 @@ libmpdclient = dependency('libmpdclient', required: get_option('mpd'))
src_files = files( src_files = files(
'src/factory.cpp', 'src/factory.cpp',
'src/AModule.cpp',
'src/ALabel.cpp', 'src/ALabel.cpp',
'src/modules/memory.cpp', 'src/modules/memory.cpp',
'src/modules/battery.cpp', 'src/modules/battery.cpp',
@ -125,10 +126,10 @@ executable(
src_files, src_files,
dependencies: [ dependencies: [
thread_dep, thread_dep,
wlroots,
client_protos, client_protos,
wayland_client, wayland_client,
fmt, fmt,
spdlog,
sigcpp, sigcpp,
jsoncpp, jsoncpp,
libinput, libinput,

View File

@ -96,6 +96,7 @@
"format": "{capacity}% {icon}", "format": "{capacity}% {icon}",
"format-charging": "{capacity}% ", "format-charging": "{capacity}% ",
"format-plugged": "{capacity}% ", "format-plugged": "{capacity}% ",
"format-alt": "{time} {icon}",
// "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": ["", "", "", "", ""]
@ -108,13 +109,16 @@
"format-wifi": "{essid} ({signalStrength}%) ", "format-wifi": "{essid} ({signalStrength}%) ",
"format-ethernet": "{ifname}: {ipaddr}/{cidr} ", "format-ethernet": "{ifname}: {ipaddr}/{cidr} ",
"format-linked": "{ifname} (No IP) ", "format-linked": "{ifname} (No IP) ",
"format-disconnected": "Disconnected ⚠" "format-disconnected": "Disconnected ⚠",
"format-alt": "{ifname}: {ipaddr}/{cidr}"
}, },
"pulseaudio": { "pulseaudio": {
// "scroll-step": 1, // %, can be a float // "scroll-step": 1, // %, can be a float
"format": "{volume}% {icon}", "format": "{volume}% {icon} {format_source}",
"format-bluetooth": "{volume}% {icon}", "format-bluetooth": "{volume}% {icon} {format_source}",
"format-muted": "", "format-muted": " {format_source}",
"format-source": "{volume}% ",
"format-source-muted": "",
"format-icons": { "format-icons": {
"headphones": "", "headphones": "",
"handsfree": "", "handsfree": "",

View File

@ -39,7 +39,7 @@ def on_metadata(player, metadata, manager):
track_info = '{artist} - {title}'.format(artist=player.get_artist(), track_info = '{artist} - {title}'.format(artist=player.get_artist(),
title=player.get_title()) title=player.get_title())
if player.props.status != 'Playing': if player.props.status != 'Playing' and track_info:
track_info = '' + track_info track_info = '' + track_info
write_output(track_info, player) write_output(track_info, player)

View File

@ -7,10 +7,10 @@
} }
window#waybar { window#waybar {
background: rgba(43, 48, 59, 0.5); background-color: rgba(43, 48, 59, 0.5);
border-bottom: 3px solid rgba(100, 114, 125, 0.5); border-bottom: 3px solid rgba(100, 114, 125, 0.5);
color: #ffffff; color: #ffffff;
transition-property: background, background-color; transition-property: background-color;
transition-duration: .5s; transition-duration: .5s;
} }
@ -20,10 +20,10 @@ window#waybar.hidden {
/* /*
window#waybar.empty { window#waybar.empty {
background: transparent; background-color: transparent;
} }
window#waybar.solo { window#waybar.solo {
background: #FFFFFF; background-color: #FFFFFF;
} }
*/ */
@ -39,13 +39,13 @@ window#waybar.chromium {
/* https://github.com/Alexays/Waybar/wiki/FAQ#the-workspace-buttons-have-a-strange-hover-effect */ /* https://github.com/Alexays/Waybar/wiki/FAQ#the-workspace-buttons-have-a-strange-hover-effect */
#workspaces button { #workspaces button {
padding: 0 5px; padding: 0 5px;
background: transparent; background-color: transparent;
color: #ffffff; color: #ffffff;
border-bottom: 3px solid transparent; border-bottom: 3px solid transparent;
} }
#workspaces button.focused { #workspaces button.focused {
background: #64727D; background-color: #64727D;
border-bottom: 3px solid #ffffff; border-bottom: 3px solid #ffffff;
} }
@ -54,13 +54,24 @@ window#waybar.chromium {
} }
#mode { #mode {
background: #64727D; background-color: #64727D;
border-bottom: 3px solid #ffffff; border-bottom: 3px solid #ffffff;
} }
#clock, #battery, #cpu, #memory, #temperature, #backlight, #network, #pulseaudio, #custom-media, #tray, #mode, #idle_inhibitor { #clock,
#battery,
#cpu,
#memory,
#temperature,
#backlight,
#network,
#pulseaudio,
#custom-media,
#tray,
#mode,
#idle_inhibitor {
padding: 0 10px; padding: 0 10px;
margin: 0 5px; margin: 0 4px;
color: #ffffff; color: #ffffff;
} }
@ -86,7 +97,7 @@ window#waybar.chromium {
} }
#battery.critical:not(.charging) { #battery.critical:not(.charging) {
background: #f53c3c; background-color: #f53c3c;
color: #ffffff; color: #ffffff;
animation-name: blink; animation-name: blink;
animation-duration: 0.5s; animation-duration: 0.5s;
@ -100,56 +111,56 @@ label:focus {
} }
#cpu { #cpu {
background: #2ecc71; background-color: #2ecc71;
color: #000000; color: #000000;
} }
#memory { #memory {
background: #9b59b6; background-color: #9b59b6;
} }
#backlight { #backlight {
background: #90b1b1; background-color: #90b1b1;
} }
#network { #network {
background: #2980b9; background-color: #2980b9;
} }
#network.disconnected { #network.disconnected {
background: #f53c3c; background-color: #f53c3c;
} }
#pulseaudio { #pulseaudio {
background: #f1c40f; background-color: #f1c40f;
color: #000000; color: #000000;
} }
#pulseaudio.muted { #pulseaudio.muted {
background: #90b1b1; background-color: #90b1b1;
color: #2a5c45; color: #2a5c45;
} }
#custom-media { #custom-media {
background: #66cc99; background-color: #66cc99;
color: #2a5c45; color: #2a5c45;
min-width: 100px; min-width: 100px;
} }
.custom-spotify { #custom-media.custom-spotify {
background: #66cc99; background-color: #66cc99;
} }
.custom-vlc { #custom-media.custom-vlc {
background: #ffa000; background-color: #ffa000;
} }
#temperature { #temperature {
background: #f0932b; background-color: #f0932b;
} }
#temperature.critical { #temperature.critical {
background: #eb4d4b; background-color: #eb4d4b;
} }
#tray { #tray {
@ -166,18 +177,18 @@ label:focus {
} }
#mpd { #mpd {
background: #66cc99; background-color: #66cc99;
color: #2a5c45; color: #2a5c45;
} }
#mpd.disconnected { #mpd.disconnected {
background: #f53c3c; background-color: #f53c3c;
} }
#mpd.stopped { #mpd.stopped {
background: #90b1b1; background-color: #90b1b1;
} }
#mpd.paused { #mpd.paused {
background: #51a37a; background-color: #51a37a;
} }

View File

@ -2,14 +2,21 @@
#include <fmt/format.h> #include <fmt/format.h>
#include <util/command.hpp> #include <util/command.hpp>
waybar::ALabel::ALabel(const Json::Value& config, const std::string& format, uint16_t interval) namespace waybar {
: config_(config),
ALabel::ALabel(const Json::Value& config, const std::string& name, const std::string& id,
const std::string& format, uint16_t interval)
: AModule(config, name, id, config["format-alt"].isString()),
format_(config_["format"].isString() ? config_["format"].asString() : format), format_(config_["format"].isString() ? config_["format"].asString() : format),
interval_(config_["interval"] == "once" interval_(config_["interval"] == "once"
? std::chrono::seconds(100000000) ? std::chrono::seconds(100000000)
: std::chrono::seconds( : std::chrono::seconds(
config_["interval"].isUInt() ? config_["interval"].asUInt() : interval)), config_["interval"].isUInt() ? config_["interval"].asUInt() : interval)),
default_format_(format_) { default_format_(format_) {
label_.set_name(name);
if (!id.empty()) {
label_.get_style_context()->add_class(id);
}
event_box_.add(label_); event_box_.add(label_);
if (config_["max-length"].isUInt()) { if (config_["max-length"].isUInt()) {
label_.set_max_width_chars(config_["max-length"].asUInt()); label_.set_max_width_chars(config_["max-length"].asUInt());
@ -19,97 +26,13 @@ waybar::ALabel::ALabel(const Json::Value& config, const std::string& format, uin
if (config_["rotate"].isUInt()) { if (config_["rotate"].isUInt()) {
label_.set_angle(config["rotate"].asUInt()); label_.set_angle(config["rotate"].asUInt());
} }
if (config_["format-alt"].isString()) {
event_box_.add_events(Gdk::BUTTON_PRESS_MASK);
event_box_.signal_button_press_event().connect(sigc::mem_fun(*this, &ALabel::handleToggle));
}
// configure events' user commands
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_.signal_button_press_event().connect(sigc::mem_fun(*this, &ALabel::handleToggle));
}
if (config_["on-scroll-up"].isString() || config_["on-scroll-down"].isString()) {
event_box_.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK);
event_box_.signal_scroll_event().connect(sigc::mem_fun(*this, &ALabel::handleScroll));
}
} }
waybar::ALabel::~ALabel() { auto ALabel::update() -> void {
for (const auto& pid : pid_) {
if (pid != -1) {
kill(-pid, 9);
}
}
}
auto waybar::ALabel::update() -> void {
// Nothing here // Nothing here
} }
bool waybar::ALabel::handleToggle(GdkEventButton* const& e) { std::string ALabel::getIcon(uint16_t percentage, const std::string& alt, uint16_t max) {
std::string format;
if (config_["on-click"].isString() && e->button == 1) {
format = config_["on-click"].asString();
} else if (config_["on-click-middle"].isString() && e->button == 2) {
format = config_["on-click-middle"].asString();
} else if (config_["on-click-right"].isString() && e->button == 3) {
format = config_["on-click-right"].asString();
} else if (config_["on-click-forward"].isString() && e->button == 8) {
format = config_["on-click-backward"].asString();
} else if (config_["on-click-backward"].isString() && e->button == 9) {
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()) {
alt_ = !alt_;
if (alt_ && config_["format-alt"].isString()) {
format_ = config_["format-alt"].asString();
} else {
format_ = default_format_;
}
}
dp.emit();
return true;
}
bool waybar::ALabel::handleScroll(GdkEventScroll* e) {
// Avoid concurrent scroll event
std::lock_guard<std::mutex> lock(mutex_);
bool direction_up = false;
if (e->direction == GDK_SCROLL_UP) {
direction_up = true;
}
if (e->direction == GDK_SCROLL_DOWN) {
direction_up = false;
}
if (e->direction == GDK_SCROLL_SMOOTH) {
gdouble delta_x, delta_y;
gdk_event_get_scroll_deltas(reinterpret_cast<const GdkEvent*>(e), &delta_x, &delta_y);
if (delta_y < 0) {
direction_up = true;
} else if (delta_y > 0) {
direction_up = false;
}
}
if (direction_up && config_["on-scroll-up"].isString()) {
pid_.push_back(waybar::util::command::forkExec(config_["on-scroll-up"].asString()));
} else if (config_["on-scroll-down"].isString()) {
pid_.push_back(waybar::util::command::forkExec(config_["on-scroll-down"].asString()));
}
dp.emit();
return true;
}
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())) {
@ -129,7 +52,19 @@ std::string waybar::ALabel::getIcon(uint16_t percentage, const std::string& alt,
return ""; return "";
} }
std::string waybar::ALabel::getState(uint8_t value, bool lesser) { bool waybar::ALabel::handleToggle(GdkEventButton* const& e) {
if (config_["format-alt-click"].isUInt() && e->button == config_["format-alt-click"].asUInt()) {
alt_ = !alt_;
if (alt_ && config_["format-alt"].isString()) {
format_ = config_["format-alt"].asString();
} else {
format_ = default_format_;
}
}
return AModule::handleToggle(e);
}
std::string ALabel::getState(uint8_t value, bool lesser) {
if (!config_["states"].isObject()) { if (!config_["states"].isObject()) {
return ""; return "";
} }
@ -158,8 +93,4 @@ std::string waybar::ALabel::getState(uint8_t value, bool lesser) {
return valid_state; return valid_state;
} }
bool waybar::ALabel::tooltipEnabled() { } // namespace waybar
return config_["tooltip"].isBool() ? config_["tooltip"].asBool() : true;
}
waybar::ALabel::operator Gtk::Widget&() { return event_box_; }

119
src/AModule.cpp Normal file
View File

@ -0,0 +1,119 @@
#include "AModule.hpp"
#include <fmt/format.h>
#include <util/command.hpp>
namespace waybar {
AModule::AModule(const Json::Value& config, const std::string& name, const std::string& id,
bool enable_click, bool enable_scroll)
: config_(std::move(config)) {
// configure events' user commands
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() || enable_click) {
event_box_.add_events(Gdk::BUTTON_PRESS_MASK);
event_box_.signal_button_press_event().connect(sigc::mem_fun(*this, &AModule::handleToggle));
}
if (config_["on-scroll-up"].isString() || config_["on-scroll-down"].isString() || enable_scroll) {
event_box_.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK);
event_box_.signal_scroll_event().connect(sigc::mem_fun(*this, &AModule::handleScroll));
}
}
AModule::~AModule() {
for (const auto& pid : pid_) {
if (pid != -1) {
kill(-pid, 9);
}
}
}
auto AModule::update() -> void {
// Nothing here
}
bool AModule::handleToggle(GdkEventButton* const& e) {
std::string format;
if (config_["on-click"].isString() && e->button == 1) {
format = config_["on-click"].asString();
} else if (config_["on-click-middle"].isString() && e->button == 2) {
format = config_["on-click-middle"].asString();
} else if (config_["on-click-right"].isString() && e->button == 3) {
format = config_["on-click-right"].asString();
} else if (config_["on-click-forward"].isString() && e->button == 8) {
format = config_["on-click-backward"].asString();
} else if (config_["on-click-backward"].isString() && e->button == 9) {
format = config_["on-click-forward"].asString();
}
if (!format.empty()) {
pid_.push_back(util::command::forkExec(fmt::format(format, fmt::arg("arg", click_param_))));
}
dp.emit();
return true;
}
AModule::SCROLL_DIR AModule::getScrollDir(GdkEventScroll* e) {
switch (e -> direction) {
case GDK_SCROLL_UP: return SCROLL_DIR::UP;
case GDK_SCROLL_DOWN: return SCROLL_DIR::DOWN;
case GDK_SCROLL_LEFT: return SCROLL_DIR::LEFT;
case GDK_SCROLL_RIGHT: return SCROLL_DIR::RIGHT;
case GDK_SCROLL_SMOOTH: {
SCROLL_DIR dir{SCROLL_DIR::NONE};
distance_scrolled_y_ += e->delta_y;
distance_scrolled_x_ += e->delta_x;
gdouble threshold = 0;
if (config_["smooth-scrolling-threshold"].isNumeric()) {
threshold = config_["smooth-scrolling-threshold"].asDouble();
}
if (distance_scrolled_y_ < -threshold) {
dir = SCROLL_DIR::UP;
} else if (distance_scrolled_y_ > threshold) {
dir = SCROLL_DIR::DOWN;
} else if (distance_scrolled_x_ > threshold) {
dir = SCROLL_DIR::RIGHT;
} else if (distance_scrolled_x_ < -threshold) {
dir = SCROLL_DIR::LEFT;
}
switch (dir) {
case SCROLL_DIR::UP:
case SCROLL_DIR::DOWN:
distance_scrolled_y_ = 0;
break;
case SCROLL_DIR::LEFT:
case SCROLL_DIR::RIGHT:
distance_scrolled_x_ = 0;
break;
case SCROLL_DIR::NONE:
break;
}
return dir;
}
// Silence -Wreturn-type:
default: return SCROLL_DIR::NONE;
}
}
bool AModule::handleScroll(GdkEventScroll* e) {
auto dir = getScrollDir(e);
if (dir == SCROLL_DIR::UP && config_["on-scroll-up"].isString()) {
pid_.push_back(util::command::forkExec(config_["on-scroll-up"].asString()));
} else if (dir == SCROLL_DIR::DOWN && config_["on-scroll-down"].isString()) {
pid_.push_back(util::command::forkExec(config_["on-scroll-down"].asString()));
}
dp.emit();
return true;
}
bool AModule::tooltipEnabled() {
return config_["tooltip"].isBool() ? config_["tooltip"].asBool() : true;
}
AModule::operator Gtk::Widget&() { return event_box_; }
} // namespace waybar

View File

@ -1,4 +1,5 @@
#include "bar.hpp" #include "bar.hpp"
#include <spdlog/spdlog.h>
#include "client.hpp" #include "client.hpp"
#include "factory.hpp" #include "factory.hpp"
@ -8,6 +9,7 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config)
window{Gtk::WindowType::WINDOW_TOPLEVEL}, window{Gtk::WindowType::WINDOW_TOPLEVEL},
surface(nullptr), surface(nullptr),
layer_surface(nullptr), layer_surface(nullptr),
anchor_(ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP),
left_(Gtk::ORIENTATION_HORIZONTAL, 0), left_(Gtk::ORIENTATION_HORIZONTAL, 0),
center_(Gtk::ORIENTATION_HORIZONTAL, 0), center_(Gtk::ORIENTATION_HORIZONTAL, 0),
right_(Gtk::ORIENTATION_HORIZONTAL, 0), right_(Gtk::ORIENTATION_HORIZONTAL, 0),
@ -15,46 +17,33 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config)
window.set_title("waybar"); window.set_title("waybar");
window.set_name("waybar"); window.set_name("waybar");
window.set_decorated(false); window.set_decorated(false);
window.get_style_context()->add_class(output->name);
if (config["position"] == "right" || config["position"] == "left") { if (config["position"] == "right" || config["position"] == "left") {
height_ = 0; height_ = 0;
width_ = 1; width_ = 1;
} }
height_ = config["height"].isUInt() ? config["height"].asUInt() : height_;
width_ = config["width"].isUInt() ? config["width"].asUInt() : width_;
auto gtk_window = window.gobj(); window.signal_realize().connect_notify(sigc::mem_fun(*this, &Bar::onRealize));
auto gtk_widget = GTK_WIDGET(gtk_window); window.signal_map_event().connect_notify(sigc::mem_fun(*this, &Bar::onMap));
gtk_widget_realize(gtk_widget); window.signal_configure_event().connect_notify(sigc::mem_fun(*this, &Bar::onConfigure));
auto gdk_window = window.get_window()->gobj(); window.set_size_request(width_, height_);
gdk_wayland_window_set_use_custom_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 : ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM;
auto client = waybar::Client::inst();
layer_surface = zwlr_layer_shell_v1_get_layer_surface(
client->layer_shell, surface, output->output, layer, "waybar");
static const struct zwlr_layer_surface_v1_listener layer_surface_listener = {
.configure = layerSurfaceHandleConfigure,
.closed = layerSurfaceHandleClosed,
};
zwlr_layer_surface_v1_add_listener(layer_surface, &layer_surface_listener, this);
auto height = config["height"].isUInt() ? config["height"].asUInt() : height_;
auto width = config["width"].isUInt() ? config["width"].asUInt() : width_;
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;
} else if (config["position"] == "left") { } else if (config["position"] == "left") {
anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT; anchor_ = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT;
} else if (config["position"] == "right") { } else if (config["position"] == "right") {
anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; anchor_ = ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
} }
if (anchor == ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM || anchor == ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP) { if (anchor_ == ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM ||
anchor |= ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; anchor_ == ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP) {
} else if (anchor == ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT || anchor_ |= ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
anchor == ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT) { } else if (anchor_ == ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT ||
anchor |= ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; anchor_ == ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT) {
anchor_ |= ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
left_ = Gtk::Box(Gtk::ORIENTATION_VERTICAL, 0); left_ = Gtk::Box(Gtk::ORIENTATION_VERTICAL, 0);
center_ = Gtk::Box(Gtk::ORIENTATION_VERTICAL, 0); center_ = Gtk::Box(Gtk::ORIENTATION_VERTICAL, 0);
right_ = Gtk::Box(Gtk::ORIENTATION_VERTICAL, 0); right_ = Gtk::Box(Gtk::ORIENTATION_VERTICAL, 0);
@ -62,17 +51,71 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config)
vertical = true; vertical = true;
} }
zwlr_layer_surface_v1_set_anchor(layer_surface, anchor); setupWidgets();
zwlr_layer_surface_v1_set_size(layer_surface, width, height);
setMarginsAndZone(height, width); if (window.get_realized()) {
onRealize();
}
window.show_all();
}
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) {
spdlog::warn(MIN_HEIGHT_MSG, height_, ev->height);
}
if (config["height"].isUInt()) {
spdlog::info(SIZE_DEFINED, "Height");
} else {
tmp_height = ev->height;
}
}
if (ev->width > static_cast<int>(width_)) {
// Default minimal value
if (width_ != 1) {
spdlog::warn(MIN_WIDTH_MSG, width_, ev->width);
}
if (config["width"].isUInt()) {
spdlog::info(SIZE_DEFINED, "Width");
} else {
tmp_width = ev->width;
}
}
if (tmp_width != width_ || tmp_height != height_) {
zwlr_layer_surface_v1_set_size(layer_surface, tmp_width, tmp_height);
}
}
void waybar::Bar::onRealize() {
auto gdk_window = window.get_window()->gobj();
gdk_wayland_window_set_use_custom_surface(gdk_window);
}
void waybar::Bar::onMap(GdkEventAny* ev) {
auto gdk_window = window.get_window()->gobj();
surface = gdk_wayland_window_get_wl_surface(gdk_window);
auto client = waybar::Client::inst();
auto layer =
config["layer"] == "top" ? ZWLR_LAYER_SHELL_V1_LAYER_TOP : ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM;
layer_surface = zwlr_layer_shell_v1_get_layer_surface(
client->layer_shell, surface, output->output, layer, "waybar");
zwlr_layer_surface_v1_set_keyboard_interactivity(layer_surface, false);
zwlr_layer_surface_v1_set_anchor(layer_surface, anchor_);
zwlr_layer_surface_v1_set_size(layer_surface, width_, height_);
setMarginsAndZone(height_, width_);
static const struct zwlr_layer_surface_v1_listener layer_surface_listener = {
.configure = layerSurfaceHandleConfigure,
.closed = layerSurfaceHandleClosed,
};
zwlr_layer_surface_v1_add_listener(layer_surface, &layer_surface_listener, this);
wl_surface_commit(surface); wl_surface_commit(surface);
wl_display_roundtrip(client->wl_display); wl_display_roundtrip(client->wl_display);
setupWidgets();
window.set_size_request(width_, height_);
window.signal_configure_event().connect_notify(sigc::mem_fun(*this, &Bar::onConfigure));
} }
void waybar::Bar::setMarginsAndZone(uint32_t height, uint32_t width) { void waybar::Bar::setMarginsAndZone(uint32_t height, uint32_t width) {
@ -114,7 +157,7 @@ void waybar::Bar::setMarginsAndZone(uint32_t height, uint32_t width) {
.left = std::stoi(margins[3], nullptr, 10)}; .left = std::stoi(margins[3], nullptr, 10)};
} }
} catch (...) { } catch (...) {
std::cerr << "Invalid margins: " + config["margin"].asString() << std::endl; spdlog::warn("Invalid margins: {}", config["margin"].asString());
} }
} else if (config["margin"].isInt()) { } else if (config["margin"].isInt()) {
auto gaps = config["margin"].asInt(); auto gaps = config["margin"].asInt();
@ -126,36 +169,6 @@ void waybar::Bar::setMarginsAndZone(uint32_t height, uint32_t width) {
zwlr_layer_surface_v1_set_exclusive_zone(layer_surface, zone); 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
void waybar::Bar::setupAltFormatKeyForModule(const std::string& module_name) { void waybar::Bar::setupAltFormatKeyForModule(const std::string& module_name) {
if (config.isMember(module_name)) { if (config.isMember(module_name)) {
@ -227,11 +240,10 @@ void waybar::Bar::layerSurfaceHandleConfigure(void* data, struct zwlr_layer_surf
o->window.resize(o->width_, o->height_); o->window.resize(o->width_, o->height_);
auto zone = o->vertical ? width + o->margins_.right : height + o->margins_.bottom; auto zone = o->vertical ? width + o->margins_.right : height + o->margins_.bottom;
zwlr_layer_surface_v1_set_exclusive_zone(o->layer_surface, zone); zwlr_layer_surface_v1_set_exclusive_zone(o->layer_surface, zone);
std::cout << fmt::format(BAR_SIZE_MSG, spdlog::info(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_),
o->output->name) o->output->name);
<< std::endl;
wl_surface_commit(o->surface); wl_surface_commit(o->surface);
} }
zwlr_layer_surface_v1_ack_configure(surface, serial); zwlr_layer_surface_v1_ack_configure(surface, serial);
@ -239,7 +251,10 @@ void waybar::Bar::layerSurfaceHandleConfigure(void* data, struct zwlr_layer_surf
void waybar::Bar::layerSurfaceHandleClosed(void* data, struct zwlr_layer_surface_v1* /*surface*/) { void waybar::Bar::layerSurfaceHandleClosed(void* data, struct zwlr_layer_surface_v1* /*surface*/) {
auto o = static_cast<waybar::Bar*>(data); auto o = static_cast<waybar::Bar*>(data);
if (o->layer_surface) {
zwlr_layer_surface_v1_destroy(o->layer_surface); zwlr_layer_surface_v1_destroy(o->layer_surface);
o->layer_surface = nullptr;
}
o->modules_left_.clear(); o->modules_left_.clear();
o->modules_center_.clear(); o->modules_center_.clear();
o->modules_right_.clear(); o->modules_right_.clear();
@ -272,17 +287,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] {
// Fix https://github.com/Alexays/Waybar/issues/320, proper way?
Glib::signal_idle().connect_once([module, &name] {
try { try {
module->update(); module->update();
} catch (const std::exception& e) { } catch (const std::exception& e) {
std::cerr << name.asString() + ": " + e.what() << std::endl; spdlog::error("{}: {}", name.asString(), e.what());
} }
}); });
});
} catch (const std::exception& e) { } catch (const std::exception& e) {
std::cerr << e.what() << std::endl; spdlog::warn("module {}: {}", name.asString(), e.what());
} }
} }
} }
@ -313,5 +325,4 @@ auto waybar::Bar::setupWidgets() -> void {
for (auto const& module : modules_right_) { for (auto const& module : modules_right_) {
right_.pack_end(*module, false, false, 0); right_.pack_end(*module, false, false, 0);
} }
window.show_all();
} }

View File

@ -1,4 +1,5 @@
#include "client.hpp" #include "client.hpp"
#include <spdlog/spdlog.h>
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include "util/clara.hpp" #include "util/clara.hpp"
@ -9,7 +10,7 @@ waybar::Client *waybar::Client::inst() {
return c; return c;
} }
const std::string waybar::Client::getValidPath(const std::vector<std::string> &paths) { const std::string waybar::Client::getValidPath(const std::vector<std::string> &paths) const {
wordexp_t p; wordexp_t p;
for (const std::string &path : paths) { for (const std::string &path : paths) {
@ -55,7 +56,7 @@ void waybar::Client::handleGlobalRemove(void * data, struct wl_registry * /*re
auto output_name = (*it)->output->name; auto output_name = (*it)->output->name;
(*it)->window.close(); (*it)->window.close();
it = client->bars.erase(it); it = client->bars.erase(it);
std::cout << "Bar removed from output: " + output_name << std::endl; spdlog::info("Bar removed from output: {}", output_name);
} else { } else {
++it; ++it;
} }
@ -171,8 +172,9 @@ void waybar::Client::handleDescription(void * /*data*/, struct zxdg_output_v1 *
// Nothing here // Nothing here
} }
void waybar::Client::setupConfigs(const std::string &config, const std::string &style) { std::tuple<const std::string, const std::string> waybar::Client::getConfigs(
config_file_ = config.empty() ? getValidPath({ const std::string &config, const std::string &style) const {
auto config_file = config.empty() ? getValidPath({
"$XDG_CONFIG_HOME/waybar/config", "$XDG_CONFIG_HOME/waybar/config",
"$HOME/.config/waybar/config", "$HOME/.config/waybar/config",
"$HOME/waybar/config", "$HOME/waybar/config",
@ -180,7 +182,7 @@ void waybar::Client::setupConfigs(const std::string &config, const std::string &
"./resources/config", "./resources/config",
}) })
: config; : config;
css_file_ = style.empty() ? getValidPath({ auto css_file = style.empty() ? getValidPath({
"$XDG_CONFIG_HOME/waybar/style.css", "$XDG_CONFIG_HOME/waybar/style.css",
"$HOME/.config/waybar/style.css", "$HOME/.config/waybar/style.css",
"$HOME/waybar/style.css", "$HOME/waybar/style.css",
@ -188,14 +190,15 @@ void waybar::Client::setupConfigs(const std::string &config, const std::string &
"./resources/style.css", "./resources/style.css",
}) })
: style; : style;
if (css_file_.empty() || config_file_.empty()) { if (css_file.empty() || config_file.empty()) {
throw std::runtime_error("Missing required resources files"); throw std::runtime_error("Missing required resources files");
} }
std::cout << "Resources files: " + config_file_ + ", " + css_file_ << std::endl; spdlog::info("Resources files: {}, {}", config_file, css_file);
return {config_file, css_file};
} }
auto waybar::Client::setupConfig() -> void { auto waybar::Client::setupConfig(const std::string &config_file) -> void {
std::ifstream file(config_file_); std::ifstream file(config_file);
if (!file.is_open()) { if (!file.is_open()) {
throw std::runtime_error("Can't open config file"); throw std::runtime_error("Can't open config file");
} }
@ -204,12 +207,12 @@ auto waybar::Client::setupConfig() -> void {
config_ = parser.parse(str); config_ = parser.parse(str);
} }
auto waybar::Client::setupCss() -> void { auto waybar::Client::setupCss(const std::string &css_file) -> void {
css_provider_ = Gtk::CssProvider::create(); css_provider_ = Gtk::CssProvider::create();
style_context_ = Gtk::StyleContext::create(); style_context_ = Gtk::StyleContext::create();
// Load our css file, wherever that may be hiding // Load our css file, wherever that may be hiding
if (!css_provider_->load_from_path(css_file_)) { if (!css_provider_->load_from_path(css_file)) {
throw std::runtime_error("Can't open style file"); throw std::runtime_error("Can't open style file");
} }
} }
@ -228,28 +231,23 @@ void waybar::Client::bindInterfaces() {
} }
int waybar::Client::main(int argc, char *argv[]) { int waybar::Client::main(int argc, char *argv[]) {
gtk_app = Gtk::Application::create(argc, argv, "fr.arouillard.waybar");
gdk_display = Gdk::Display::get_default();
if (!gdk_display) {
throw std::runtime_error("Can't find display");
}
if (!GDK_IS_WAYLAND_DISPLAY(gdk_display->gobj())) {
throw std::runtime_error("Bar need to run under Wayland");
}
wl_display = gdk_wayland_display_get_wl_display(gdk_display->gobj());
bool show_help = false; bool show_help = false;
bool show_version = false; bool show_version = false;
std::string config; std::string config;
std::string style; std::string style;
std::string bar_id; std::string bar_id;
std::string log_level;
auto cli = clara::detail::Help(show_help) | auto cli = clara::detail::Help(show_help) |
clara::detail::Opt(show_version)["-v"]["--version"]("Show version") | clara::detail::Opt(show_version)["-v"]["--version"]("Show version") |
clara::detail::Opt(config, "config")["-c"]["--config"]("Config path") | clara::detail::Opt(config, "config")["-c"]["--config"]("Config path") |
clara::detail::Opt(style, "style")["-s"]["--style"]("Style path") | clara::detail::Opt(style, "style")["-s"]["--style"]("Style path") |
clara::detail::Opt(
log_level,
"trace|debug|info|warning|error|critical|off")["-l"]["--log-level"]("Log level") |
clara::detail::Opt(bar_id, "id")["-b"]["--bar"]("Bar id"); clara::detail::Opt(bar_id, "id")["-b"]["--bar"]("Bar id");
auto res = cli.parse(clara::detail::Args(argc, argv)); auto res = cli.parse(clara::detail::Args(argc, argv));
if (!res) { if (!res) {
std::cerr << "Error in command line: " << res.errorMessage() << std::endl; spdlog::error("Error in command line: {}", res.errorMessage());
return 1; return 1;
} }
if (show_help) { if (show_help) {
@ -260,9 +258,21 @@ int waybar::Client::main(int argc, char *argv[]) {
std::cout << "Waybar v" << VERSION << std::endl; std::cout << "Waybar v" << VERSION << std::endl;
return 0; return 0;
} }
setupConfigs(config, style); if (!log_level.empty()) {
setupConfig(); spdlog::set_level(spdlog::level::from_str(log_level));
setupCss(); }
gtk_app = Gtk::Application::create(argc, argv, "fr.arouillard.waybar");
gdk_display = Gdk::Display::get_default();
if (!gdk_display) {
throw std::runtime_error("Can't find display");
}
if (!GDK_IS_WAYLAND_DISPLAY(gdk_display->gobj())) {
throw std::runtime_error("Bar need to run under Wayland");
}
wl_display = gdk_wayland_display_get_wl_display(gdk_display->gobj());
auto [config_file, css_file] = getConfigs(config, style);
setupConfig(config_file);
setupCss(css_file);
bindInterfaces(); bindInterfaces();
gtk_app->hold(); gtk_app->hold();
gtk_app->run(); gtk_app->run();

View File

@ -2,7 +2,7 @@
waybar::Factory::Factory(const Bar& bar, const Json::Value& config) : bar_(bar), config_(config) {} waybar::Factory::Factory(const Bar& bar, const Json::Value& config) : bar_(bar), config_(config) {}
waybar::IModule* waybar::Factory::makeModule(const std::string& name) const { waybar::AModule* waybar::Factory::makeModule(const std::string& name) const {
try { try {
auto hash_pos = name.find('#'); auto hash_pos = name.find('#');
auto ref = name.substr(0, hash_pos); auto ref = name.substr(0, hash_pos);
@ -64,7 +64,7 @@ waybar::IModule* waybar::Factory::makeModule(const std::string& name) const {
return new waybar::modules::Temperature(id, config_[name]); return new waybar::modules::Temperature(id, config_[name]);
} }
if (ref.compare(0, 7, "custom/") == 0 && ref.size() > 7) { if (ref.compare(0, 7, "custom/") == 0 && ref.size() > 7) {
return new waybar::modules::Custom(ref.substr(7), config_[name]); return new waybar::modules::Custom(ref.substr(7), id, config_[name]);
} }
} catch (const std::exception& e) { } catch (const std::exception& e) {
auto err = fmt::format("Disabling module \"{}\", {}", name, e.what()); auto err = fmt::format("Disabling module \"{}\", {}", name, e.what());

View File

@ -1,5 +1,5 @@
#include <csignal> #include <csignal>
#include <iostream> #include <spdlog/spdlog.h>
#include "client.hpp" #include "client.hpp"
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
@ -23,10 +23,10 @@ int main(int argc, char* argv[]) {
delete client; delete client;
return ret; return ret;
} catch (const std::exception& e) { } catch (const std::exception& e) {
std::cerr << e.what() << std::endl; spdlog::error("{}", e.what());
return 1; return 1;
} catch (const Glib::Exception& e) { } catch (const Glib::Exception& e) {
std::cerr << e.what().c_str() << std::endl; spdlog::error("{}", static_cast<std::string>(e.what()));
return 1; return 1;
} }
} }

View File

@ -88,12 +88,9 @@ int waybar::modules::Backlight::BacklightDev::get_max() const { return max_; }
void waybar::modules::Backlight::BacklightDev::set_max(int max) { max_ = max; } void waybar::modules::Backlight::BacklightDev::set_max(int max) { max_ = max; }
waybar::modules::Backlight::Backlight(const std::string &name, const Json::Value &config) waybar::modules::Backlight::Backlight(const std::string &id, const Json::Value &config)
: ALabel(config, "{percent}%", 2), : ALabel(config, "backlight", id, "{percent}%", 2),
name_(name),
preferred_device_(config["device"].isString() ? config["device"].asString() : "") { preferred_device_(config["device"].isString() ? config["device"].asString() : "") {
label_.set_name("backlight");
// Get initial state // Get initial state
{ {
std::unique_ptr<udev, UdevDeleter> udev_check{udev_new()}; std::unique_ptr<udev, UdevDeleter> udev_check{udev_new()};
@ -213,7 +210,10 @@ void waybar::modules::Backlight::upsert_device(ForwardIt first, ForwardIt last,
const char *name = udev_device_get_sysname(dev); const char *name = udev_device_get_sysname(dev);
check_nn(name); check_nn(name);
const char *actual = udev_device_get_sysattr_value(dev, "actual_brightness"); const char *actual_brightness_attr =
strcmp(name, "amdgpu_bl0") == 0 ? "brightness" : "actual_brightness";
const char *actual = udev_device_get_sysattr_value(dev, actual_brightness_attr);
check_nn(actual); check_nn(actual);
const int actual_int = std::stoi(actual); const int actual_int = std::stoi(actual);

View File

@ -1,11 +1,8 @@
#include "modules/battery.hpp" #include "modules/battery.hpp"
#include <spdlog/spdlog.h>
waybar::modules::Battery::Battery(const std::string& id, const Json::Value& config) waybar::modules::Battery::Battery(const std::string& id, const Json::Value& config)
: ALabel(config, "{capacity}%", 60) { : ALabel(config, "battery", id, "{capacity}%", 60) {
label_.set_name("battery");
if (!id.empty()) {
label_.get_style_context()->add_class(id);
}
getBatteries(); getBatteries();
fd_ = inotify_init1(IN_CLOEXEC); fd_ = inotify_init1(IN_CLOEXEC);
if (fd_ == -1) { if (fd_ == -1) {
@ -75,23 +72,34 @@ void waybar::modules::Battery::getBatteries() {
} }
} }
const std::tuple<uint8_t, uint32_t, std::string> waybar::modules::Battery::getInfos() const { const std::tuple<uint8_t, float, std::string> waybar::modules::Battery::getInfos() const {
try { try {
uint16_t total = 0; uint16_t total = 0;
uint32_t total_current = 0; uint32_t total_power = 0; // μW
uint32_t total_energy = 0; // μWh
uint32_t total_energy_full = 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; uint32_t power_now;
uint32_t energy_full;
uint32_t energy_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; auto rate_path = fs::exists(bat / "current_now") ? "current_now" : "power_now";
std::ifstream(bat / rate_path) >> power_now;
auto now_path = fs::exists(bat / "charge_now") ? "charge_now" : "energy_now";
std::ifstream(bat / now_path) >> energy_now;
auto full_path = fs::exists(bat / "charge_full") ? "charge_full" : "energy_full";
std::ifstream(bat / full_path) >> energy_full;
if (_status != "Unknown") { if (_status != "Unknown") {
status = _status; status = _status;
} }
total += capacity; total += capacity;
total_current += current_now; total_power += power_now;
total_energy += energy_now;
total_energy_full += energy_full;
} }
if (!adapter_.empty() && status == "Discharging") { if (!adapter_.empty() && status == "Discharging") {
bool online; bool online;
@ -100,16 +108,21 @@ const std::tuple<uint8_t, uint32_t, std::string> waybar::modules::Battery::getIn
status = "Plugged"; status = "Plugged";
} }
} }
float time_remaining = 0;
if (status == "Discharging" && total_power != 0) {
time_remaining = (float)total_energy / total_power;
} else if (status == "Charging" && total_power != 0) {
time_remaining = -(float)(total_energy_full - total_energy) / total_power;
}
uint16_t capacity = total / batteries_.size(); uint16_t capacity = total / batteries_.size();
return {capacity, total_current, status}; return {capacity, time_remaining, status};
} catch (const std::exception& e) { } catch (const std::exception& e) {
std::cerr << e.what() << std::endl; spdlog::error("Battery: {}", e.what());
return {0, 0, "Unknown"}; return {0, 0, "Unknown"};
} }
} }
const std::string waybar::modules::Battery::getAdapterStatus(uint8_t capacity, const std::string waybar::modules::Battery::getAdapterStatus(uint8_t capacity) const {
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;
@ -124,13 +137,27 @@ const std::string waybar::modules::Battery::getAdapterStatus(uint8_t capacity,
return "Unknown"; return "Unknown";
} }
const std::string waybar::modules::Battery::formatTimeRemaining(float hoursRemaining) {
hoursRemaining = std::fabs(hoursRemaining);
uint16_t full_hours = static_cast<uint16_t>(hoursRemaining);
uint16_t minutes = static_cast<uint16_t>(60 * (hoursRemaining - full_hours));
return std::to_string(full_hours) + " h " + std::to_string(minutes) + " min";
}
auto waybar::modules::Battery::update() -> void { auto waybar::modules::Battery::update() -> void {
auto [capacity, current_now, status] = getInfos(); auto [capacity, time_remaining, status] = getInfos();
if (status == "Unknown") { if (status == "Unknown") {
status = getAdapterStatus(capacity, current_now); status = getAdapterStatus(capacity);
} }
if (tooltipEnabled()) { if (tooltipEnabled()) {
label_.set_tooltip_text(status); std::string tooltip_text;
if (time_remaining != 0) {
std::string time_to = std::string("Time to ") + ((time_remaining > 0) ? "empty" : "full");
tooltip_text = time_to + ": " + formatTimeRemaining(time_remaining);
} else {
tooltip_text = status;
}
label_.set_tooltip_text(tooltip_text);
} }
std::transform(status.begin(), status.end(), status.begin(), ::tolower); std::transform(status.begin(), status.end(), status.begin(), ::tolower);
auto format = format_; auto format = format_;
@ -151,7 +178,9 @@ auto waybar::modules::Battery::update() -> void {
event_box_.hide(); event_box_.hide();
} else { } else {
event_box_.show(); event_box_.show();
label_.set_markup(fmt::format( label_.set_markup(fmt::format(format,
format, fmt::arg("capacity", capacity), fmt::arg("icon", getIcon(capacity, state)))); fmt::arg("capacity", capacity),
fmt::arg("icon", getIcon(capacity, state)),
fmt::arg("time", formatTimeRemaining(time_remaining))));
} }
} }

View File

@ -1,29 +1,19 @@
#include "modules/clock.hpp" #include "modules/clock.hpp"
waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
: ALabel(config, "{:%H:%M}", 60) { : ALabel(config, "clock", id, "{:%H:%M}", 60) {
label_.set_name("clock");
if (!id.empty()) {
label_.get_style_context()->add_class(id);
}
thread_ = [this] { thread_ = [this] {
dp.emit(); dp.emit();
auto now = std::chrono::system_clock::now(); auto now = std::chrono::system_clock::now();
auto timeout = std::chrono::floor<std::chrono::seconds>(now + interval_); auto timeout = std::chrono::floor<std::chrono::seconds>(now + interval_);
auto time_s = std::chrono::time_point_cast<std::chrono::seconds>(timeout); auto diff = std::chrono::seconds(timeout.time_since_epoch().count() % interval_.count());
auto sub_m = thread_.sleep_until(timeout - diff);
std::chrono::duration_cast<std::chrono::seconds>(time_s.time_since_epoch()).count() %
interval_.count();
if (sub_m > 0) {
thread_.sleep_until(timeout - std::chrono::seconds(sub_m - 1));
} else {
thread_.sleep_until(timeout - std::chrono::seconds(sub_m));
}
}; };
} }
auto waybar::modules::Clock::update() -> void { auto waybar::modules::Clock::update() -> void {
auto localtime = fmt::localtime(std::time(nullptr)); auto now = std::chrono::system_clock::now();
auto localtime = fmt::localtime(std::chrono::system_clock::to_time_t(now));
auto text = fmt::format(format_, localtime); auto text = fmt::format(format_, localtime);
label_.set_markup(text); label_.set_markup(text);

View File

@ -1,11 +1,8 @@
#include "modules/cpu.hpp" #include "modules/cpu.hpp"
#include <numeric>
waybar::modules::Cpu::Cpu(const std::string& id, const Json::Value& config) waybar::modules::Cpu::Cpu(const std::string& id, const Json::Value& config)
: ALabel(config, "{usage}%", 10) { : ALabel(config, "cpu", id, "{usage}%", 10) {
label_.set_name("cpu");
if (!id.empty()) {
label_.get_style_context()->add_class(id);
}
thread_ = [this] { thread_ = [this] {
dp.emit(); dp.emit();
thread_.sleep_for(interval_); thread_.sleep_for(interval_);

View File

@ -1,8 +1,9 @@
#include "modules/custom.hpp" #include "modules/custom.hpp"
#include <spdlog/spdlog.h>
waybar::modules::Custom::Custom(const std::string& name, const Json::Value& config) waybar::modules::Custom::Custom(const std::string& name, const std::string& id,
: ALabel(config, "{}"), name_(name), fp_(nullptr), pid_(-1) { const Json::Value& config)
label_.set_name("custom-" + name_); : ALabel(config, "custom-" + name, id, "{}"), name_(name), fp_(nullptr), pid_(-1) {
if (config_["exec"].isString()) { if (config_["exec"].isString()) {
if (interval_.count() > 0) { if (interval_.count() > 0) {
delayWorker(); delayWorker();
@ -24,14 +25,14 @@ void waybar::modules::Custom::delayWorker() {
thread_ = [this] { thread_ = [this] {
bool can_update = true; bool can_update = true;
if (config_["exec-if"].isString()) { if (config_["exec-if"].isString()) {
auto res = waybar::util::command::exec(config_["exec-if"].asString()); auto res = util::command::exec(config_["exec-if"].asString());
if (res.exit_code != 0) { if (res.exit_code != 0) {
can_update = false; can_update = false;
event_box_.hide(); event_box_.hide();
} }
} }
if (can_update) { if (can_update) {
output_ = waybar::util::command::exec(config_["exec"].asString()); output_ = util::command::exec(config_["exec"].asString());
dp.emit(); dp.emit();
} }
thread_.sleep_for(interval_); thread_.sleep_for(interval_);
@ -58,7 +59,7 @@ void waybar::modules::Custom::continuousWorker() {
if (exit_code != 0) { if (exit_code != 0) {
output_ = {exit_code, ""}; output_ = {exit_code, ""};
dp.emit(); dp.emit();
std::cerr << name_ + " just stopped unexpectedly, is it endless?" << std::endl; spdlog::error("{} stopped unexpectedly, is it endless?", name_);
} }
return; return;
} }
@ -73,7 +74,7 @@ void waybar::modules::Custom::continuousWorker() {
}; };
} }
void waybar::modules::Custom::refresh(int sig /*signal*/) { void waybar::modules::Custom::refresh(int sig) {
if (sig == SIGRTMIN + config_["signal"].asInt()) { if (sig == SIGRTMIN + config_["signal"].asInt()) {
thread_.wake_up(); thread_.wake_up();
} }
@ -101,12 +102,14 @@ auto waybar::modules::Custom::update() -> void {
} else { } else {
parseOutputRaw(); parseOutputRaw();
} }
auto str = fmt::format(format_, auto str = fmt::format(format_,
text_, text_,
fmt::arg("alt", alt_), fmt::arg("alt", alt_),
fmt::arg("icon", getIcon(percentage_, alt_)), fmt::arg("icon", getIcon(percentage_, alt_)),
fmt::arg("percentage", percentage_)); fmt::arg("percentage", percentage_));
if (str.empty()) {
event_box_.hide();
} else {
label_.set_markup(str); label_.set_markup(str);
if (tooltipEnabled()) { if (tooltipEnabled()) {
if (text_ == tooltip_) { if (text_ == tooltip_) {
@ -122,9 +125,9 @@ auto waybar::modules::Custom::update() -> void {
for (auto const& c : class_) { for (auto const& c : class_) {
label_.get_style_context()->add_class(c); label_.get_style_context()->add_class(c);
} }
event_box_.show(); event_box_.show();
} }
}
} }
void waybar::modules::Custom::parseOutputRaw() { void waybar::modules::Custom::parseOutputRaw() {

View File

@ -3,15 +3,11 @@
waybar::modules::IdleInhibitor::IdleInhibitor(const std::string& id, const Bar& bar, waybar::modules::IdleInhibitor::IdleInhibitor(const std::string& id, const Bar& bar,
const Json::Value& config) const Json::Value& config)
: ALabel(config, "{status}"), : ALabel(config, "idle_inhibitor", id, "{status}"),
bar_(bar), bar_(bar),
status_("deactivated"), status_("deactivated"),
idle_inhibitor_(nullptr), idle_inhibitor_(nullptr),
pid_(-1) { pid_(-1) {
label_.set_name("idle_inhibitor");
if (!id.empty()) {
label_.get_style_context()->add_class(id);
}
event_box_.add_events(Gdk::BUTTON_PRESS_MASK); event_box_.add_events(Gdk::BUTTON_PRESS_MASK);
event_box_.signal_button_press_event().connect( event_box_.signal_button_press_event().connect(
sigc::mem_fun(*this, &IdleInhibitor::handleToggle)); sigc::mem_fun(*this, &IdleInhibitor::handleToggle));

View File

@ -1,11 +1,7 @@
#include "modules/memory.hpp" #include "modules/memory.hpp"
waybar::modules::Memory::Memory(const std::string& id, const Json::Value& config) waybar::modules::Memory::Memory(const std::string& id, const Json::Value& config)
: ALabel(config, "{}%", 30) { : ALabel(config, "memory", id, "{}%", 30) {
label_.set_name("memory");
if (!id.empty()) {
label_.get_style_context()->add_class(id);
}
thread_ = [this] { thread_ = [this] {
dp.emit(); dp.emit();
thread_.sleep_for(interval_); thread_.sleep_for(interval_);
@ -15,10 +11,18 @@ waybar::modules::Memory::Memory(const std::string& id, const Json::Value& config
auto waybar::modules::Memory::update() -> void { auto waybar::modules::Memory::update() -> void {
parseMeminfo(); parseMeminfo();
if (memtotal_ > 0 && memfree_ >= 0) { if (memtotal_ > 0 && memfree_ >= 0) {
auto total_ram_gigabytes = memtotal_ / std::pow(1024, 2);
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));
auto used_ram_gigabytes = (memtotal_ - memfree_) / std::pow(1024, 2); auto used_ram_gigabytes = (memtotal_ - memfree_) / std::pow(1024, 2);
auto available_ram_gigabytes = memfree_ / std::pow(1024, 2);
getState(used_ram_percentage);
label_.set_markup(fmt::format(format_,
used_ram_percentage,
fmt::arg("total", total_ram_gigabytes),
fmt::arg("percentage", used_ram_percentage),
fmt::arg("used", used_ram_gigabytes),
fmt::arg("avail", available_ram_gigabytes)));
if (tooltipEnabled()) { if (tooltipEnabled()) {
label_.set_tooltip_text(fmt::format("{:.{}f}Gb used", used_ram_gigabytes, 1)); label_.set_tooltip_text(fmt::format("{:.{}f}Gb used", used_ram_gigabytes, 1));
} }

View File

@ -1,10 +1,10 @@
#include "modules/mpd.hpp" #include "modules/mpd.hpp"
#include <fmt/chrono.h> #include <fmt/chrono.h>
#include <iostream> #include <spdlog/spdlog.h>
waybar::modules::MPD::MPD(const std::string& id, const Json::Value& config) waybar::modules::MPD::MPD(const std::string& id, const Json::Value& config)
: ALabel(config, "{album} - {artist} - {title}", 5), : ALabel(config, "mpd", id, "{album} - {artist} - {title}", 5),
module_name_(id.empty() ? "mpd" : "mpd#" + id), module_name_(id.empty() ? "mpd" : "mpd#" + id),
server_(nullptr), server_(nullptr),
port_(config_["port"].isUInt() ? config["port"].asUInt() : 0), port_(config_["port"].isUInt() ? config["port"].asUInt() : 0),
@ -14,21 +14,16 @@ waybar::modules::MPD::MPD(const std::string& id, const Json::Value& config)
status_(nullptr, &mpd_status_free), status_(nullptr, &mpd_status_free),
song_(nullptr, &mpd_song_free) { song_(nullptr, &mpd_song_free) {
if (!config_["port"].isNull() && !config_["port"].isUInt()) { if (!config_["port"].isNull() && !config_["port"].isUInt()) {
std::cerr << module_name_ << ": `port` configuration should be an unsigned int" << std::endl; spdlog::warn("{}: `port` configuration should be an unsigned int", module_name_);
} }
if (!config_["timeout"].isNull() && !config_["timeout"].isUInt()) { if (!config_["timeout"].isNull() && !config_["timeout"].isUInt()) {
std::cerr << module_name_ << ": `timeout` configuration should be an unsigned int" << std::endl; spdlog::warn("{}: `timeout` configuration should be an unsigned int", module_name_);
}
label_.set_name("mpd");
if (!id.empty()) {
label_.get_style_context()->add_class(id);
} }
if (!config["server"].isNull()) { if (!config["server"].isNull()) {
if (!config_["server"].isString()) { if (!config_["server"].isString()) {
std::cerr << module_name_ << "`server` configuration should be a string" << std::endl; spdlog::warn("{}:`server` configuration should be a string", module_name_);
} }
server_ = config["server"].asCString(); server_ = config["server"].asCString();
} }
@ -51,7 +46,7 @@ auto waybar::modules::MPD::update() -> void {
periodic_updater().detach(); periodic_updater().detach();
} }
} catch (const std::exception& e) { } catch (const std::exception& e) {
std::cerr << module_name_ + ": " + e.what() << std::endl; spdlog::error("{}: {}", module_name_, e.what());
state_ = MPD_STATE_UNKNOWN; state_ = MPD_STATE_UNKNOWN;
} }
} }
@ -72,7 +67,7 @@ std::thread waybar::modules::MPD::event_listener() {
dp.emit(); dp.emit();
} }
} catch (const std::exception& e) { } catch (const std::exception& e) {
std::cerr << module_name_ + ": " + e.what() << std::endl; spdlog::warn("{}: {}", module_name_, e.what());
} }
} }
}); });
@ -206,12 +201,12 @@ std::string waybar::modules::MPD::getStateIcon() {
} }
if (connection_ == nullptr) { if (connection_ == nullptr) {
std::cerr << module_name_ << ": Trying to fetch state icon while disconnected" << std::endl; spdlog::warn("{}: Trying to fetch state icon while disconnected", module_name_);
return ""; return "";
} }
if (stopped()) { if (stopped()) {
std::cerr << module_name_ << ": Trying to fetch state icon while stopped" << std::endl; spdlog::warn("{}: Trying to fetch state icon while stopped", module_name_);
return ""; return "";
} }
@ -228,7 +223,7 @@ std::string waybar::modules::MPD::getOptionIcon(std::string optionName, bool act
} }
if (connection_ == nullptr) { if (connection_ == nullptr) {
std::cerr << module_name_ << ": Trying to fetch option icon while disconnected" << std::endl; spdlog::warn("{}: Trying to fetch option icon while disconnected", module_name_);
return ""; return "";
} }
@ -251,7 +246,7 @@ void waybar::modules::MPD::tryConnect() {
unique_connection(mpd_connection_new(server_, port_, timeout_), &mpd_connection_free); unique_connection(mpd_connection_new(server_, port_, timeout_), &mpd_connection_free);
if (connection_ == nullptr || alternate_connection_ == nullptr) { if (connection_ == nullptr || alternate_connection_ == nullptr) {
std::cerr << module_name_ << ": Failed to connect to MPD" << std::endl; spdlog::error("{}: Failed to connect to MPD", module_name_);
connection_.reset(); connection_.reset();
alternate_connection_.reset(); alternate_connection_.reset();
return; return;
@ -259,9 +254,9 @@ void waybar::modules::MPD::tryConnect() {
try { try {
checkErrors(connection_.get()); checkErrors(connection_.get());
std::cerr << module_name_ << ": Connected to MPD" << std::endl; spdlog::info("{}: Connected to MPD", module_name_);
} catch (std::runtime_error& e) { } catch (std::runtime_error& e) {
std::cerr << module_name_ << ": Failed to connect to MPD: " << e.what() << std::endl; spdlog::error("{}: Failed to connect to MPD: {}", module_name_, e.what());
connection_.reset(); connection_.reset();
alternate_connection_.reset(); alternate_connection_.reset();
} }

View File

@ -1,34 +1,35 @@
#include "modules/network.hpp" #include "modules/network.hpp"
#include <spdlog/spdlog.h>
#include <sys/eventfd.h> #include <sys/eventfd.h>
#include <fstream> #include <fstream>
#include <iostream>
namespace { namespace {
constexpr const char * NETSTAT_FILE = "/proc/net/netstat"; // std::ifstream does not take std::string_view as param constexpr const char *NETSTAT_FILE =
constexpr std::string_view BANDWIDTH_CATEGORY = "IpExt"; "/proc/net/netstat"; // std::ifstream does not take std::string_view as param
constexpr std::string_view BANDWIDTH_DOWN_TOTAL_KEY = "InOctets"; constexpr std::string_view BANDWIDTH_CATEGORY = "IpExt";
constexpr std::string_view BANDWIDTH_UP_TOTAL_KEY = "OutOctets"; constexpr std::string_view BANDWIDTH_DOWN_TOTAL_KEY = "InOctets";
constexpr std::string_view BANDWIDTH_UP_TOTAL_KEY = "OutOctets";
std::ifstream netstat(NETSTAT_FILE); std::ifstream netstat(NETSTAT_FILE);
std::optional<unsigned long long> read_netstat(std::string_view category, std::string_view key) { std::optional<unsigned long long> read_netstat(std::string_view category, std::string_view key) {
if (!netstat) { if (!netstat) {
std::cerr << "Failed to open netstat file " << NETSTAT_FILE << '\n' << std::flush; spdlog::warn("Failed to open netstat file {}", NETSTAT_FILE);
return {}; return {};
} }
netstat.seekg(std::ios_base::beg); netstat.seekg(std::ios_base::beg);
// finding corresponding line (category) // finding corresponding line (category)
// looks into the file for the first line starting by the 'category' string // looks into the file for the first line starting by the 'category' string
auto starts_with = [](const std::string& str, std::string_view start) { auto starts_with = [](const std::string &str, std::string_view start) {
return start == std::string_view{str.data(), std::min(str.size(), start.size())}; return start == std::string_view{str.data(), std::min(str.size(), start.size())};
}; };
std::string read; std::string read;
while (std::getline(netstat, read) && !starts_with(read, category)); while (std::getline(netstat, read) && !starts_with(read, category))
;
if (!starts_with(read, category)) { if (!starts_with(read, category)) {
std::cerr << "Category '" << category << "' not found in netstat file " << NETSTAT_FILE << '\n' << std::flush; spdlog::warn("Category '{}' not found in netstat file {}", category, NETSTAT_FILE);
return {}; return {};
} }
@ -52,7 +53,8 @@ namespace {
} }
if (r_it == read.end() && k_it != key.end()) { 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; spdlog::warn(
"Key '{}' not found in category '{}' of netstat file {}", key, category, NETSTAT_FILE);
return {}; return {};
} }
@ -67,13 +69,12 @@ namespace {
unsigned long long value; unsigned long long value;
iss >> value; iss >> value;
return value; return value;
}
} }
} // namespace
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, "network", id, "{ifname}", 60),
ifid_(-1), ifid_(-1),
last_ext_iface_(-1),
family_(config["family"] == "ipv6" ? AF_INET6 : AF_INET), family_(config["family"] == "ipv6" ? AF_INET6 : AF_INET),
efd_(-1), efd_(-1),
ev_fd_(-1), ev_fd_(-1),
@ -81,11 +82,6 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf
signal_strength_dbm_(0), signal_strength_dbm_(0),
signal_strength_(0), signal_strength_(0),
frequency_(0) { frequency_(0) {
label_.set_name("network");
if (!id.empty()) {
label_.get_style_context()->add_class(id);
}
auto down_octets = read_netstat(BANDWIDTH_CATEGORY, BANDWIDTH_DOWN_TOTAL_KEY); auto down_octets = read_netstat(BANDWIDTH_CATEGORY, BANDWIDTH_DOWN_TOTAL_KEY);
auto up_octets = read_netstat(BANDWIDTH_CATEGORY, BANDWIDTH_UP_TOTAL_KEY); auto up_octets = read_netstat(BANDWIDTH_CATEGORY, BANDWIDTH_UP_TOTAL_KEY);
if (down_octets) { if (down_octets) {
@ -100,10 +96,11 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf
bandwidth_up_total_ = 0; bandwidth_up_total_ = 0;
} }
createInfoSocket();
createEventSocket(); createEventSocket();
createInfoSocket();
auto default_iface = getPreferredIface(); auto default_iface = getPreferredIface();
if (default_iface != -1) { if (default_iface != -1) {
ifid_ = default_iface;
char ifname[IF_NAMESIZE]; char ifname[IF_NAMESIZE];
if_indextoname(default_iface, ifname); if_indextoname(default_iface, ifname);
ifname_ = ifname; ifname_ = ifname;
@ -124,8 +121,11 @@ waybar::modules::Network::~Network() {
} }
if (ev_sock_ != nullptr) { if (ev_sock_ != nullptr) {
nl_socket_drop_membership(ev_sock_, RTNLGRP_LINK); nl_socket_drop_membership(ev_sock_, RTNLGRP_LINK);
if (family_ == AF_INET) {
nl_socket_drop_membership(ev_sock_, RTNLGRP_IPV4_IFADDR); nl_socket_drop_membership(ev_sock_, RTNLGRP_IPV4_IFADDR);
} else {
nl_socket_drop_membership(ev_sock_, RTNLGRP_IPV6_IFADDR); nl_socket_drop_membership(ev_sock_, RTNLGRP_IPV6_IFADDR);
}
nl_close(ev_sock_); nl_close(ev_sock_);
nl_socket_free(ev_sock_); nl_socket_free(ev_sock_);
} }
@ -135,17 +135,21 @@ waybar::modules::Network::~Network() {
} }
} }
void waybar::modules::Network::createInfoSocket() { void waybar::modules::Network::createEventSocket() {
ev_sock_ = nl_socket_alloc(); ev_sock_ = nl_socket_alloc();
nl_socket_disable_seq_check(ev_sock_); nl_socket_disable_seq_check(ev_sock_);
nl_socket_modify_cb(ev_sock_, NL_CB_VALID, NL_CB_CUSTOM, handleEvents, this); nl_socket_modify_cb(ev_sock_, NL_CB_VALID, NL_CB_CUSTOM, handleEvents, this);
nl_join_groups(ev_sock_, RTMGRP_LINK); auto groups = RTMGRP_LINK | (family_ == AF_INET ? RTMGRP_IPV4_IFADDR : RTMGRP_IPV6_IFADDR);
nl_join_groups(ev_sock_, groups); // Deprecated
if (nl_connect(ev_sock_, NETLINK_ROUTE) != 0) { 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");
} }
nl_socket_add_membership(ev_sock_, RTNLGRP_LINK); nl_socket_add_membership(ev_sock_, RTNLGRP_LINK);
if (family_ == AF_INET) {
nl_socket_add_membership(ev_sock_, RTNLGRP_IPV4_IFADDR); nl_socket_add_membership(ev_sock_, RTNLGRP_IPV4_IFADDR);
} else {
nl_socket_add_membership(ev_sock_, RTNLGRP_IPV6_IFADDR); nl_socket_add_membership(ev_sock_, RTNLGRP_IPV6_IFADDR);
}
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");
@ -172,7 +176,7 @@ void waybar::modules::Network::createInfoSocket() {
} }
} }
void waybar::modules::Network::createEventSocket() { void waybar::modules::Network::createInfoSocket() {
sock_ = nl_socket_alloc(); sock_ = nl_socket_alloc();
if (genl_connect(sock_) != 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");
@ -202,9 +206,7 @@ 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(ev_sock_)) { if (events[i].data.fd != nl_socket_get_fd(ev_sock_) || nl_recvmsgs_default(ev_sock_) < 0) {
nl_recvmsgs_default(ev_sock_);
} else {
thread_.stop(); thread_.stop();
break; break;
} }
@ -213,10 +215,16 @@ void waybar::modules::Network::worker() {
}; };
} }
const std::string waybar::modules::Network::getNetworkState() const {
if (ifid_ == -1) return "disconnected";
if (ipaddr_.empty()) return "linked";
if (essid_.empty()) return "ethernet";
return "wifi";
}
auto waybar::modules::Network::update() -> void { auto waybar::modules::Network::update() -> void {
std::string connectiontype;
std::string tooltip_format;
std::lock_guard<std::mutex> lock(mutex_); std::lock_guard<std::mutex> lock(mutex_);
std::string tooltip_format;
auto down_octets = read_netstat(BANDWIDTH_CATEGORY, BANDWIDTH_DOWN_TOTAL_KEY); auto down_octets = read_netstat(BANDWIDTH_CATEGORY, BANDWIDTH_DOWN_TOTAL_KEY);
auto up_octets = read_netstat(BANDWIDTH_CATEGORY, BANDWIDTH_UP_TOTAL_KEY); auto up_octets = read_netstat(BANDWIDTH_CATEGORY, BANDWIDTH_UP_TOTAL_KEY);
@ -231,56 +239,35 @@ auto waybar::modules::Network::update() -> void {
bandwidth_up = *up_octets - bandwidth_up_total_; bandwidth_up = *up_octets - bandwidth_up_total_;
bandwidth_up_total_ = *up_octets; bandwidth_up_total_ = *up_octets;
} }
if (ifid_ <= 0 || !linked_) {
if (config_["format-disconnected"].isString()) {
default_format_ = config_["format-disconnected"].asString();
}
if (config_["tooltip-format-disconnected"].isString()) {
tooltip_format = config_["tooltip-format-disconnected"].asString();
}
label_.get_style_context()->add_class("disconnected");
connectiontype = "disconnected";
} else {
if (essid_.empty()) {
if (config_["format-ethernet"].isString()) {
default_format_ = config_["format-ethernet"].asString();
}
if (config_["tooltip-format-ethernet"].isString()) {
tooltip_format = config_["tooltip-format-ethernet"].asString();
}
connectiontype = "ethernet";
} else if (ipaddr_.empty()) {
if (config_["format-linked"].isString()) {
default_format_ = config_["format-linked"].asString();
}
if (config_["tooltip-format-linked"].isString()) {
tooltip_format = config_["tooltip-format-linked"].asString();
}
connectiontype = "linked";
} else {
if (config_["format-wifi"].isString()) {
default_format_ = config_["format-wifi"].asString();
}
if (config_["tooltip-format-wifi"].isString()) {
tooltip_format = config_["tooltip-format-wifi"].asString();
}
connectiontype = "wifi";
}
label_.get_style_context()->remove_class("disconnected");
}
if (!alt_) { if (!alt_) {
auto state = getNetworkState();
if (!state_.empty() && label_.get_style_context()->has_class(state_)) {
label_.get_style_context()->remove_class(state_);
}
if (config_["format-" + state].isString()) {
default_format_ = config_["format-" + state].asString();
}
if (config_["tooltip-format-" + state].isString()) {
tooltip_format = config_["tooltip-format-" + state].asString();
}
if (!label_.get_style_context()->has_class(state)) {
label_.get_style_context()->add_class(state);
}
format_ = default_format_; format_ = default_format_;
state_ = state;
} }
getState(signal_strength_); getState(signal_strength_);
auto pow_format = [](unsigned long long value, const std::string& unit) { auto pow_format = [](unsigned long long value, const std::string &unit) {
if (value > 2000ull * 1000ull * 1000ull) { // > 2G if (value > 2000ull * 1000ull * 1000ull) { // > 2G
auto go = value / (1000 * 1000 * 1000); auto go = value / (1000 * 1000 * 1000);
return std::to_string(go) + "." + std::to_string((value - go * 1000 * 1000 * 1000) / (100 * 1000 * 1000)) + "G" + unit; return std::to_string(go) + "." +
std::to_string((value - go * 1000 * 1000 * 1000) / (100 * 1000 * 1000)) + "G" + unit;
} else if (value > 2000ull * 1000ull) { // > 2M } else if (value > 2000ull * 1000ull) { // > 2M
auto mo = value / (1000 * 1000); auto mo = value / (1000 * 1000);
return std::to_string(mo) + "." + std::to_string((value - mo * 1000 * 1000) / (100 * 1000)) + "M" + unit; return std::to_string(mo) + "." + std::to_string((value - mo * 1000 * 1000) / (100 * 1000)) +
"M" + unit;
} else if (value > 2000ull) { // > 2k } else if (value > 2000ull) { // > 2k
auto ko = value / 1000; auto ko = value / 1000;
@ -291,7 +278,8 @@ auto waybar::modules::Network::update() -> void {
} }
}; };
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_),
fmt::arg("signalStrength", signal_strength_), fmt::arg("signalStrength", signal_strength_),
@ -300,7 +288,7 @@ auto waybar::modules::Network::update() -> void {
fmt::arg("ipaddr", ipaddr_), fmt::arg("ipaddr", ipaddr_),
fmt::arg("cidr", cidr_), fmt::arg("cidr", cidr_),
fmt::arg("frequency", frequency_), fmt::arg("frequency", frequency_),
fmt::arg("icon", getIcon(signal_strength_, connectiontype)), fmt::arg("icon", getIcon(signal_strength_, state_)),
fmt::arg("bandwidthDownBits", pow_format(bandwidth_down * 8ull / interval_.count(), "b/s")), 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("bandwidthUpBits", pow_format(bandwidth_up * 8ull / interval_.count(), "b/s")),
fmt::arg("bandwidthDownOctets", pow_format(bandwidth_down / interval_.count(), "o/s")), fmt::arg("bandwidthDownOctets", pow_format(bandwidth_down / interval_.count(), "o/s")),
@ -313,7 +301,8 @@ auto waybar::modules::Network::update() -> void {
tooltip_format = config_["tooltip-format"].asString(); 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_),
fmt::arg("signaldBm", signal_strength_dbm_), fmt::arg("signaldBm", signal_strength_dbm_),
fmt::arg("signalStrength", signal_strength_), fmt::arg("signalStrength", signal_strength_),
@ -322,8 +311,9 @@ auto waybar::modules::Network::update() -> void {
fmt::arg("ipaddr", ipaddr_), fmt::arg("ipaddr", ipaddr_),
fmt::arg("cidr", cidr_), fmt::arg("cidr", cidr_),
fmt::arg("frequency", frequency_), fmt::arg("frequency", frequency_),
fmt::arg("icon", getIcon(signal_strength_, connectiontype)), fmt::arg("icon", getIcon(signal_strength_, state_)),
fmt::arg("bandwidthDownBits", pow_format(bandwidth_down * 8ull / interval_.count(), "b/s")), 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("bandwidthUpBits", pow_format(bandwidth_up * 8ull / interval_.count(), "b/s")),
fmt::arg("bandwidthDownOctets", pow_format(bandwidth_down / interval_.count(), "o/s")), fmt::arg("bandwidthDownOctets", pow_format(bandwidth_down / interval_.count(), "o/s")),
fmt::arg("bandwidthUpOctets", pow_format(bandwidth_up / interval_.count(), "o/s"))); fmt::arg("bandwidthUpOctets", pow_format(bandwidth_up / interval_.count(), "o/s")));
@ -337,7 +327,7 @@ auto waybar::modules::Network::update() -> void {
} }
// 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(int skip_idx) const {
static const uint32_t route_buffer_size = 8192; static const uint32_t route_buffer_size = 8192;
struct nlmsghdr * hdr = nullptr; struct nlmsghdr * hdr = nullptr;
struct rtmsg * rt = nullptr; struct rtmsg * rt = nullptr;
@ -447,7 +437,7 @@ int waybar::modules::Network::getExternalInterface() {
/* If this is the default route, and we know the interface index, /* If this is the default route, and we know the interface index,
* we can stop parsing this message. * we can stop parsing this message.
*/ */
if (has_gateway && !has_destination && temp_idx != -1) { if (has_gateway && !has_destination && temp_idx != -1 && temp_idx != skip_idx) {
ifidx = temp_idx; ifidx = temp_idx;
break; break;
} }
@ -455,22 +445,19 @@ int waybar::modules::Network::getExternalInterface() {
} while (true); } while (true);
out: out:
last_ext_iface_ = ifidx;
return ifidx; return ifidx;
} }
void waybar::modules::Network::getInterfaceAddress() { void waybar::modules::Network::getInterfaceAddress() {
unsigned int cidrRaw; unsigned int cidrRaw;
struct ifaddrs *ifaddr, *ifa; struct ifaddrs *ifaddr, *ifa;
ipaddr_.clear();
netmask_.clear();
cidr_ = 0; cidr_ = 0;
int success = getifaddrs(&ifaddr); int success = getifaddrs(&ifaddr);
if (success != 0) { if (success != 0) {
return; return;
} }
ifa = ifaddr; ifa = ifaddr;
while (ifa != nullptr && ipaddr_.empty() && netmask_.empty()) { while (ifa != nullptr) {
if (ifa->ifa_addr != nullptr && ifa->ifa_addr->sa_family == family_ && if (ifa->ifa_addr != nullptr && ifa->ifa_addr->sa_family == family_ &&
ifa->ifa_name == ifname_) { ifa->ifa_name == ifname_) {
char ipaddr[INET6_ADDRSTRLEN]; char ipaddr[INET6_ADDRSTRLEN];
@ -482,20 +469,20 @@ void waybar::modules::Network::getInterfaceAddress() {
auto net_addr = reinterpret_cast<struct sockaddr_in *>(ifa->ifa_netmask); auto net_addr = reinterpret_cast<struct sockaddr_in *>(ifa->ifa_netmask);
netmask_ = inet_ntop(family_, &net_addr->sin_addr, netmask, INET6_ADDRSTRLEN); netmask_ = inet_ntop(family_, &net_addr->sin_addr, netmask, INET6_ADDRSTRLEN);
cidrRaw = net_addr->sin_addr.s_addr; cidrRaw = net_addr->sin_addr.s_addr;
linked_ = ifa->ifa_flags & IFF_RUNNING;
unsigned int cidr = 0; unsigned int cidr = 0;
while (cidrRaw) { while (cidrRaw) {
cidr += cidrRaw & 1; cidr += cidrRaw & 1;
cidrRaw >>= 1; cidrRaw >>= 1;
} }
cidr_ = cidr; cidr_ = cidr;
break;
} }
ifa = ifa->ifa_next; ifa = ifa->ifa_next;
} }
freeifaddrs(ifaddr); 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) const {
struct sockaddr_nl sa = {}; struct sockaddr_nl sa = {};
sa.nl_family = AF_NETLINK; sa.nl_family = AF_NETLINK;
sa.nl_groups = groups; sa.nl_groups = groups;
@ -509,7 +496,7 @@ int waybar::modules::Network::netlinkRequest(void *req, uint32_t reqlen, uint32_
return sendmsg(nl_socket_get_fd(ev_sock_), &msg, 0); 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) const {
struct sockaddr_nl sa = {}; struct sockaddr_nl sa = {};
sa.nl_family = AF_NETLINK; sa.nl_family = AF_NETLINK;
sa.nl_groups = groups; sa.nl_groups = groups;
@ -532,20 +519,23 @@ bool waybar::modules::Network::checkInterface(struct ifinfomsg *rtif, std::strin
return config_["interface"].asString() == name || return config_["interface"].asString() == name ||
wildcardMatch(config_["interface"].asString(), name); wildcardMatch(config_["interface"].asString(), name);
} }
// getExternalInterface may need some delay to detect external interface
for (uint8_t tries = 0; tries < MAX_RETRY; tries += 1) {
auto external_iface = getExternalInterface(); auto external_iface = getExternalInterface();
if (external_iface == -1) { if (external_iface > 0) {
// Try with lastest working external iface
return last_ext_iface_ == rtif->ifi_index;
}
return external_iface == rtif->ifi_index; return external_iface == rtif->ifi_index;
}
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
return false;
} }
int waybar::modules::Network::getPreferredIface() { int waybar::modules::Network::getPreferredIface(int skip_idx) const {
int ifid = -1;
if (config_["interface"].isString()) { if (config_["interface"].isString()) {
ifid_ = if_nametoindex(config_["interface"].asCString()); ifid = if_nametoindex(config_["interface"].asCString());
if (ifid_ > 0) { if (ifid > 0) {
ifname_ = config_["interface"].asString(); return ifid;
return ifid_;
} else { } else {
// Try with wildcard // Try with wildcard
struct ifaddrs *ifaddr, *ifa; struct ifaddrs *ifaddr, *ifa;
@ -554,84 +544,112 @@ int waybar::modules::Network::getPreferredIface() {
return -1; return -1;
} }
ifa = ifaddr; ifa = ifaddr;
ifid_ = -1; ifid = -1;
while (ifa != nullptr) { while (ifa != nullptr) {
if (wildcardMatch(config_["interface"].asString(), ifa->ifa_name)) { if (ifa->ifa_addr->sa_family == family_ &&
ifid_ = if_nametoindex(ifa->ifa_name); wildcardMatch(config_["interface"].asString(), ifa->ifa_name)) {
ifid = if_nametoindex(ifa->ifa_name);
break; break;
} }
ifa = ifa->ifa_next; ifa = ifa->ifa_next;
} }
freeifaddrs(ifaddr); freeifaddrs(ifaddr);
return ifid_; return ifid;
} }
} }
ifid_ = getExternalInterface(); // getExternalInterface may need some delay to detect external interface
if (ifid_ > 0) { for (uint8_t tries = 0; tries < MAX_RETRY; tries += 1) {
char ifname[IF_NAMESIZE]; ifid = getExternalInterface(skip_idx);
if_indextoname(ifid_, ifname); if (ifid > 0) {
ifname_ = ifname; return ifid;
return ifid_; }
std::this_thread::sleep_for(std::chrono::milliseconds(500));
} }
return -1; return -1;
} }
void waybar::modules::Network::clearIface() {
essid_.clear();
ipaddr_.clear();
netmask_.clear();
cidr_ = 0;
signal_strength_dbm_ = 0;
signal_strength_ = 0;
frequency_ = 0;
}
void waybar::modules::Network::checkNewInterface(struct ifinfomsg *rtif) {
auto new_iface = getPreferredIface(rtif->ifi_index);
if (new_iface != -1) {
ifid_ = new_iface;
char ifname[IF_NAMESIZE];
if_indextoname(new_iface, ifname);
ifname_ = ifname;
getInterfaceAddress();
thread_timer_.wake_up();
} else {
ifid_ = -1;
dp.emit();
}
}
int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) {
auto net = static_cast<waybar::modules::Network *>(data); auto net = static_cast<waybar::modules::Network *>(data);
auto nh = nlmsg_hdr(msg);
std::lock_guard<std::mutex> lock(net->mutex_); std::lock_guard<std::mutex> lock(net->mutex_);
auto nh = nlmsg_hdr(msg);
if (nh->nlmsg_type == RTM_NEWADDR) { auto ifi = static_cast<struct ifinfomsg *>(NLMSG_DATA(nh));
auto rtif = static_cast<struct ifinfomsg *>(NLMSG_DATA(nh)); if (nh->nlmsg_type == RTM_DELADDR) {
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->ifid_ = rtif->ifi_index;
}
// Check for valid interface // Check for valid interface
if (rtif->ifi_index == net->ifid_) { if (ifi->ifi_index == net->ifid_) {
// Get Iface and WIFI info
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->ipaddr_.clear();
net->netmask_.clear(); net->netmask_.clear();
net->cidr_ = 0; net->cidr_ = 0;
net->dp.emit(); if (!(ifi->ifi_flags & IFF_RUNNING)) {
} net->clearIface();
} 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 // Check for a new interface and get info
auto new_iface = net->getPreferredIface(); net->checkNewInterface(ifi);
if (new_iface != -1) {
net->getInterfaceAddress();
net->thread_timer_.wake_up();
} else { } else {
net->dp.emit(); net->dp.emit();
} }
return NL_OK;
}
} else if (nh->nlmsg_type == RTM_NEWLINK || nh->nlmsg_type == RTM_DELLINK) {
char ifname[IF_NAMESIZE];
if_indextoname(ifi->ifi_index, ifname);
// Check for valid interface
if (ifi->ifi_index != net->ifid_ && net->checkInterface(ifi, ifname)) {
net->ifname_ = ifname;
net->ifid_ = ifi->ifi_index;
// Get Iface and WIFI info
net->getInterfaceAddress();
net->thread_timer_.wake_up();
return NL_OK;
} else if (ifi->ifi_index == net->ifid_ &&
(!(ifi->ifi_flags & IFF_RUNNING) || !(ifi->ifi_flags & IFF_UP) ||
!net->checkInterface(ifi, ifname))) {
net->clearIface();
// Check for a new interface and get info
net->checkNewInterface(ifi);
return NL_OK;
}
} else {
char ifname[IF_NAMESIZE];
if_indextoname(ifi->ifi_index, ifname);
// Auto detected network can also be assigned here
if (ifi->ifi_index != net->ifid_ && net->checkInterface(ifi, ifname)) {
// If iface is different, clear data
if (ifi->ifi_index != net->ifid_) {
net->clearIface();
}
net->ifname_ = ifname;
net->ifid_ = ifi->ifi_index;
}
// Check for valid interface
if (ifi->ifi_index == net->ifid_) {
// Get Iface and WIFI info
net->getInterfaceAddress();
net->thread_timer_.wake_up();
return NL_OK;
} }
} }
return NL_SKIP; return NL_SKIP;
@ -669,11 +687,10 @@ int waybar::modules::Network::handleScan(struct nl_msg *msg, void *data) {
net->parseEssid(bss); net->parseEssid(bss);
net->parseSignal(bss); net->parseSignal(bss);
net->parseFreq(bss); net->parseFreq(bss);
return NL_SKIP; return NL_OK;
} }
void waybar::modules::Network::parseEssid(struct nlattr **bss) { void waybar::modules::Network::parseEssid(struct nlattr **bss) {
essid_.clear();
if (bss[NL80211_BSS_INFORMATION_ELEMENTS] != nullptr) { if (bss[NL80211_BSS_INFORMATION_ELEMENTS] != nullptr) {
auto ies = static_cast<char *>(nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS])); auto ies = static_cast<char *>(nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]));
auto ies_len = nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]); auto ies_len = nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
@ -746,7 +763,8 @@ auto waybar::modules::Network::getInfo() -> void {
} }
// https://gist.github.com/rressi/92af77630faf055934c723ce93ae2495 // https://gist.github.com/rressi/92af77630faf055934c723ce93ae2495
bool waybar::modules::Network::wildcardMatch(const std::string &pattern, const std::string &text) { bool waybar::modules::Network::wildcardMatch(const std::string &pattern,
const std::string &text) const {
auto P = int(pattern.size()); auto P = int(pattern.size());
auto T = int(text.size()); auto T = int(text.size());

View File

@ -1,19 +1,16 @@
#include "modules/pulseaudio.hpp" #include "modules/pulseaudio.hpp"
#include <array>
waybar::modules::Pulseaudio::Pulseaudio(const std::string &id, const Json::Value &config) waybar::modules::Pulseaudio::Pulseaudio(const std::string &id, const Json::Value &config)
: ALabel(config, "{volume}%"), : ALabel(config, "pulseaudio", id, "{volume}%"),
mainloop_(nullptr), mainloop_(nullptr),
mainloop_api_(nullptr), mainloop_api_(nullptr),
context_(nullptr), context_(nullptr),
sink_idx_(0), sink_idx_(0),
volume_(0), volume_(0),
muted_(false), muted_(false),
scrolling_(false) { source_idx_(0),
label_.set_name("pulseaudio"); source_volume_(0),
if (!id.empty()) { source_muted_(false) {
label_.get_style_context()->add_class(id);
}
mainloop_ = pa_threaded_mainloop_new(); mainloop_ = pa_threaded_mainloop_new();
if (mainloop_ == nullptr) { if (mainloop_ == nullptr) {
throw std::runtime_error("pa_mainloop_new() failed."); throw std::runtime_error("pa_mainloop_new() failed.");
@ -34,13 +31,6 @@ waybar::modules::Pulseaudio::Pulseaudio(const std::string &id, const Json::Value
throw std::runtime_error("pa_mainloop_run() failed."); throw std::runtime_error("pa_mainloop_run() failed.");
} }
pa_threaded_mainloop_unlock(mainloop_); pa_threaded_mainloop_unlock(mainloop_);
// define the pulse scroll events only when no user provided
// events are configured
if (!config["on-scroll-up"].isString() && !config["on-scroll-down"].isString()) {
event_box_.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK);
event_box_.signal_scroll_event().connect(sigc::mem_fun(*this, &Pulseaudio::handleVolume));
}
} }
waybar::modules::Pulseaudio::~Pulseaudio() { waybar::modules::Pulseaudio::~Pulseaudio() {
@ -58,7 +48,12 @@ void waybar::modules::Pulseaudio::contextStateCb(pa_context *c, void *data) {
case PA_CONTEXT_READY: case PA_CONTEXT_READY:
pa_context_get_server_info(c, serverInfoCb, data); pa_context_get_server_info(c, serverInfoCb, data);
pa_context_set_subscribe_callback(c, subscribeCb, data); pa_context_set_subscribe_callback(c, subscribeCb, data);
pa_context_subscribe(c, PA_SUBSCRIPTION_MASK_SINK, nullptr, nullptr); pa_context_subscribe(
c,
static_cast<enum pa_subscription_mask>(static_cast<int>(PA_SUBSCRIPTION_MASK_SINK) |
static_cast<int>(PA_SUBSCRIPTION_MASK_SOURCE)),
nullptr,
nullptr);
break; break;
case PA_CONTEXT_FAILED: case PA_CONTEXT_FAILED:
pa->mainloop_api_->quit(pa->mainloop_api_, 1); pa->mainloop_api_->quit(pa->mainloop_api_, 1);
@ -71,50 +66,33 @@ void waybar::modules::Pulseaudio::contextStateCb(pa_context *c, void *data) {
} }
} }
bool waybar::modules::Pulseaudio::handleVolume(GdkEventScroll *e) { bool waybar::modules::Pulseaudio::handleScroll(GdkEventScroll *e) {
// Avoid concurrent scroll event // change the pulse volume only when no user provided
if (scrolling_) { // events are configured
return false; if (config_["on-scroll-up"].isString() || config_["on-scroll-down"].isString()) {
return AModule::handleScroll(e);
} }
bool direction_up = false; auto dir = AModule::getScrollDir(e);
double volume_tick = (double)PA_VOLUME_NORM / 100; if (dir == SCROLL_DIR::NONE) {
return true;
}
double volume_tick = static_cast<double>(PA_VOLUME_NORM) / 100;
pa_volume_t change = volume_tick; pa_volume_t change = volume_tick;
pa_cvolume pa_volume = pa_volume_; pa_cvolume pa_volume = pa_volume_;
scrolling_ = true;
if (e->direction == GDK_SCROLL_UP) {
direction_up = true;
}
if (e->direction == GDK_SCROLL_DOWN) {
direction_up = false;
}
if (e->direction == GDK_SCROLL_SMOOTH) {
gdouble delta_x, delta_y;
gdk_event_get_scroll_deltas(reinterpret_cast<const GdkEvent *>(e), &delta_x, &delta_y);
if (delta_y < 0) {
direction_up = true;
} else if (delta_y > 0) {
direction_up = false;
}
}
// isDouble returns true for integers as well, just in case // isDouble returns true for integers as well, just in case
if (config_["scroll-step"].isDouble()) { if (config_["scroll-step"].isDouble()) {
change = round(config_["scroll-step"].asDouble() * volume_tick); change = round(config_["scroll-step"].asDouble() * volume_tick);
} }
if (dir == SCROLL_DIR::UP) {
if (direction_up) {
if (volume_ + 1 < 100) { if (volume_ + 1 < 100) {
pa_cvolume_inc(&pa_volume, change); pa_cvolume_inc(&pa_volume, change);
} }
} else { } else if (dir == SCROLL_DIR::DOWN) {
if (volume_ - 1 > 0) { if (volume_ - 1 >= 0) {
pa_cvolume_dec(&pa_volume, change); pa_cvolume_dec(&pa_volume, change);
} }
} }
pa_context_set_sink_volume_by_index(context_, sink_idx_, &pa_volume, volumeModifyCb, this); pa_context_set_sink_volume_by_index(context_, sink_idx_, &pa_volume, volumeModifyCb, this);
return true; return true;
} }
@ -125,8 +103,14 @@ void waybar::modules::Pulseaudio::subscribeCb(pa_context * conte
pa_subscription_event_type_t type, uint32_t idx, pa_subscription_event_type_t type, uint32_t idx,
void *data) { void *data) {
unsigned facility = type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK; unsigned facility = type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK;
unsigned operation = type & PA_SUBSCRIPTION_EVENT_TYPE_MASK;
if (operation != PA_SUBSCRIPTION_EVENT_CHANGE) {
return;
}
if (facility == PA_SUBSCRIPTION_EVENT_SINK) { if (facility == PA_SUBSCRIPTION_EVENT_SINK) {
pa_context_get_sink_info_by_index(context, idx, sinkInfoCb, data); pa_context_get_sink_info_by_index(context, idx, sinkInfoCb, data);
} else if (facility == PA_SUBSCRIPTION_EVENT_SOURCE) {
pa_context_get_source_info_by_index(context, idx, sourceInfoCb, data);
} }
} }
@ -140,6 +124,23 @@ void waybar::modules::Pulseaudio::volumeModifyCb(pa_context *c, int success, voi
} }
} }
/*
* Called when the requested source information is ready.
*/
void waybar::modules::Pulseaudio::sourceInfoCb(pa_context * /*context*/, const pa_source_info *i,
int /*eol*/, void *data) {
if (i != nullptr) {
auto self = static_cast<waybar::modules::Pulseaudio *>(data);
auto source_volume = static_cast<float>(pa_cvolume_avg(&(i->volume))) / float{PA_VOLUME_NORM};
self->source_volume_ = std::round(source_volume * 100.0F);
self->source_idx_ = i->index;
self->source_muted_ = i->mute != 0;
self->source_desc_ = i->description;
self->source_port_name_ = i->active_port != nullptr ? i->active_port->name : "Unknown";
self->dp.emit();
}
}
/* /*
* Called when the requested sink information is ready. * Called when the requested sink information is ready.
*/ */
@ -166,6 +167,7 @@ void waybar::modules::Pulseaudio::sinkInfoCb(pa_context * /*context*/, const pa_
void waybar::modules::Pulseaudio::serverInfoCb(pa_context *context, const pa_server_info *i, void waybar::modules::Pulseaudio::serverInfoCb(pa_context *context, const pa_server_info *i,
void *data) { void *data) {
pa_context_get_sink_info_by_name(context, i->default_sink_name, sinkInfoCb, data); pa_context_get_sink_info_by_name(context, i->default_sink_name, sinkInfoCb, data);
pa_context_get_source_info_by_name(context, i->default_source_name, sourceInfoCb, data);
} }
static const std::array<std::string, 9> ports = { static const std::array<std::string, 9> ports = {
@ -206,13 +208,20 @@ auto waybar::modules::Pulseaudio::update() -> void {
label_.get_style_context()->remove_class("bluetooth"); label_.get_style_context()->remove_class("bluetooth");
} }
} }
label_.set_markup(fmt::format( // TODO: find a better way to split source/sink
format, fmt::arg("volume", volume_), fmt::arg("icon", getIcon(volume_, getPortIcon())))); std::string format_source = "{volume}%";
if (source_muted_ && config_["format-source-muted"].isString()) {
format_source = config_["format-source-muted"].asString();
} else if (!source_muted_ && config_["format-source"].isString()) {
format_source = config_["format-source"].asString();
}
format_source = fmt::format(format_source, fmt::arg("volume", source_volume_));
label_.set_markup(fmt::format(format,
fmt::arg("volume", volume_),
fmt::arg("format_source", format_source),
fmt::arg("icon", getIcon(volume_, getPortIcon()))));
getState(volume_); getState(volume_);
if (tooltipEnabled()) { if (tooltipEnabled()) {
label_.set_tooltip_text(desc_); label_.set_tooltip_text(desc_);
} }
if (scrolling_) {
scrolling_ = false;
}
} }

View File

@ -1,5 +1,6 @@
#include "modules/sni/host.hpp" #include "modules/sni/host.hpp"
#include <iostream> #include <spdlog/spdlog.h>
#include <fmt/ostream.h>
namespace waybar::modules::SNI { namespace waybar::modules::SNI {
@ -63,14 +64,14 @@ void Host::proxyReady(GObject* src, GAsyncResult* res, gpointer data) {
GError* error = nullptr; GError* error = nullptr;
SnWatcher* watcher = sn_watcher_proxy_new_finish(res, &error); SnWatcher* watcher = sn_watcher_proxy_new_finish(res, &error);
if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
std::cerr << error->message << std::endl; spdlog::error("Host: {}", error->message);
g_error_free(error); g_error_free(error);
return; return;
} }
auto host = static_cast<SNI::Host*>(data); auto host = static_cast<SNI::Host*>(data);
host->watcher_ = watcher; host->watcher_ = watcher;
if (error != nullptr) { if (error != nullptr) {
std::cerr << error->message << std::endl; spdlog::error("Host: {}", error->message);
g_error_free(error); g_error_free(error);
return; return;
} }
@ -82,13 +83,13 @@ void Host::registerHost(GObject* src, GAsyncResult* res, gpointer data) {
GError* error = nullptr; GError* error = nullptr;
sn_watcher_call_register_host_finish(SN_WATCHER(src), res, &error); sn_watcher_call_register_host_finish(SN_WATCHER(src), res, &error);
if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
std::cerr << error->message << std::endl; spdlog::error("Host: {}", error->message);
g_error_free(error); g_error_free(error);
return; return;
} }
auto host = static_cast<SNI::Host*>(data); auto host = static_cast<SNI::Host*>(data);
if (error != nullptr) { if (error != nullptr) {
std::cerr << error->message << std::endl; spdlog::error("Host: {}", error->message);
g_error_free(error); g_error_free(error);
return; return;
} }

View File

@ -1,6 +1,32 @@
#include "modules/sni/item.hpp" #include "modules/sni/item.hpp"
#include <glibmm/main.h> #include <glibmm/main.h>
#include <iostream> #include <spdlog/spdlog.h>
template <>
struct fmt::formatter<Glib::ustring> : formatter<std::string> {
template <typename FormatContext>
auto format(const Glib::ustring& value, FormatContext& ctx) {
return formatter<std::string>::format(value, ctx);
}
};
template <>
struct fmt::formatter<Glib::VariantBase> : formatter<std::string> {
bool is_printable(const Glib::VariantBase& value) {
auto type = value.get_type_string();
/* Print only primitive (single character excluding 'v') and short complex types */
return (type.length() == 1 && islower(type[0]) && type[0] != 'v') || value.get_size() <= 32;
}
template <typename FormatContext>
auto format(const Glib::VariantBase& value, FormatContext& ctx) {
if (is_printable(value)) {
return formatter<std::string>::format(value.print(), ctx);
} else {
return formatter<std::string>::format(value.get_type_string(), ctx);
}
}
};
namespace waybar::modules::SNI { namespace waybar::modules::SNI {
@ -47,23 +73,16 @@ void Item::proxyReady(Glib::RefPtr<Gio::AsyncResult>& result) {
this->proxy_->signal_signal().connect(sigc::mem_fun(*this, &Item::onSignal)); this->proxy_->signal_signal().connect(sigc::mem_fun(*this, &Item::onSignal));
if (this->id.empty() || this->category.empty() || this->status.empty()) { if (this->id.empty() || this->category.empty() || this->status.empty()) {
std::cerr << "Invalid Status Notifier Item: " + this->bus_name + "," + this->object_path spdlog::error("Invalid Status Notifier Item: {}, {}", bus_name, object_path);
<< std::endl;
return; return;
} }
this->updateImage(); this->updateImage();
// this->event_box.set_tooltip_text(this->title); // this->event_box.set_tooltip_text(this->title);
} catch (const Glib::Error& err) { } catch (const Glib::Error& err) {
g_error("Failed to create DBus Proxy for %s %s: %s", spdlog::error("Failed to create DBus Proxy for {} {}: {}", bus_name, object_path, err.what());
bus_name.c_str(),
object_path.c_str(),
err.what().c_str());
} catch (const std::exception& err) { } catch (const std::exception& err) {
g_error("Failed to create DBus Proxy for %s %s: %s", spdlog::error("Failed to create DBus Proxy for {} {}: {}", bus_name, object_path, err.what());
bus_name.c_str(),
object_path.c_str(),
err.what());
} }
} }
@ -73,6 +92,9 @@ T get_variant(Glib::VariantBase& value) {
} }
void Item::setProperty(const Glib::ustring& name, Glib::VariantBase& value) { void Item::setProperty(const Glib::ustring& name, Glib::VariantBase& value) {
try {
spdlog::trace("Set tray item property: {}.{} = {}", id.empty() ? bus_name : id, name, value);
if (name == "Category") { if (name == "Category") {
category = get_variant<std::string>(value); category = get_variant<std::string>(value);
} else if (name == "Id") { } else if (name == "Id") {
@ -109,6 +131,19 @@ void Item::setProperty(const Glib::ustring& name, Glib::VariantBase& value) {
} else if (name == "ItemIsMenu") { } else if (name == "ItemIsMenu") {
item_is_menu = get_variant<bool>(value); item_is_menu = get_variant<bool>(value);
} }
} catch (const Glib::Error& err) {
spdlog::warn("Failed to set tray item property: {}.{}, value = {}, err = {}",
id.empty() ? bus_name : id,
name,
value,
err.what());
} catch (const std::exception& err) {
spdlog::warn("Failed to set tray item property: {}.{}, value = {}, err = {}",
id.empty() ? bus_name : id,
name,
value,
err.what());
}
} }
void Item::getUpdatedProperties() { void Item::getUpdatedProperties() {
@ -132,7 +167,7 @@ void Item::processUpdatedProperties(Glib::RefPtr<Gio::AsyncResult>& _result) {
for (const auto& [name, value] : properties) { for (const auto& [name, value] : properties) {
Glib::VariantBase old_value; Glib::VariantBase old_value;
proxy_->get_cached_property(old_value, name); proxy_->get_cached_property(old_value, name);
if (!value.equal(old_value)) { if (!old_value || !value.equal(old_value)) {
proxy_->set_cached_property(name, value); proxy_->set_cached_property(name, value);
setProperty(name, const_cast<Glib::VariantBase&>(value)); setProperty(name, const_cast<Glib::VariantBase&>(value));
} }
@ -141,14 +176,15 @@ void Item::processUpdatedProperties(Glib::RefPtr<Gio::AsyncResult>& _result) {
this->updateImage(); this->updateImage();
// this->event_box.set_tooltip_text(this->title); // this->event_box.set_tooltip_text(this->title);
} catch (const Glib::Error& err) { } catch (const Glib::Error& err) {
g_warning("Failed to update properties: %s", err.what().c_str()); spdlog::warn("Failed to update properties: {}", err.what());
} catch (const std::exception& err) { } catch (const std::exception& err) {
g_warning("Failed to update properties: %s", err.what()); spdlog::warn("Failed to update properties: {}", err.what());
} }
} }
void Item::onSignal(const Glib::ustring& sender_name, const Glib::ustring& signal_name, void Item::onSignal(const Glib::ustring& sender_name, const Glib::ustring& signal_name,
const Glib::VariantContainerBase& arguments) { const Glib::VariantContainerBase& arguments) {
spdlog::trace("Tray item '{}' got signal {}", id, signal_name);
if (!update_pending_ && signal_name.compare(0, 3, "New") == 0) { if (!update_pending_ && signal_name.compare(0, 3, "New") == 0) {
/* Debounce signals and schedule update of all properties. /* Debounce signals and schedule update of all properties.
* Based on behavior of Plasma dataengine for StatusNotifierItem. * Based on behavior of Plasma dataengine for StatusNotifierItem.
@ -235,7 +271,7 @@ void Item::updateImage() {
image.set(getIconByName(icon_name, icon_size)); image.set(getIconByName(icon_name, icon_size));
} }
} catch (Glib::Error& e) { } catch (Glib::Error& e) {
std::cerr << "Exception: " << e.what() << std::endl; spdlog::error("Item '{}': {}", id, static_cast<std::string>(e.what()));
} }
} else if (icon_pixmap) { } else if (icon_pixmap) {
// An icon extracted may be the wrong size for the tray // An icon extracted may be the wrong size for the tray
@ -298,7 +334,7 @@ void Item::makeMenu(GdkEventButton* const& ev) {
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 || !menu.empty())) || ev->button == 3) {
makeMenu(ev); makeMenu(ev);
if (gtk_menu != nullptr) { if (gtk_menu != nullptr) {
#if GTK_CHECK_VERSION(3, 22, 0) #if GTK_CHECK_VERSION(3, 22, 0)

View File

@ -1,14 +1,19 @@
#include "modules/sni/tray.hpp" #include "modules/sni/tray.hpp"
#include <iostream> #include <spdlog/spdlog.h>
namespace waybar::modules::SNI { namespace waybar::modules::SNI {
Tray::Tray(const std::string& id, const Bar& bar, const Json::Value& config) Tray::Tray(const std::string& id, const Bar& bar, const Json::Value& config)
: config_(config), : AModule(config, "tray", id),
box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0), box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0),
watcher_(nb_hosts_),
host_(nb_hosts_, config, std::bind(&Tray::onAdd, this, std::placeholders::_1), host_(nb_hosts_, config, std::bind(&Tray::onAdd, this, std::placeholders::_1),
std::bind(&Tray::onRemove, this, std::placeholders::_1)) { std::bind(&Tray::onRemove, this, std::placeholders::_1)) {
spdlog::warn(
"For a functionnal tray you must have libappindicator-* installed and export "
"XDG_CURRENT_DESKTOP=Unity");
box_.set_name("tray"); box_.set_name("tray");
event_box_.add(box_);
if (!id.empty()) { if (!id.empty()) {
box_.get_style_context()->add_class(id); box_.get_style_context()->add_class(id);
} }
@ -37,6 +42,4 @@ auto Tray::update() -> void {
} }
} }
Tray::operator Gtk::Widget&() { return box_; } } // namespace waybar::modules::SNI
}

View File

@ -1,16 +1,16 @@
#include "modules/sni/watcher.hpp" #include "modules/sni/watcher.hpp"
#include <spdlog/spdlog.h>
#include <iostream>
using namespace waybar::modules::SNI; using namespace waybar::modules::SNI;
Watcher::Watcher() Watcher::Watcher(std::size_t id)
: bus_name_id_(Gio::DBus::own_name(Gio::DBus::BusType::BUS_TYPE_SESSION, : bus_name_id_(Gio::DBus::own_name(Gio::DBus::BusType::BUS_TYPE_SESSION,
"org.kde.StatusNotifierWatcher", "org.kde.StatusNotifierWatcher",
sigc::mem_fun(*this, &Watcher::busAcquired), sigc::mem_fun(*this, &Watcher::busAcquired),
Gio::DBus::SlotNameAcquired(), Gio::DBus::SlotNameLost(), Gio::DBus::SlotNameAcquired(), Gio::DBus::SlotNameLost(),
Gio::DBus::BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT | Gio::DBus::BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT |
Gio::DBus::BUS_NAME_OWNER_FLAGS_REPLACE)), Gio::DBus::BUS_NAME_OWNER_FLAGS_REPLACE)),
watcher_id_(id),
watcher_(sn_watcher_skeleton_new()) {} watcher_(sn_watcher_skeleton_new()) {}
Watcher::~Watcher() { Watcher::~Watcher() {
@ -34,7 +34,7 @@ void Watcher::busAcquired(const Glib::RefPtr<Gio::DBus::Connection>& conn, Glib:
if (error != nullptr) { if (error != nullptr) {
// Don't print an error when a watcher is already present // Don't print an error when a watcher is already present
if (error->code != 2) { if (error->code != 2) {
std::cerr << error->message << std::endl; spdlog::error("Watcher {}: {}", watcher_id_, error->message);
} }
g_error_free(error); g_error_free(error);
return; return;

View File

@ -1,12 +1,9 @@
#include "modules/sway/mode.hpp" #include "modules/sway/mode.hpp"
#include <spdlog/spdlog.h>
namespace waybar::modules::sway { namespace waybar::modules::sway {
Mode::Mode(const std::string& id, const Json::Value& config) : ALabel(config, "{}") { Mode::Mode(const std::string& id, const Json::Value& config) : ALabel(config, "mode", id, "{}") {
label_.set_name("mode");
if (!id.empty()) {
label_.get_style_context()->add_class(id);
}
ipc_.subscribe(R"(["mode"])"); ipc_.subscribe(R"(["mode"])");
ipc_.signal_event.connect(sigc::mem_fun(*this, &Mode::onEvent)); ipc_.signal_event.connect(sigc::mem_fun(*this, &Mode::onEvent));
// Launch worker // Launch worker
@ -16,15 +13,16 @@ Mode::Mode(const std::string& id, const Json::Value& config) : ALabel(config, "{
void Mode::onEvent(const struct Ipc::ipc_response& res) { void Mode::onEvent(const struct Ipc::ipc_response& res) {
try { try {
std::lock_guard<std::mutex> lock(mutex_);
auto payload = parser_.parse(res.payload); auto payload = parser_.parse(res.payload);
if (payload["change"] != "default") { if (payload["change"] != "default") {
mode_ = payload["change"].asString(); mode_ = Glib::Markup::escape_text(payload["change"].asString());
} else { } else {
mode_.clear(); mode_.clear();
} }
dp.emit(); dp.emit();
} catch (const std::exception& e) { } catch (const std::exception& e) {
std::cerr << "Mode: " << e.what() << std::endl; spdlog::error("Mode: {}", e.what());
} }
} }
@ -33,7 +31,7 @@ void Mode::worker() {
try { try {
ipc_.handleEvent(); ipc_.handleEvent();
} catch (const std::exception& e) { } catch (const std::exception& e) {
std::cerr << "Mode: " << e.what() << std::endl; spdlog::error("Mode: {}", e.what());
} }
}; };
} }

View File

@ -1,15 +1,11 @@
#include "modules/sway/window.hpp" #include "modules/sway/window.hpp"
#include <spdlog/spdlog.h>
namespace waybar::modules::sway { namespace waybar::modules::sway {
Window::Window(const std::string& id, const Bar& bar, const Json::Value& config) Window::Window(const std::string& id, const Bar& bar, const Json::Value& config)
: ALabel(config, "{}"), bar_(bar), windowId_(-1) { : ALabel(config, "window", id, "{}"), bar_(bar), windowId_(-1) {
label_.set_name("window");
if (!id.empty()) {
label_.get_style_context()->add_class(id);
}
if (label_.get_max_width_chars() == -1) { if (label_.get_max_width_chars() == -1) {
label_.set_hexpand(true);
label_.set_ellipsize(Pango::EllipsizeMode::ELLIPSIZE_END); label_.set_ellipsize(Pango::EllipsizeMode::ELLIPSIZE_END);
} }
ipc_.subscribe(R"(["window","workspace"])"); ipc_.subscribe(R"(["window","workspace"])");
@ -27,35 +23,11 @@ void Window::onCmd(const struct Ipc::ipc_response& res) {
try { try {
std::lock_guard<std::mutex> lock(mutex_); std::lock_guard<std::mutex> lock(mutex_);
auto payload = parser_.parse(res.payload); auto payload = parser_.parse(res.payload);
auto [nb, id, name, app_id] = getFocusedNode(payload); auto output = payload["ouput"].isString() ? payload["output"].asString() : "";
if (!app_id_.empty()) { std::tie(app_nb_, windowId_, window_, app_id_) = getFocusedNode(payload["nodes"], output);
bar_.window.get_style_context()->remove_class(app_id_);
}
if (nb == 0) {
bar_.window.get_style_context()->remove_class("solo");
if (!bar_.window.get_style_context()->has_class("empty")) {
bar_.window.get_style_context()->add_class("empty");
}
} else if (nb == 1) {
bar_.window.get_style_context()->remove_class("empty");
if (!bar_.window.get_style_context()->has_class("solo")) {
bar_.window.get_style_context()->add_class("solo");
}
if (!app_id.empty() && !bar_.window.get_style_context()->has_class(app_id)) {
bar_.window.get_style_context()->add_class(app_id);
}
} else {
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(); dp.emit();
}
} catch (const std::exception& e) { } catch (const std::exception& e) {
std::cerr << "Window: " << e.what() << std::endl; spdlog::error("Window: {}", e.what());
} }
} }
@ -64,12 +36,33 @@ void Window::worker() {
try { try {
ipc_.handleEvent(); ipc_.handleEvent();
} catch (const std::exception& e) { } catch (const std::exception& e) {
std::cerr << "Window: " << e.what() << std::endl; spdlog::error("Window: {}", e.what());
} }
}; };
} }
auto Window::update() -> void { auto Window::update() -> void {
if (!old_app_id_.empty()) {
bar_.window.get_style_context()->remove_class(old_app_id_);
}
if (app_nb_ == 0) {
bar_.window.get_style_context()->remove_class("solo");
if (!bar_.window.get_style_context()->has_class("empty")) {
bar_.window.get_style_context()->add_class("empty");
}
} else if (app_nb_ == 1) {
bar_.window.get_style_context()->remove_class("empty");
if (!bar_.window.get_style_context()->has_class("solo")) {
bar_.window.get_style_context()->add_class("solo");
}
if (!app_id_.empty() && !bar_.window.get_style_context()->has_class(app_id_)) {
bar_.window.get_style_context()->add_class(app_id_);
old_app_id_ = app_id_;
}
} else {
bar_.window.get_style_context()->remove_class("solo");
bar_.window.get_style_context()->remove_class("empty");
}
label_.set_markup(fmt::format(format_, window_)); label_.set_markup(fmt::format(format_, window_));
if (tooltipEnabled()) { if (tooltipEnabled()) {
label_.set_tooltip_text(window_); label_.set_tooltip_text(window_);
@ -77,20 +70,28 @@ auto Window::update() -> void {
} }
std::tuple<std::size_t, int, std::string, std::string> Window::getFocusedNode( std::tuple<std::size_t, int, std::string, std::string> Window::getFocusedNode(
const Json::Value& nodes) { const Json::Value& nodes, std::string& output) {
for (auto const& node : nodes["nodes"]) { for (auto const& node : nodes) {
if (node["focused"].asBool() && node["type"] == "con") { if (node["output"].isString()) {
if ((!config_["all-outputs"].asBool() && nodes["output"] == bar_.output->name) || output = node["output"].asString();
}
if (node["focused"].asBool() && (node["type"] == "con" || node["type"] == "floating_con")) {
if ((!config_["all-outputs"].asBool() && output == bar_.output->name) ||
config_["all-outputs"].asBool()) { config_["all-outputs"].asBool()) {
auto app_id = node["app_id"].isString() ? node["app_id"].asString() auto app_id = node["app_id"].isString() ? node["app_id"].asString()
: node["window_properties"]["instance"].asString(); : node["window_properties"]["instance"].asString();
return {nodes["nodes"].size(), return {nodes.size(),
node["id"].asInt(), node["id"].asInt(),
Glib::Markup::escape_text(node["name"].asString()), Glib::Markup::escape_text(node["name"].asString()),
app_id}; app_id};
} }
} }
auto [nb, id, name, app_id] = getFocusedNode(node); auto [nb, id, name, app_id] = getFocusedNode(node["nodes"], output);
if (id > -1 && !name.empty()) {
return {nb, id, name, app_id};
}
// Search for floating node
std::tie(nb, id, name, app_id) = getFocusedNode(node["floating_nodes"], output);
if (id > -1 && !name.empty()) { if (id > -1 && !name.empty()) {
return {nb, id, name, app_id}; return {nb, id, name, app_id};
} }
@ -102,7 +103,7 @@ void Window::getTree() {
try { try {
ipc_.sendCmd(IPC_GET_TREE); ipc_.sendCmd(IPC_GET_TREE);
} catch (const std::exception& e) { } catch (const std::exception& e) {
std::cerr << e.what() << std::endl; spdlog::error("Window: {}", e.what());
} }
} }

View File

@ -1,22 +1,23 @@
#include "modules/sway/workspaces.hpp" #include "modules/sway/workspaces.hpp"
#include <spdlog/spdlog.h>
namespace waybar::modules::sway { namespace waybar::modules::sway {
Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value &config) Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value &config)
: bar_(bar), : AModule(config, "workspaces", id, false, !config["disable-scroll"].asBool()),
config_(config), bar_(bar),
box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0), box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0) {
scrolling_(false) {
box_.set_name("workspaces"); box_.set_name("workspaces");
if (!id.empty()) { if (!id.empty()) {
box_.get_style_context()->add_class(id); box_.get_style_context()->add_class(id);
} }
event_box_.add(box_);
ipc_.subscribe(R"(["workspace"])"); ipc_.subscribe(R"(["workspace"])");
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()) { if (config["enable-bar-scroll"].asBool()) {
auto &window = const_cast<Bar&>(bar_).window; auto &window = const_cast<Bar &>(bar_).window;
window.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK); window.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK);
window.signal_scroll_event().connect(sigc::mem_fun(*this, &Workspaces::handleScroll)); window.signal_scroll_event().connect(sigc::mem_fun(*this, &Workspaces::handleScroll));
} }
@ -28,16 +29,16 @@ void Workspaces::onEvent(const struct Ipc::ipc_response &res) {
try { try {
ipc_.sendCmd(IPC_GET_WORKSPACES); ipc_.sendCmd(IPC_GET_WORKSPACES);
} catch (const std::exception &e) { } catch (const std::exception &e) {
std::cerr << "Workspaces: " << e.what() << std::endl; spdlog::error("Workspaces: {}", e.what());
} }
} }
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) {
try { try {
auto payload = parser_.parse(res.payload); {
if (payload.isArray()) {
std::lock_guard<std::mutex> lock(mutex_); std::lock_guard<std::mutex> lock(mutex_);
auto payload = parser_.parse(res.payload);
workspaces_.clear(); workspaces_.clear();
std::copy_if(payload.begin(), std::copy_if(payload.begin(),
payload.end(), payload.end(),
@ -47,14 +48,53 @@ void Workspaces::onCmd(const struct Ipc::ipc_response &res) {
? workspace["output"].asString() == bar_.output->name ? workspace["output"].asString() == bar_.output->name
: true; : true;
}); });
dp.emit();
// adding persistant workspaces (as per the config file)
if (config_["persistant_workspaces"].isObject()) {
const Json::Value & p_workspaces = config_["persistant_workspaces"];
const std::vector<std::string> p_workspaces_names = p_workspaces.getMemberNames();
for (const std::string &p_w_name : p_workspaces_names) {
const Json::Value &p_w = p_workspaces[p_w_name];
auto it =
std::find_if(payload.begin(), payload.end(), [&p_w_name](const Json::Value &node) {
return node["name"].asString() == p_w_name;
});
if (it != payload.end()) {
continue; // already displayed by some bar
}
if (p_w.isArray() && !p_w.empty()) {
// Adding to target outputs
for (const Json::Value &output : p_w) {
if (output.asString() == bar_.output->name) {
Json::Value v;
v["name"] = p_w_name;
v["target_output"] = bar_.output->name;
workspaces_.emplace_back(std::move(v));
break;
} }
} catch (const std::exception &e) {
std::cerr << "Workspaces: " << e.what() << std::endl;
} }
} else { } else {
if (scrolling_) { // Adding to all outputs
scrolling_ = false; Json::Value v;
v["name"] = p_w_name;
v["target_output"] = "";
workspaces_.emplace_back(std::move(v));
}
}
std::sort(workspaces_.begin(),
workspaces_.end(),
[](const Json::Value &lhs, const Json::Value &rhs) {
return lhs["name"].asString() < rhs["name"].asString();
});
}
}
dp.emit();
} catch (const std::exception &e) {
spdlog::error("Workspaces: {}", e.what());
} }
} }
} }
@ -64,7 +104,7 @@ void Workspaces::worker() {
try { try {
ipc_.handleEvent(); ipc_.handleEvent();
} catch (const std::exception &e) { } catch (const std::exception &e) {
std::cerr << "Workspaces: " << e.what() << std::endl; spdlog::error("Workspaces: {}", e.what());
} }
}; };
} }
@ -110,6 +150,11 @@ auto Workspaces::update() -> void {
} else { } else {
button.get_style_context()->remove_class("urgent"); button.get_style_context()->remove_class("urgent");
} }
if ((*it)["target_output"].isString()) {
button.get_style_context()->add_class("persistant");
} else {
button.get_style_context()->remove_class("persistant");
}
if (needReorder) { if (needReorder) {
box_.reorder_child(button, it - workspaces_.begin()); box_.reorder_child(button, it - workspaces_.begin());
} }
@ -132,20 +177,25 @@ auto Workspaces::update() -> void {
Gtk::Button &Workspaces::addButton(const Json::Value &node) { Gtk::Button &Workspaces::addButton(const Json::Value &node) {
auto pair = buttons_.emplace(node["name"].asString(), node["name"].asString()); auto pair = buttons_.emplace(node["name"].asString(), node["name"].asString());
auto &button = pair.first->second; auto &&button = pair.first->second;
box_.pack_start(button, false, false, 0); box_.pack_start(button, false, false, 0);
button.set_relief(Gtk::RELIEF_NONE); button.set_relief(Gtk::RELIEF_NONE);
button.signal_clicked().connect([this, pair] { button.signal_clicked().connect([this, node] {
try { try {
ipc_.sendCmd(IPC_COMMAND, fmt::format("workspace \"{}\"", pair.first->first)); if (node["target_output"].isString()) {
ipc_.sendCmd(
IPC_COMMAND,
fmt::format("workspace \"{}\"; move workspace to output \"{}\"; workspace \"{}\"",
node["name"].asString(),
node["target_output"].asString(),
node["name"].asString()));
} else {
ipc_.sendCmd(IPC_COMMAND, fmt::format("workspace \"{}\"", node["name"].asString()));
}
} catch (const std::exception &e) { } catch (const std::exception &e) {
std::cerr << e.what() << std::endl; spdlog::error("Workspaces: {}", e.what());
} }
}); });
if (!config_["disable-scroll"].asBool()) {
button.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK);
button.signal_scroll_event().connect(sigc::mem_fun(*this, &Workspaces::handleScroll));
}
return button; return button;
} }
@ -164,51 +214,32 @@ std::string Workspaces::getIcon(const std::string &name, const Json::Value &node
} }
bool Workspaces::handleScroll(GdkEventScroll *e) { bool Workspaces::handleScroll(GdkEventScroll *e) {
// Avoid concurrent scroll event auto dir = AModule::getScrollDir(e);
if (scrolling_) { if (dir == SCROLL_DIR::NONE) {
return false; return true;
} }
std::string name;
scrolling_ = true;
{
std::lock_guard<std::mutex> lock(mutex_); std::lock_guard<std::mutex> lock(mutex_);
auto it = std::find_if(workspaces_.begin(), workspaces_.end(), [](const auto &workspace) { auto it = std::find_if(workspaces_.begin(), workspaces_.end(), [](const auto &workspace) {
return workspace["focused"].asBool(); return workspace["focused"].asBool();
}); });
if (it == workspaces_.end()) { if (it == workspaces_.end()) {
scrolling_ = false; return true;
return false;
} }
switch (e->direction) { std::string name;
case GDK_SCROLL_DOWN: if (dir == SCROLL_DIR::DOWN || dir == SCROLL_DIR::RIGHT) {
case GDK_SCROLL_RIGHT:
name = getCycleWorkspace(it, false); name = getCycleWorkspace(it, false);
break; } else if (dir == SCROLL_DIR::UP || dir == SCROLL_DIR::LEFT) {
case GDK_SCROLL_UP:
case GDK_SCROLL_LEFT:
name = getCycleWorkspace(it, true); name = getCycleWorkspace(it, true);
break; } else {
case GDK_SCROLL_SMOOTH: return true;
gdouble delta_x, delta_y;
gdk_event_get_scroll_deltas(reinterpret_cast<const GdkEvent *>(e), &delta_x, &delta_y);
if (delta_y < 0) {
name = getCycleWorkspace(it, true);
} else if (delta_y > 0) {
name = getCycleWorkspace(it, false);
}
break;
default:
break;
}
if (name.empty() || name == (*it)["name"].asString()) {
scrolling_ = false;
return false;
} }
if (name == (*it)["name"].asString()) {
return true;
} }
try { try {
ipc_.sendCmd(IPC_COMMAND, fmt::format("workspace \"{}\"", name)); ipc_.sendCmd(IPC_COMMAND, fmt::format("workspace \"{}\"", name));
} catch (const std::exception &e) { } catch (const std::exception &e) {
std::cerr << "Workspaces: " << e.what() << std::endl; spdlog::error("Workspaces: {}", e.what());
} }
return true; return true;
} }
@ -252,6 +283,4 @@ void Workspaces::onButtonReady(const Json::Value &node, Gtk::Button &button) {
} }
} }
Workspaces::operator Gtk::Widget &() { return box_; }
} // namespace waybar::modules::sway } // namespace waybar::modules::sway

View File

@ -1,7 +1,7 @@
#include "modules/temperature.hpp" #include "modules/temperature.hpp"
waybar::modules::Temperature::Temperature(const std::string& id, const Json::Value& config) waybar::modules::Temperature::Temperature(const std::string& id, const Json::Value& config)
: ALabel(config, "{temperatureC}°C", 10) { : ALabel(config, "temperature", id, "{temperatureC}°C", 10) {
if (config_["hwmon-path"].isString()) { if (config_["hwmon-path"].isString()) {
file_path_ = config_["hwmon-path"].asString(); file_path_ = config_["hwmon-path"].asString();
} else { } else {
@ -12,10 +12,6 @@ waybar::modules::Temperature::Temperature(const std::string& id, const Json::Val
if (!temp.is_open()) { if (!temp.is_open()) {
throw std::runtime_error("Can't open " + file_path_); throw std::runtime_error("Can't open " + file_path_);
} }
label_.set_name("temperature");
if (!id.empty()) {
label_.get_style_context()->add_class(id);
}
thread_ = [this] { thread_ = [this] {
dp.emit(); dp.emit();
thread_.sleep_for(interval_); thread_.sleep_for(interval_);

10
subprojects/spdlog.wrap Normal file
View File

@ -0,0 +1,10 @@
[wrap-file]
directory = spdlog-1.3.1
source_url = https://github.com/gabime/spdlog/archive/v1.3.1.tar.gz
source_filename = v1.3.1.tar.gz
source_hash = 160845266e94db1d4922ef755637f6901266731c4cb3b30b45bf41efa0e6ab70
patch_url = https://wrapdb.mesonbuild.com/v1/projects/spdlog/1.3.1/1/get_zip
patch_filename = spdlog-1.3.1-1-wrap.zip
patch_hash = 715a0229781019b853d409cc0bf891ee4b9d3a17bec0cf87f4ad30b28bbecc87