Compare commits

...

134 Commits
0.6.3 ... 0.6.7

Author SHA1 Message Date
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
6ffc7ee3b3 chore: 0.6.5 2019-05-18 16:12:19 +02:00
ff28de0482 feat(custom): update on click/scroll 2019-05-18 16:07:55 +02:00
d5e8a37e63 Adding bandwidth support for network module (#328)
Adding bandwidth support for network module
2019-05-18 15:52:37 +02:00
67786c32a8 fix(merge): re-add missing code 2019-05-18 15:45:18 +02:00
93a644eec4 Merge branch 'master' into master 2019-05-18 15:42:27 +02:00
aa385e28b6 refactor: execute update on idle 2019-05-18 15:32:40 +02:00
2c1a3d0430 Adding logging 2019-05-18 09:27:47 -04:00
b31a64ad00 Displaying in ko/s and kb/s instead of interval dependant unit 2019-05-18 09:13:00 -04:00
4865a9ad6c fix(network): reset frequency 2019-05-18 13:57:50 +02:00
e70d8aff73 Merge pull request #327 from RX14/feature/disable-scroll-wraparound-option
Add option to disable scroll wraparound on workspaces
2019-05-18 13:50:35 +02:00
3e1c77d158 Add option to disable scroll wraparound on workspaces 2019-05-18 12:15:35 +01:00
d34c3a801c fix(Network): less updates 2019-05-18 12:27:10 +02:00
794fb12e8c Adding bandwidth support for network module [linux only] 2019-05-17 23:39:51 -04:00
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
43d724ebad Merge pull request #326 from RX14/rename-bar-scroll
Rename the "disable-workspace-scroll" option to "disable-bar-scroll"
2019-05-17 20:24:15 +02:00
c862fde986 Merge pull request #325 from RX14/bugfix/scroll-broken
Fix workspace scroll wrapping off the end of the list
2019-05-17 20:23:51 +02:00
0d59f7b7d1 Rename the "disable-workspace-scroll" option to "disable-bar-scroll" 2019-05-17 17:42:11 +01:00
1e95f5d9b6 Fix workspace scroll wrapping off the end of the list 2019-05-17 17:37:24 +01:00
9234be8544 revert: re-add rountrip before widgets setup 2019-05-17 14:45:02 +02:00
9d3255fe9f fix: remove redundant roundtrip 2019-05-17 14:41:12 +02:00
d2d9db23b5 fix: uninitialized bool 2019-05-17 14:23:52 +02:00
d8be72e4b6 refactor: unexport tray watcher 2019-05-17 13:51:55 +02:00
f8a47598ba fix: roundtrip before bar creation 2019-05-17 13:40:04 +02:00
cb2d6e1997 feat(Network): frequency 2019-05-17 11:27:38 +02:00
17291dffdf fix(Battery): plugged state 2019-05-17 10:59:54 +02:00
9a091d7740 chore: 0.6.4 2019-05-17 10:18:05 +02:00
2cb70c7324 Merge pull request #322 from RX14/feature/generic-scroll
Allow scrolling on the entire bar surface
2019-05-17 10:03:52 +02:00
4d4cadb5ae refactor: simpler code 2019-05-17 09:59:37 +02:00
cddee2626a Change scroll-step unit to percent (#316)
Change scroll-step unit to percent
2019-05-17 09:44:34 +02:00
d5c1e6f312 Change scroll-step unit to percent 2019-05-17 11:19:58 +08:00
b45dcdf74e Allow scrolling on the entire bar surface 2019-05-16 22:18:43 +01:00
a1ffa7fa9f chore: enable alpine on Travis 2019-05-16 17:20:27 +02:00
4b4b74db0c feat(Battery): get icon by state 2019-05-16 17:18:27 +02:00
8901df9702 chore: add alpine dockerfile 2019-05-16 17:10:35 +02:00
e12766a656 fix: compilation on some os 2019-05-16 17:09:25 +02:00
31f63398dc Merge pull request #319 from Alexays/network
Network improvements
2019-05-16 14:11:32 +02:00
0a14e7f3ab feat: ipv6 family 2019-05-16 12:22:08 +02:00
31416ffae6 fix: bar removed 2019-05-16 12:19:47 +02:00
fbe19d886a fix: drop memberships 2019-05-16 12:16:44 +02:00
9c67150884 refactor: prepare ipv6 2019-05-16 12:14:12 +02:00
f3c467cc46 refactor: remove non wanted headers 2019-05-16 11:27:22 +02:00
a09d2222be style: remove non wanted tags 2019-05-16 11:26:48 +02:00
45ebee52a6 fix: typo 2019-05-16 11:26:06 +02:00
841576497a refactor: cleaner events 2019-05-16 11:22:22 +02:00
e730105950 Merge pull request #318 from unresolvedsymbol/patch
Fix state behavior
2019-05-16 09:40:09 +02:00
963d4f68e4 refactor: remove useless param 2019-05-16 09:39:06 +02:00
7e8eee0571 fix state behavior 2019-05-15 22:14:51 -05:00
37f87be9dd Merge pull request #317 from jorgenbele/master
fix(battery): change comparison expr. to assignment
2019-05-15 21:02:04 +02:00
380fc58f3c fix(battery): change comparison expr. to assignment 2019-05-15 19:19:00 +02:00
22bf0b161a fix(Network): do not stop thread 2019-05-15 10:24:35 +02:00
5b3402e110 feat(Battery): plugged status 2019-05-14 15:43:57 +02:00
d209d350fe style(media): reduce min-width 2019-05-13 15:48:18 +02:00
0968170074 style(media): min-width 2019-05-13 15:40:02 +02:00
362c393b1d refactor: try/catch, sigc trackable 2019-05-13 15:15:50 +02:00
0c3c548bc0 fix(Window): avoid concurrency 2019-05-13 14:35:45 +02:00
b54160e02f fix(Tray): add item if not exist 2019-05-13 14:27:01 +02:00
db14fac038 style: remove chromium style 2019-05-13 13:23:32 +02:00
4f1defe6d5 fix(Pulseaudio): avoid handleScroll override 2019-05-13 11:46:12 +02:00
92967c7c06 fix(Label): reverse only battery states 2019-05-13 11:36:34 +02:00
fcb23d3104 feat(temperature): format-icons 2019-05-13 11:31:05 +02:00
62f8af8a39 fix(Window): avoid multiple same classes 2019-05-13 10:56:48 +02:00
49 changed files with 1258 additions and 709 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

@ -8,6 +8,7 @@ env:
- distro: archlinux
- distro: opensuse
- distro: fedora
- distro: alpine
before_install:
- docker pull alexays/waybar:${distro}
@ -16,4 +17,4 @@ script:
- echo FROM alexays/waybar:${distro} > Dockerfile
- echo ADD . /root >> Dockerfile
- 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"

3
Dockerfiles/alpine Normal file
View File

@ -0,0 +1,3 @@
FROM alpine:latest
RUN apk add --no-cache git meson alpine-sdk libinput-dev wayland-dev wayland-protocols mesa-dev libxkbcommon-dev eudev-dev pixman-dev gtkmm3-dev jsoncpp-dev libnl3-dev pulseaudio-dev libmpdclient-dev

View File

@ -10,10 +10,11 @@ namespace waybar {
class ALabel : public IModule {
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,
uint16_t interval = 0);
virtual ~ALabel();
virtual auto update() -> void;
virtual std::string getIcon(uint16_t, const std::string &alt = "");
virtual std::string getIcon(uint16_t, const std::string &alt = "", uint16_t max = 0);
virtual operator Gtk::Widget &();
protected:
@ -29,9 +30,9 @@ class ALabel : public IModule {
bool alt_ = false;
std::string default_format_;
virtual bool handleToggle(GdkEventButton *const &ev);
virtual bool handleScroll(GdkEventScroll *);
virtual std::string getState(uint8_t value);
virtual bool handleToggle(GdkEventButton *const &ev);
virtual bool handleScroll(GdkEventScroll *);
virtual std::string getState(uint8_t value, bool lesser = false);
private:
std::vector<int> pid_;

View File

@ -38,13 +38,13 @@ class Bar {
bool vertical = false;
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";
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";
static inline const std::string BAR_SIZE_MSG =
static constexpr const char *BAR_SIZE_MSG =
"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";
static void layerSurfaceHandleConfigure(void *, struct zwlr_layer_surface_v1 *, uint32_t,
uint32_t, uint32_t);
@ -52,6 +52,8 @@ class Bar {
void destroyOutput();
void onConfigure(GdkEventConfigure *ev);
void onRealize();
void onMap(GdkEventAny *ev);
void setMarginsAndZone(uint32_t height, uint32_t width);
auto setupWidgets() -> void;
void getModules(const Factory &, const std::string &);
@ -66,6 +68,7 @@ class Bar {
} margins_;
uint32_t width_ = 0;
uint32_t height_ = 1;
uint8_t anchor_;
Gtk::Box left_;
Gtk::Box center_;
Gtk::Box right_;

View File

@ -26,14 +26,15 @@ class Client {
private:
Client() = default;
void setupConfigs(const std::string &config, const std::string &style);
void bindInterfaces();
const std::string getValidPath(const std::vector<std::string> &paths);
std::tuple<const std::string, const std::string> getConfigs(const std::string &config,
const std::string &style) const;
void bindInterfaces();
const std::string getValidPath(const std::vector<std::string> &paths) const;
void handleOutput(std::unique_ptr<struct waybar_output> &output);
bool isValidOutput(const Json::Value &config, std::unique_ptr<struct waybar_output> &output);
auto setupConfig() -> void;
auto setupCss() -> void;
std::unique_ptr<struct waybar_output>& getOutput(uint32_t wl_name);
auto setupConfig(const std::string &config_file) -> void;
auto setupCss(const std::string &css_file) -> void;
std::unique_ptr<struct waybar_output> &getOutput(uint32_t wl_name);
std::vector<Json::Value> getOutputConfigs(std::unique_ptr<struct waybar_output> &output);
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 *);
Json::Value config_;
std::string css_file_;
std::string config_file_;
Glib::RefPtr<Gtk::StyleContext> style_context_;
Glib::RefPtr<Gtk::CssProvider> css_provider_;
std::vector<std::unique_ptr<struct waybar_output>> outputs_;

View File

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

View File

@ -9,7 +9,8 @@
#include <sys/inotify.h>
#include <algorithm>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
#include "ALabel.hpp"
#include "util/sleeper_thread.hpp"
@ -30,18 +31,20 @@ class Battery : public ALabel {
private:
static inline const fs::path data_dir_ = "/sys/class/power_supply/";
void getBatteries();
void worker();
const std::string getAdapterStatus(uint8_t capacity) const;
const std::tuple<uint8_t, std::string> getInfos() const;
void getBatteries();
void worker();
const std::string getAdapterStatus(uint8_t capacity) 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_;
fs::path adapter_;
int fd_;
std::vector<int> wds_;
std::string old_status_;
util::SleeperThread thread_;
util::SleeperThread thread_timer_;
};
} // namespace waybar::modules

View File

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

View File

@ -2,9 +2,11 @@
#include <fmt/format.h>
#include <sys/sysinfo.h>
#include <cstdint>
#include <fstream>
#include <iostream>
#include <numeric>
#include <string>
#include <utility>
#include <vector>
#include "ALabel.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>> prev_times_;
waybar::util::SleeperThread thread_;
util::SleeperThread thread_;
};
} // namespace waybar::modules

View File

@ -2,7 +2,7 @@
#include <fmt/format.h>
#include <csignal>
#include <iostream>
#include <string>
#include "ALabel.hpp"
#include "util/command.hpp"
#include "util/json.hpp"
@ -12,7 +12,7 @@ namespace waybar::modules {
class Custom : public ALabel {
public:
Custom(const std::string&, const Json::Value&);
Custom(const std::string&, const std::string&, const Json::Value&);
~Custom();
auto update() -> void;
void refresh(int /*signal*/);
@ -22,18 +22,21 @@ class Custom : public ALabel {
void continuousWorker();
void parseOutputRaw();
void parseOutputJson();
bool handleScroll(GdkEventScroll* e);
bool handleToggle(GdkEventButton* const& e);
const std::string name_;
std::string text_;
std::string alt_;
std::string tooltip_;
std::vector<std::string> class_;
int percentage_;
waybar::util::SleeperThread thread_;
waybar::util::command::res output_;
waybar::util::JsonParser parser_;
FILE* fp_;
int pid_;
const std::string name_;
std::string text_;
std::string alt_;
std::string tooltip_;
std::vector<std::string> class_;
int percentage_;
FILE* fp_;
int pid_;
util::command::res output_;
util::JsonParser parser_;
util::SleeperThread thread_;
};
} // namespace waybar::modules

View File

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

View File

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

View File

@ -18,23 +18,31 @@ class Pulseaudio : public ALabel {
static void subscribeCb(pa_context*, pa_subscription_event_type_t, uint32_t, void*);
static void contextStateCb(pa_context*, 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 volumeModifyCb(pa_context*, int, void*);
bool handleScroll(GdkEventScroll* e);
bool handleVolume(GdkEventScroll* e);
const std::string getPortIcon() const;
pa_threaded_mainloop* mainloop_;
pa_mainloop_api* mainloop_api_;
pa_context* context_;
uint32_t sink_idx_{0};
uint16_t volume_;
pa_cvolume pa_volume_;
bool muted_;
std::string port_name_;
std::string desc_;
std::string monitor_;
bool scrolling_;
// SINK
uint32_t sink_idx_{0};
uint16_t volume_;
pa_cvolume pa_volume_;
bool muted_;
std::string port_name_;
std::string desc_;
std::string monitor_;
// 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

View File

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

View File

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

View File

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

View File

@ -6,12 +6,12 @@
#include "bar.hpp"
#include "client.hpp"
#include "modules/sway/ipc/client.hpp"
#include "util/sleeper_thread.hpp"
#include "util/json.hpp"
#include "util/sleeper_thread.hpp"
namespace waybar::modules::sway {
class Window : public ALabel {
class Window : public ALabel, public sigc::trackable {
public:
Window(const std::string&, const waybar::Bar&, const Json::Value&);
~Window() = default;
@ -24,13 +24,15 @@ class Window : public ALabel {
std::tuple<std::size_t, int, std::string, std::string> getFocusedNode(const Json::Value& nodes);
void getTree();
const Bar& bar_;
waybar::util::SleeperThread thread_;
Ipc ipc_;
std::string window_;
int windowId_;
std::string app_id_;
const Bar& bar_;
std::mutex mutex_;
std::string window_;
int windowId_;
std::string app_id_;
util::JsonParser parser_;
util::SleeperThread thread_;
Ipc ipc_;
};
} // namespace waybar::modules::sway

View File

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

View File

@ -17,8 +17,8 @@ class Temperature : public ALabel {
std::tuple<uint16_t, uint16_t> getTemperature();
bool isCritical(uint16_t);
std::string file_path_;
waybar::util::SleeperThread thread_;
std::string file_path_;
util::SleeperThread thread_;
};
} // namespace waybar::modules
} // namespace waybar::modules

View File

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

View File

@ -1,6 +1,6 @@
project(
'waybar', 'cpp', 'c',
version: '0.6.3',
version: '0.6.7',
license: 'MIT',
default_options : [
'cpp_std=c++17',
@ -48,10 +48,10 @@ add_global_link_arguments(cpp_link_args, language : 'cpp')
thread_dep = dependency('threads')
libinput = dependency('libinput')
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_cursor = dependency('wayland-cursor')
wayland_protos = dependency('wayland-protocols')
wlroots = dependency('wlroots', fallback: ['wlroots', 'wlroots'])
gtkmm = dependency('gtkmm-3.0')
dbusmenu_gtk = dependency('dbusmenu-gtk3-0.4', required: get_option('dbusmenu-gtk'))
giounix = dependency('gio-unix-2.0', required: get_option('dbusmenu-gtk'))
@ -125,10 +125,10 @@ executable(
src_files,
dependencies: [
thread_dep,
wlroots,
client_protos,
wayland_client,
fmt,
spdlog,
sigcpp,
jsoncpp,
libinput,

View File

@ -78,8 +78,9 @@
// "thermal-zone": 2,
// "hwmon-path": "/sys/class/hwmon/hwmon2/temp1_input",
"critical-threshold": 80,
// "format-critical": "{temperatureC}°C ",
"format": "{temperatureC}°C "
// "format-critical": "{temperatureC}°C {icon}",
"format": "{temperatureC}°C {icon}",
"format-icons": ["", "", ""]
},
"backlight": {
// "device": "acpi_video1",
@ -93,6 +94,9 @@
"critical": 15
},
"format": "{capacity}% {icon}",
"format-charging": "{capacity}% ",
"format-plugged": "{capacity}% ",
"format-alt": "{time} {icon}",
// "format-good": "", // An empty format will hide the module
// "format-full": "",
"format-icons": ["", "", "", "", ""]
@ -101,16 +105,20 @@
"bat": "BAT2"
},
"network": {
// "interface": "wlp2s0", // (Optional) To force the use of this interface
// "interface": "wlp2*", // (Optional) To force the use of this interface
"format-wifi": "{essid} ({signalStrength}%) ",
"format-ethernet": "{ifname}: {ipaddr}/{cidr} ",
"format-disconnected": "Disconnected ⚠"
"format-ethernet": "{ifname}: {ipaddr}/{cidr} ",
"format-linked": "{ifname} (No IP) ",
"format-disconnected": "Disconnected ⚠",
"format-alt": "{ifname}: {ipaddr}/{cidr}"
},
"pulseaudio": {
//"scroll-step": 1,
"format": "{volume}% {icon}",
"format-bluetooth": "{volume}% {icon}",
"format-muted": "",
// "scroll-step": 1, // %, can be a float
"format": "{volume}% {icon} {format_source}",
"format-bluetooth": "{volume}% {icon} {format_source}",
"format-muted": " {format_source}",
"format-source": "{volume}% ",
"format-source-muted": "",
"format-icons": {
"headphones": "",
"handsfree": "",
@ -118,7 +126,7 @@
"phone": "",
"portable": "",
"car": "",
"default": ["", ""]
"default": ["", "", ""]
},
"on-click": "pavucontrol"
},

View File

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

View File

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

View File

@ -2,7 +2,8 @@
#include <fmt/format.h>
#include <util/command.hpp>
waybar::ALabel::ALabel(const Json::Value& config, const std::string& format, uint16_t interval)
waybar::ALabel::ALabel(const Json::Value& config, const std::string& name, const std::string& id,
const std::string& format, uint16_t interval)
: config_(config),
format_(config_["format"].isString() ? config_["format"].asString() : format),
interval_(config_["interval"] == "once"
@ -10,6 +11,10 @@ waybar::ALabel::ALabel(const Json::Value& config, const std::string& format, uin
: std::chrono::seconds(
config_["interval"].isUInt() ? config_["interval"].asUInt() : interval)),
default_format_(format_) {
label_.set_name(name);
if (!id.empty()) {
label_.get_style_context()->add_class(id);
}
event_box_.add(label_);
if (config_["max-length"].isUInt()) {
label_.set_max_width_chars(config_["max-length"].asUInt());
@ -64,8 +69,7 @@ bool waybar::ALabel::handleToggle(GdkEventButton* const& e) {
format = config_["on-click-forward"].asString();
}
if (!format.empty()) {
pid_.push_back(
waybar::util::command::forkExec(fmt::format(format, fmt::arg("arg", click_param))));
pid_.push_back(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_;
@ -101,15 +105,15 @@ bool waybar::ALabel::handleScroll(GdkEventScroll* e) {
}
}
if (direction_up && config_["on-scroll-up"].isString()) {
pid_.push_back(waybar::util::command::forkExec(config_["on-scroll-up"].asString()));
pid_.push_back(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()));
pid_.push_back(util::command::forkExec(config_["on-scroll-down"].asString()));
}
dp.emit();
return true;
}
std::string waybar::ALabel::getIcon(uint16_t percentage, const std::string& alt) {
std::string waybar::ALabel::getIcon(uint16_t percentage, const std::string& alt, uint16_t max) {
auto format_icons = config_["format-icons"];
if (format_icons.isObject()) {
if (!alt.empty() && (format_icons[alt].isString() || format_icons[alt].isArray())) {
@ -120,7 +124,7 @@ std::string waybar::ALabel::getIcon(uint16_t percentage, const std::string& alt)
}
if (format_icons.isArray()) {
auto size = format_icons.size();
auto idx = std::clamp(percentage / (100 / size), 0U, size - 1);
auto idx = std::clamp(percentage / ((max == 0 ? 100 : max) / size), 0U, size - 1);
format_icons = format_icons[idx];
}
if (format_icons.isString()) {
@ -129,7 +133,10 @@ std::string waybar::ALabel::getIcon(uint16_t percentage, const std::string& alt)
return "";
}
std::string waybar::ALabel::getState(uint8_t value) {
std::string waybar::ALabel::getState(uint8_t value, bool lesser) {
if (!config_["states"].isObject()) {
return "";
}
// Get current state
std::vector<std::pair<std::string, uint8_t>> states;
if (config_["states"].isObject()) {
@ -140,10 +147,12 @@ std::string waybar::ALabel::getState(uint8_t value) {
}
}
// Sort states
std::sort(states.begin(), states.end(), [](auto& a, auto& b) { return a.second < b.second; });
std::sort(states.begin(), states.end(), [&lesser](auto& a, auto& b) {
return lesser ? a.second < b.second : a.second > b.second;
});
std::string valid_state;
for (auto const& state : states) {
if (value <= state.second && valid_state.empty()) {
if ((lesser ? value <= state.second : value >= state.second) && valid_state.empty()) {
label_.get_style_context()->add_class(state.first);
valid_state = state.first;
} else {

View File

@ -1,4 +1,5 @@
#include "bar.hpp"
#include <spdlog/spdlog.h>
#include "client.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},
surface(nullptr),
layer_surface(nullptr),
anchor_(ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP),
left_(Gtk::ORIENTATION_HORIZONTAL, 0),
center_(Gtk::ORIENTATION_HORIZONTAL, 0),
right_(Gtk::ORIENTATION_HORIZONTAL, 0),
@ -20,44 +22,27 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config)
height_ = 0;
width_ = 1;
}
height_ = config["height"].isUInt() ? config["height"].asUInt() : height_;
width_ = config["width"].isUInt() ? config["width"].asUInt() : width_;
window.signal_realize().connect_notify(sigc::mem_fun(*this, &Bar::onRealize));
window.signal_map_event().connect_notify(sigc::mem_fun(*this, &Bar::onMap));
window.signal_configure_event().connect_notify(sigc::mem_fun(*this, &Bar::onConfigure));
window.set_size_request(width_, height_);
auto gtk_window = window.gobj();
auto gtk_widget = GTK_WIDGET(gtk_window);
gtk_widget_realize(gtk_widget);
auto gdk_window = window.get_window()->gobj();
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_;
window.signal_configure_event().connect_notify(sigc::mem_fun(*this, &Bar::onConfigure));
std::size_t anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP;
if (config["position"] == "bottom") {
anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
anchor_ = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
} else if (config["position"] == "left") {
anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT;
anchor_ = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT;
} 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) {
anchor |= ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
} else if (anchor == ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT ||
anchor == ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT) {
anchor |= ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
if (anchor_ == ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM ||
anchor_ == ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP) {
anchor_ |= ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
} else if (anchor_ == ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT ||
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);
center_ = Gtk::Box(Gtk::ORIENTATION_VERTICAL, 0);
right_ = Gtk::Box(Gtk::ORIENTATION_VERTICAL, 0);
@ -65,14 +50,71 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config)
vertical = true;
}
zwlr_layer_surface_v1_set_anchor(layer_surface, anchor);
zwlr_layer_surface_v1_set_size(layer_surface, width, height);
setMarginsAndZone(height, width);
setupWidgets();
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_display_roundtrip(client->wl_display);
setupWidgets();
}
void waybar::Bar::setMarginsAndZone(uint32_t height, uint32_t width) {
@ -114,7 +156,7 @@ void waybar::Bar::setMarginsAndZone(uint32_t height, uint32_t width) {
.left = std::stoi(margins[3], nullptr, 10)};
}
} catch (...) {
std::cerr << "Invalid margins: " + config["margin"].asString() << std::endl;
spdlog::warn("Invalid margins: {}", config["margin"].asString());
}
} else if (config["margin"].isInt()) {
auto gaps = config["margin"].asInt();
@ -126,36 +168,6 @@ void waybar::Bar::setMarginsAndZone(uint32_t height, uint32_t width) {
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
void waybar::Bar::setupAltFormatKeyForModule(const std::string& module_name) {
if (config.isMember(module_name)) {
@ -227,11 +239,10 @@ void waybar::Bar::layerSurfaceHandleConfigure(void* data, struct zwlr_layer_surf
o->window.resize(o->width_, o->height_);
auto zone = o->vertical ? width + o->margins_.right : height + o->margins_.bottom;
zwlr_layer_surface_v1_set_exclusive_zone(o->layer_surface, zone);
std::cout << fmt::format(BAR_SIZE_MSG,
o->width_ == 1 ? "auto" : std::to_string(o->width_),
o->height_ == 1 ? "auto" : std::to_string(o->height_),
o->output->name)
<< std::endl;
spdlog::info(BAR_SIZE_MSG,
o->width_ == 1 ? "auto" : std::to_string(o->width_),
o->height_ == 1 ? "auto" : std::to_string(o->height_),
o->output->name);
wl_surface_commit(o->surface);
}
zwlr_layer_surface_v1_ack_configure(surface, serial);
@ -239,7 +250,10 @@ void waybar::Bar::layerSurfaceHandleConfigure(void* data, struct zwlr_layer_surf
void waybar::Bar::layerSurfaceHandleClosed(void* data, struct zwlr_layer_surface_v1* /*surface*/) {
auto o = static_cast<waybar::Bar*>(data);
zwlr_layer_surface_v1_destroy(o->layer_surface);
if (o->layer_surface) {
zwlr_layer_surface_v1_destroy(o->layer_surface);
o->layer_surface = nullptr;
}
o->modules_left_.clear();
o->modules_center_.clear();
o->modules_right_.clear();
@ -272,14 +286,17 @@ void waybar::Bar::getModules(const Factory& factory, const std::string& pos) {
modules_right_.emplace_back(module);
}
module->dp.connect([module, &name] {
try {
module->update();
} catch (const std::exception& e) {
std::cerr << name.asString() + ": " + e.what() << std::endl;
}
// Fix https://github.com/Alexays/Waybar/issues/320, proper way?
Glib::signal_idle().connect_once([module, &name] {
try {
module->update();
} catch (const std::exception& e) {
spdlog::error("{}: {}", name.asString(), e.what());
}
});
});
} catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
spdlog::warn("module {}: {}", name.asString(), e.what());
}
}
}
@ -310,5 +327,4 @@ auto waybar::Bar::setupWidgets() -> void {
for (auto const& module : modules_right_) {
right_.pack_end(*module, false, false, 0);
}
window.show_all();
}

View File

@ -1,4 +1,5 @@
#include "client.hpp"
#include <spdlog/spdlog.h>
#include <fstream>
#include <iostream>
#include "util/clara.hpp"
@ -9,7 +10,7 @@ waybar::Client *waybar::Client::inst() {
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;
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;
(*it)->window.close();
it = client->bars.erase(it);
std::cout << "Bar removed from output: " + output_name << std::endl;
spdlog::info("Bar removed from output: {}", output_name);
} else {
++it;
}
@ -153,6 +154,7 @@ void waybar::Client::handleName(void * data, struct zxdg_output_v1 * /*xdg_
wl_output_destroy(output->output);
zxdg_output_v1_destroy(output->xdg_output);
} else {
wl_display_roundtrip(client->wl_display);
for (const auto &config : configs) {
client->bars.emplace_back(std::make_unique<Bar>(output.get(), config));
Glib::RefPtr<Gdk::Screen> screen = client->bars.back()->window.get_screen();
@ -170,31 +172,33 @@ void waybar::Client::handleDescription(void * /*data*/, struct zxdg_output_v1 *
// Nothing here
}
void waybar::Client::setupConfigs(const std::string &config, const std::string &style) {
config_file_ = config.empty() ? getValidPath({
"$XDG_CONFIG_HOME/waybar/config",
"$HOME/.config/waybar/config",
"$HOME/waybar/config",
"/etc/xdg/waybar/config",
"./resources/config",
std::tuple<const std::string, const std::string> waybar::Client::getConfigs(
const std::string &config, const std::string &style) const {
auto config_file = config.empty() ? getValidPath({
"$XDG_CONFIG_HOME/waybar/config",
"$HOME/.config/waybar/config",
"$HOME/waybar/config",
"/etc/xdg/waybar/config",
"./resources/config",
})
: config;
auto css_file = style.empty() ? getValidPath({
"$XDG_CONFIG_HOME/waybar/style.css",
"$HOME/.config/waybar/style.css",
"$HOME/waybar/style.css",
"/etc/xdg/waybar/style.css",
"./resources/style.css",
})
: config;
css_file_ = style.empty() ? getValidPath({
"$XDG_CONFIG_HOME/waybar/style.css",
"$HOME/.config/waybar/style.css",
"$HOME/waybar/style.css",
"/etc/xdg/waybar/style.css",
"./resources/style.css",
})
: style;
if (css_file_.empty() || config_file_.empty()) {
: style;
if (css_file.empty() || config_file.empty()) {
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 {
std::ifstream file(config_file_);
auto waybar::Client::setupConfig(const std::string &config_file) -> void {
std::ifstream file(config_file);
if (!file.is_open()) {
throw std::runtime_error("Can't open config file");
}
@ -203,12 +207,12 @@ auto waybar::Client::setupConfig() -> void {
config_ = parser.parse(str);
}
auto waybar::Client::setupCss() -> void {
auto waybar::Client::setupCss(const std::string &css_file) -> void {
css_provider_ = Gtk::CssProvider::create();
style_context_ = Gtk::StyleContext::create();
// 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");
}
}
@ -227,28 +231,23 @@ void waybar::Client::bindInterfaces() {
}
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_version = false;
std::string config;
std::string style;
std::string bar_id;
std::string log_level;
auto cli = clara::detail::Help(show_help) |
clara::detail::Opt(show_version)["-v"]["--version"]("Show version") |
clara::detail::Opt(config, "config")["-c"]["--config"]("Config 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");
auto res = cli.parse(clara::detail::Args(argc, argv));
if (!res) {
std::cerr << "Error in command line: " << res.errorMessage() << std::endl;
spdlog::error("Error in command line: {}", res.errorMessage());
return 1;
}
if (show_help) {
@ -259,9 +258,21 @@ int waybar::Client::main(int argc, char *argv[]) {
std::cout << "Waybar v" << VERSION << std::endl;
return 0;
}
setupConfigs(config, style);
setupConfig();
setupCss();
if (!log_level.empty()) {
spdlog::set_level(spdlog::level::from_str(log_level));
}
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();
gtk_app->hold();
gtk_app->run();

View File

@ -64,7 +64,7 @@ waybar::IModule* waybar::Factory::makeModule(const std::string& name) const {
return new waybar::modules::Temperature(id, config_[name]);
}
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) {
auto err = fmt::format("Disabling module \"{}\", {}", name, e.what());

View File

@ -1,5 +1,5 @@
#include <csignal>
#include <iostream>
#include <spdlog/spdlog.h>
#include "client.hpp"
int main(int argc, char* argv[]) {
@ -23,10 +23,10 @@ int main(int argc, char* argv[]) {
delete client;
return ret;
} catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
spdlog::error("{}", e.what());
return 1;
} catch (const Glib::Exception& e) {
std::cerr << e.what().c_str() << std::endl;
spdlog::error("{}", static_cast<std::string>(e.what()));
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; }
waybar::modules::Backlight::Backlight(const std::string &name, const Json::Value &config)
: ALabel(config, "{percent}%", 2),
name_(name),
waybar::modules::Backlight::Backlight(const std::string &id, const Json::Value &config)
: ALabel(config, "backlight", id, "{percent}%", 2),
preferred_device_(config["device"].isString() ? config["device"].asString() : "") {
label_.set_name("backlight");
// Get initial state
{
std::unique_ptr<udev, UdevDeleter> udev_check{udev_new()};
@ -181,7 +178,7 @@ auto waybar::modules::Backlight::update() -> void {
const auto percent = best->get_max() == 0 ? 100 : best->get_actual() * 100 / best->get_max();
label_.set_markup(fmt::format(
format_, fmt::arg("percent", std::to_string(percent)), fmt::arg("icon", getIcon(percent))));
getState(percent);
getState(percent);
} else {
if (!previous_best_.has_value()) {
return;
@ -213,7 +210,10 @@ void waybar::modules::Backlight::upsert_device(ForwardIt first, ForwardIt last,
const char *name = udev_device_get_sysname(dev);
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);
const int actual_int = std::stoi(actual);

View File

@ -1,11 +1,8 @@
#include "modules/battery.hpp"
#include <spdlog/spdlog.h>
waybar::modules::Battery::Battery(const std::string& id, const Json::Value& config)
: ALabel(config, "{capacity}%", 60) {
label_.set_name("battery");
if (!id.empty()) {
label_.get_style_context()->add_class(id);
}
: ALabel(config, "battery", id, "{capacity}%", 60) {
getBatteries();
fd_ = inotify_init1(IN_CLOEXEC);
if (fd_ == -1) {
@ -75,25 +72,53 @@ void waybar::modules::Battery::getBatteries() {
}
}
const std::tuple<uint8_t, std::string> waybar::modules::Battery::getInfos() const {
const std::tuple<uint8_t, float, std::string> waybar::modules::Battery::getInfos() const {
try {
uint16_t total = 0;
uint32_t total_power = 0; // μW
uint32_t total_energy = 0; // μWh
uint32_t total_energy_full = 0;
std::string status = "Unknown";
for (auto const& bat : batteries_) {
uint16_t capacity;
uint32_t power_now;
uint32_t energy_full;
uint32_t energy_now;
std::string _status;
std::ifstream(bat / "capacity") >> capacity;
std::ifstream(bat / "status") >> _status;
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") {
status = _status;
}
total += capacity;
total_power += power_now;
total_energy += energy_now;
total_energy_full += energy_full;
}
if (!adapter_.empty() && status == "Discharging") {
bool online;
std::ifstream(adapter_ / "online") >> online;
if (online) {
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();
return {capacity, status};
return {capacity, time_remaining, status};
} catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
return {0, "Unknown"};
spdlog::error("Battery: {}", e.what());
return {0, 0, "Unknown"};
}
}
@ -104,23 +129,42 @@ const std::string waybar::modules::Battery::getAdapterStatus(uint8_t capacity) c
if (capacity == 100) {
return "Full";
}
return online ? "Charging" : "Discharging";
if (online) {
return "Charging";
}
return "Discharging";
}
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 [capacity, status] = getInfos();
auto [capacity, time_remaining, status] = getInfos();
if (status == "Unknown") {
status = getAdapterStatus(capacity);
}
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);
auto format = format_;
auto state = getState(capacity);
label_.get_style_context()->remove_class(old_status_);
auto state = getState(capacity, true);
if (!old_status_.empty()) {
label_.get_style_context()->remove_class(old_status_);
}
label_.get_style_context()->add_class(status);
old_status_ = status;
if (!state.empty() && config_["format-" + status + "-" + state].isString()) {
@ -134,7 +178,9 @@ auto waybar::modules::Battery::update() -> void {
event_box_.hide();
} else {
event_box_.show();
label_.set_markup(
fmt::format(format, fmt::arg("capacity", capacity), fmt::arg("icon", getIcon(capacity))));
label_.set_markup(fmt::format(format,
fmt::arg("capacity", capacity),
fmt::arg("icon", getIcon(capacity, state)),
fmt::arg("time", formatTimeRemaining(time_remaining))));
}
}

View File

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

View File

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

View File

@ -1,8 +1,9 @@
#include "modules/custom.hpp"
#include <spdlog/spdlog.h>
waybar::modules::Custom::Custom(const std::string& name, const Json::Value& config)
: ALabel(config, "{}"), name_(name), fp_(nullptr), pid_(-1) {
label_.set_name("custom-" + name_);
waybar::modules::Custom::Custom(const std::string& name, const std::string& id,
const Json::Value& config)
: ALabel(config, "custom-" + name, id, "{}"), name_(name), fp_(nullptr), pid_(-1) {
if (config_["exec"].isString()) {
if (interval_.count() > 0) {
delayWorker();
@ -24,14 +25,14 @@ void waybar::modules::Custom::delayWorker() {
thread_ = [this] {
bool can_update = true;
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) {
can_update = false;
event_box_.hide();
}
}
if (can_update) {
output_ = waybar::util::command::exec(config_["exec"].asString());
output_ = util::command::exec(config_["exec"].asString());
dp.emit();
}
thread_.sleep_for(interval_);
@ -58,7 +59,7 @@ void waybar::modules::Custom::continuousWorker() {
if (exit_code != 0) {
output_ = {exit_code, ""};
dp.emit();
std::cerr << name_ + " just stopped unexpectedly, is it endless?" << std::endl;
spdlog::error("{} stopped unexpectedly, is it endless?", name_);
}
return;
}
@ -73,12 +74,24 @@ 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()) {
thread_.wake_up();
}
}
bool waybar::modules::Custom::handleScroll(GdkEventScroll* e) {
auto ret = ALabel::handleScroll(e);
thread_.wake_up();
return ret;
}
bool waybar::modules::Custom::handleToggle(GdkEventButton* const& e) {
auto ret = ALabel::handleToggle(e);
thread_.wake_up();
return ret;
}
auto waybar::modules::Custom::update() -> void {
// Hide label if output is empty
if (config_["exec"].isString() && (output_.out.empty() || output_.exit_code != 0)) {
@ -89,29 +102,31 @@ auto waybar::modules::Custom::update() -> void {
} else {
parseOutputRaw();
}
auto str = fmt::format(format_,
text_,
fmt::arg("alt", alt_),
fmt::arg("icon", getIcon(percentage_, alt_)),
fmt::arg("percentage", percentage_));
label_.set_markup(str);
if (tooltipEnabled()) {
if (text_ == tooltip_) {
label_.set_tooltip_text(str);
} else {
label_.set_tooltip_text(tooltip_);
if (text_.empty()) {
event_box_.hide();
} else {
auto str = fmt::format(format_,
text_,
fmt::arg("alt", alt_),
fmt::arg("icon", getIcon(percentage_, alt_)),
fmt::arg("percentage", percentage_));
label_.set_markup(str);
if (tooltipEnabled()) {
if (text_ == tooltip_) {
label_.set_tooltip_text(str);
} else {
label_.set_tooltip_text(tooltip_);
}
}
auto classes = label_.get_style_context()->list_classes();
for (auto const& c : classes) {
label_.get_style_context()->remove_class(c);
}
for (auto const& c : class_) {
label_.get_style_context()->add_class(c);
}
event_box_.show();
}
auto classes = label_.get_style_context()->list_classes();
for (auto const& c : classes) {
label_.get_style_context()->remove_class(c);
}
for (auto const& c : class_) {
label_.get_style_context()->add_class(c);
}
event_box_.show();
}
}

View File

@ -3,15 +3,11 @@
waybar::modules::IdleInhibitor::IdleInhibitor(const std::string& id, const Bar& bar,
const Json::Value& config)
: ALabel(config, "{status}"),
: ALabel(config, "idle_inhibitor", id, "{status}"),
bar_(bar),
status_("deactivated"),
idle_inhibitor_(nullptr),
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_.signal_button_press_event().connect(
sigc::mem_fun(*this, &IdleInhibitor::handleToggle));

View File

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

View File

@ -1,10 +1,10 @@
#include "modules/mpd.hpp"
#include <fmt/chrono.h>
#include <iostream>
#include <spdlog/spdlog.h>
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),
server_(nullptr),
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),
song_(nullptr, &mpd_song_free) {
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()) {
std::cerr << module_name_ << ": `timeout` configuration should be an unsigned int" << std::endl;
}
label_.set_name("mpd");
if (!id.empty()) {
label_.get_style_context()->add_class(id);
spdlog::warn("{}: `timeout` configuration should be an unsigned int", module_name_);
}
if (!config["server"].isNull()) {
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();
}
@ -51,7 +46,7 @@ auto waybar::modules::MPD::update() -> void {
periodic_updater().detach();
}
} catch (const std::exception& e) {
std::cerr << module_name_ + ": " + e.what() << std::endl;
spdlog::error("{}: {}", module_name_, e.what());
state_ = MPD_STATE_UNKNOWN;
}
}
@ -65,14 +60,14 @@ std::thread waybar::modules::MPD::event_listener() {
try {
if (connection_ == nullptr) {
// Retry periodically if no connection
update();
dp.emit();
std::this_thread::sleep_for(interval_);
} else {
waitForEvent();
dp.emit();
}
} 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) {
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 "";
}
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 "";
}
@ -228,7 +223,7 @@ std::string waybar::modules::MPD::getOptionIcon(std::string optionName, bool act
}
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 "";
}
@ -251,7 +246,7 @@ void waybar::modules::MPD::tryConnect() {
unique_connection(mpd_connection_new(server_, port_, timeout_), &mpd_connection_free);
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();
alternate_connection_.reset();
return;
@ -259,9 +254,9 @@ void waybar::modules::MPD::tryConnect() {
try {
checkErrors(connection_.get());
std::cerr << module_name_ << ": Connected to MPD" << std::endl;
spdlog::info("{}: Connected to MPD", module_name_);
} 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();
alternate_connection_.reset();
}

View File

@ -1,33 +1,110 @@
#include "modules/network.hpp"
#include <spdlog/spdlog.h>
#include <sys/eventfd.h>
#include <fstream>
namespace {
constexpr const char *NETSTAT_FILE =
"/proc/net/netstat"; // std::ifstream does not take std::string_view as param
constexpr std::string_view BANDWIDTH_CATEGORY = "IpExt";
constexpr std::string_view BANDWIDTH_DOWN_TOTAL_KEY = "InOctets";
constexpr std::string_view BANDWIDTH_UP_TOTAL_KEY = "OutOctets";
std::ifstream netstat(NETSTAT_FILE);
std::optional<unsigned long long> read_netstat(std::string_view category, std::string_view key) {
if (!netstat) {
spdlog::warn("Failed to open netstat file {}", NETSTAT_FILE);
return {};
}
netstat.seekg(std::ios_base::beg);
// finding corresponding line (category)
// looks into the file for the first line starting by the 'category' string
auto starts_with = [](const std::string &str, std::string_view start) {
return start == std::string_view{str.data(), std::min(str.size(), start.size())};
};
std::string read;
while (std::getline(netstat, read) && !starts_with(read, category))
;
if (!starts_with(read, category)) {
spdlog::warn("Category '{}' not found in netstat file {}", category, NETSTAT_FILE);
return {};
}
// finding corresponding column (key)
// looks into the fetched line for the first word (space separated) equal to 'key'
int index = 0;
auto r_it = read.begin();
auto k_it = key.begin();
while (k_it != key.end() && r_it != read.end()) {
if (*r_it != *k_it) {
r_it = std::find(r_it, read.end(), ' ');
if (r_it != read.end()) {
++r_it;
}
k_it = key.begin();
++index;
} else {
++r_it;
++k_it;
}
}
if (r_it == read.end() && k_it != key.end()) {
spdlog::warn(
"Key '{}' not found in category '{}' of netstat file {}", key, category, NETSTAT_FILE);
return {};
}
// finally accessing value
// accesses the line right under the fetched one
std::getline(netstat, read);
assert(starts_with(read, category));
std::istringstream iss(read);
while (index--) {
std::getline(iss, read, ' ');
}
unsigned long long value;
iss >> value;
return value;
}
} // namespace
waybar::modules::Network::Network(const std::string &id, const Json::Value &config)
: ALabel(config, "{ifname}", 60),
family_(AF_INET),
: ALabel(config, "network", id, "{ifname}", 60),
ifid_(-1),
family_(config["family"] == "ipv6" ? AF_INET6 : AF_INET),
efd_(-1),
ev_fd_(-1),
cidr_(-1),
signal_strength_dbm_(0),
signal_strength_(0) {
label_.set_name("network");
if (!id.empty()) {
label_.get_style_context()->add_class(id);
}
createInfoSocket();
createEventSocket();
if (config_["interface"].isString()) {
ifid_ = if_nametoindex(config_["interface"].asCString());
ifname_ = config_["interface"].asString();
if (ifid_ <= 0) {
throw std::runtime_error("Can't found network interface");
}
signal_strength_(0),
frequency_(0) {
auto down_octets = read_netstat(BANDWIDTH_CATEGORY, BANDWIDTH_DOWN_TOTAL_KEY);
auto up_octets = read_netstat(BANDWIDTH_CATEGORY, BANDWIDTH_UP_TOTAL_KEY);
if (down_octets) {
bandwidth_down_total_ = *down_octets;
} else {
ifid_ = getExternalInterface();
if (ifid_ > 0) {
char ifname[IF_NAMESIZE];
if_indextoname(ifid_, ifname);
ifname_ = ifname;
}
bandwidth_down_total_ = 0;
}
if (up_octets) {
bandwidth_up_total_ = *up_octets;
} else {
bandwidth_up_total_ = 0;
}
createEventSocket();
createInfoSocket();
auto default_iface = getPreferredIface();
if (default_iface != -1) {
ifid_ = default_iface;
char ifname[IF_NAMESIZE];
if_indextoname(default_iface, ifname);
ifname_ = ifname;
getInterfaceAddress();
}
dp.emit();
worker();
@ -42,39 +119,45 @@ waybar::modules::Network::~Network() {
if (efd_ > -1) {
close(efd_);
}
if (info_sock_ != nullptr) {
nl_socket_drop_membership(info_sock_, RTMGRP_LINK);
nl_socket_drop_membership(info_sock_, RTMGRP_IPV4_IFADDR);
nl_close(info_sock_);
nl_socket_free(info_sock_);
if (ev_sock_ != nullptr) {
nl_socket_drop_membership(ev_sock_, RTNLGRP_LINK);
if (family_ == AF_INET) {
nl_socket_drop_membership(ev_sock_, RTNLGRP_IPV4_IFADDR);
} else {
nl_socket_drop_membership(ev_sock_, RTNLGRP_IPV6_IFADDR);
}
nl_close(ev_sock_);
nl_socket_free(ev_sock_);
}
if (sk_ != nullptr) {
nl_close(sk_);
nl_socket_free(sk_);
if (sock_ != nullptr) {
nl_close(sock_);
nl_socket_free(sock_);
}
}
void waybar::modules::Network::createInfoSocket() {
info_sock_ = nl_socket_alloc();
if (nl_connect(info_sock_, NETLINK_ROUTE) != 0) {
void waybar::modules::Network::createEventSocket() {
ev_sock_ = nl_socket_alloc();
nl_socket_disable_seq_check(ev_sock_);
nl_socket_modify_cb(ev_sock_, NL_CB_VALID, NL_CB_CUSTOM, handleEvents, this);
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) {
throw std::runtime_error("Can't connect network socket");
}
if (nl_socket_add_membership(info_sock_, RTMGRP_LINK) != 0) {
throw std::runtime_error("Can't add membership");
nl_socket_add_membership(ev_sock_, RTNLGRP_LINK);
if (family_ == AF_INET) {
nl_socket_add_membership(ev_sock_, RTNLGRP_IPV4_IFADDR);
} else {
nl_socket_add_membership(ev_sock_, RTNLGRP_IPV6_IFADDR);
}
if (nl_socket_add_membership(info_sock_, RTMGRP_IPV4_IFADDR) != 0) {
throw std::runtime_error("Can't add membership");
}
nl_socket_disable_seq_check(info_sock_);
nl_socket_set_nonblocking(info_sock_);
nl_socket_modify_cb(info_sock_, NL_CB_VALID, NL_CB_CUSTOM, handleEvents, this);
efd_ = epoll_create1(EPOLL_CLOEXEC);
if (efd_ < 0) {
throw std::runtime_error("Can't create epoll");
}
{
ev_fd_ = eventfd(0, EFD_NONBLOCK);
struct epoll_event event = {0};
struct epoll_event event;
memset(&event, 0, sizeof(event));
event.events = EPOLLIN | EPOLLET;
event.data.fd = ev_fd_;
if (epoll_ctl(efd_, EPOLL_CTL_ADD, ev_fd_, &event) == -1) {
@ -82,8 +165,9 @@ void waybar::modules::Network::createInfoSocket() {
}
}
{
auto fd = nl_socket_get_fd(info_sock_);
struct epoll_event event = {0};
auto fd = nl_socket_get_fd(ev_sock_);
struct epoll_event event;
memset(&event, 0, sizeof(event));
event.events = EPOLLIN | EPOLLET | EPOLLRDHUP;
event.data.fd = fd;
if (epoll_ctl(efd_, EPOLL_CTL_ADD, fd, &event) == -1) {
@ -92,15 +176,15 @@ void waybar::modules::Network::createInfoSocket() {
}
}
void waybar::modules::Network::createEventSocket() {
sk_ = nl_socket_alloc();
if (genl_connect(sk_) != 0) {
void waybar::modules::Network::createInfoSocket() {
sock_ = nl_socket_alloc();
if (genl_connect(sock_) != 0) {
throw std::runtime_error("Can't connect to netlink socket");
}
if (nl_socket_modify_cb(sk_, NL_CB_VALID, NL_CB_CUSTOM, handleScan, this) < 0) {
if (nl_socket_modify_cb(sock_, NL_CB_VALID, NL_CB_CUSTOM, handleScan, this) < 0) {
throw std::runtime_error("Can't set callback");
}
nl80211_id_ = genl_ctrl_resolve(sk_, "nl80211");
nl80211_id_ = genl_ctrl_resolve(sock_, "nl80211");
if (nl80211_id_ < 0) {
throw std::runtime_error("Can't resolve nl80211 interface");
}
@ -108,9 +192,12 @@ void waybar::modules::Network::createEventSocket() {
void waybar::modules::Network::worker() {
thread_timer_ = [this] {
if (ifid_ > 0) {
getInfo();
dp.emit();
{
std::lock_guard<std::mutex> lock(mutex_);
if (ifid_ > 0) {
getInfo();
dp.emit();
}
}
thread_timer_.sleep_for(interval_);
};
@ -119,103 +206,128 @@ void waybar::modules::Network::worker() {
int ec = epoll_wait(efd_, events.data(), EPOLL_MAX, -1);
if (ec > 0) {
for (auto i = 0; i < ec; i++) {
if (events[i].data.fd == nl_socket_get_fd(info_sock_)) {
nl_recvmsgs_default(info_sock_);
} else {
if (events[i].data.fd != nl_socket_get_fd(ev_sock_) || nl_recvmsgs_default(ev_sock_) < 0) {
thread_.stop();
break;
}
}
} else if (ec == -1) {
thread_.stop();
}
};
}
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 {
std::string connectiontype;
std::string tooltip_format = "";
if (config_["tooltip-format"].isString()) {
tooltip_format = config_["tooltip-format"].asString();
std::lock_guard<std::mutex> lock(mutex_);
std::string tooltip_format;
auto down_octets = read_netstat(BANDWIDTH_CATEGORY, BANDWIDTH_DOWN_TOTAL_KEY);
auto up_octets = read_netstat(BANDWIDTH_CATEGORY, BANDWIDTH_UP_TOTAL_KEY);
unsigned long long bandwidth_down = 0;
if (down_octets) {
bandwidth_down = *down_octets - bandwidth_down_total_;
bandwidth_down_total_ = *down_octets;
}
if (ifid_ <= 0 || ipaddr_.empty()) {
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 (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");
unsigned long long bandwidth_up = 0;
if (up_octets) {
bandwidth_up = *up_octets - bandwidth_up_total_;
bandwidth_up_total_ = *up_octets;
}
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_;
state_ = state;
}
getState(signal_strength_);
auto text = fmt::format(format_,
fmt::arg("essid", essid_),
fmt::arg("signaldBm", signal_strength_dbm_),
fmt::arg("signalStrength", signal_strength_),
fmt::arg("ifname", ifname_),
fmt::arg("netmask", netmask_),
fmt::arg("ipaddr", ipaddr_),
fmt::arg("cidr", cidr_),
fmt::arg("icon", getIcon(signal_strength_, connectiontype)));
label_.set_markup(text);
if (tooltipEnabled()) {
if (!tooltip_format.empty()) {
auto tooltip_text = fmt::format(tooltip_format,
fmt::arg("essid", essid_),
fmt::arg("signaldBm", signal_strength_dbm_),
fmt::arg("signalStrength", signal_strength_),
fmt::arg("ifname", ifname_),
fmt::arg("netmask", netmask_),
fmt::arg("ipaddr", ipaddr_),
fmt::arg("cidr", cidr_),
fmt::arg("icon", getIcon(signal_strength_, connectiontype)));
label_.set_tooltip_text(tooltip_text);
auto pow_format = [](unsigned long long value, const std::string &unit) {
if (value > 2000ull * 1000ull * 1000ull) { // > 2G
auto go = value / (1000 * 1000 * 1000);
return std::to_string(go) + "." +
std::to_string((value - go * 1000 * 1000 * 1000) / (100 * 1000 * 1000)) + "G" + unit;
} else if (value > 2000ull * 1000ull) { // > 2M
auto mo = value / (1000 * 1000);
return std::to_string(mo) + "." + std::to_string((value - mo * 1000 * 1000) / (100 * 1000)) +
"M" + unit;
} else if (value > 2000ull) { // > 2k
auto ko = value / 1000;
return std::to_string(ko) + "." + std::to_string((value - ko * 1000) / 100) + "k" + unit;
} else {
return std::to_string(value) + unit;
}
};
auto text = fmt::format(
format_,
fmt::arg("essid", essid_),
fmt::arg("signaldBm", signal_strength_dbm_),
fmt::arg("signalStrength", signal_strength_),
fmt::arg("ifname", ifname_),
fmt::arg("netmask", netmask_),
fmt::arg("ipaddr", ipaddr_),
fmt::arg("cidr", cidr_),
fmt::arg("frequency", frequency_),
fmt::arg("icon", getIcon(signal_strength_, state_)),
fmt::arg("bandwidthDownBits", pow_format(bandwidth_down * 8ull / interval_.count(), "b/s")),
fmt::arg("bandwidthUpBits", pow_format(bandwidth_up * 8ull / interval_.count(), "b/s")),
fmt::arg("bandwidthDownOctets", pow_format(bandwidth_down / interval_.count(), "o/s")),
fmt::arg("bandwidthUpOctets", pow_format(bandwidth_up / interval_.count(), "o/s")));
if (text != label_.get_label()) {
label_.set_markup(text);
}
if (tooltipEnabled()) {
if (tooltip_format.empty() && config_["tooltip-format"].isString()) {
tooltip_format = config_["tooltip-format"].asString();
}
if (!tooltip_format.empty()) {
auto tooltip_text = fmt::format(
tooltip_format,
fmt::arg("essid", essid_),
fmt::arg("signaldBm", signal_strength_dbm_),
fmt::arg("signalStrength", signal_strength_),
fmt::arg("ifname", ifname_),
fmt::arg("netmask", netmask_),
fmt::arg("ipaddr", ipaddr_),
fmt::arg("cidr", cidr_),
fmt::arg("frequency", frequency_),
fmt::arg("icon", getIcon(signal_strength_, state_)),
fmt::arg("bandwidthDownBits",
pow_format(bandwidth_down * 8ull / interval_.count(), "b/s")),
fmt::arg("bandwidthUpBits", pow_format(bandwidth_up * 8ull / interval_.count(), "b/s")),
fmt::arg("bandwidthDownOctets", pow_format(bandwidth_down / interval_.count(), "o/s")),
fmt::arg("bandwidthUpOctets", pow_format(bandwidth_up / interval_.count(), "o/s")));
if (label_.get_tooltip_text() != text) {
label_.set_tooltip_text(tooltip_text);
}
} else if (label_.get_tooltip_text() != text) {
label_.set_tooltip_text(text);
}
}
}
void waybar::modules::Network::disconnected() {
essid_.clear();
signal_strength_dbm_ = 0;
signal_strength_ = 0;
ipaddr_.clear();
netmask_.clear();
cidr_ = 0;
if (!config_["interface"].isString()) {
ifname_.clear();
ifid_ = -1;
}
// Need to wait otherwise we'll have the same information
thread_.sleep_for(std::chrono::seconds(1));
}
// Based on https://gist.github.com/Yawning/c70d804d4b8ae78cc698
int waybar::modules::Network::getExternalInterface() {
int waybar::modules::Network::getExternalInterface(int skip_idx) const {
static const uint32_t route_buffer_size = 8192;
struct nlmsghdr * hdr = nullptr;
struct rtmsg * rt = nullptr;
@ -325,7 +437,7 @@ int waybar::modules::Network::getExternalInterface() {
/* If this is the default route, and we know the interface index,
* we can stop parsing this message.
*/
if (has_gateway && !has_destination && temp_idx != -1) {
if (has_gateway && !has_destination && temp_idx != -1 && temp_idx != skip_idx) {
ifidx = temp_idx;
break;
}
@ -339,96 +451,206 @@ out:
void waybar::modules::Network::getInterfaceAddress() {
unsigned int cidrRaw;
struct ifaddrs *ifaddr, *ifa;
ipaddr_.clear();
netmask_.clear();
cidr_ = 0;
int success = getifaddrs(&ifaddr);
if (success == 0) {
ifa = ifaddr;
while (ifa != nullptr && ipaddr_.empty() && netmask_.empty()) {
if (ifa->ifa_addr != nullptr && ifa->ifa_addr->sa_family == family_) {
if (strcmp(ifa->ifa_name, ifname_.c_str()) == 0) {
ipaddr_ = inet_ntoa(((struct sockaddr_in *)ifa->ifa_addr)->sin_addr);
netmask_ = inet_ntoa(((struct sockaddr_in *)ifa->ifa_netmask)->sin_addr);
cidrRaw = ((struct sockaddr_in *)(ifa->ifa_netmask))->sin_addr.s_addr;
unsigned int cidr = 0;
while (cidrRaw) {
cidr += cidrRaw & 1;
cidrRaw >>= 1;
}
cidr_ = cidr;
}
}
ifa = ifa->ifa_next;
}
freeifaddrs(ifaddr);
if (success != 0) {
return;
}
ifa = ifaddr;
while (ifa != nullptr) {
if (ifa->ifa_addr != nullptr && ifa->ifa_addr->sa_family == family_ &&
ifa->ifa_name == ifname_) {
char ipaddr[INET6_ADDRSTRLEN];
ipaddr_ = inet_ntop(family_,
&reinterpret_cast<struct sockaddr_in *>(ifa->ifa_addr)->sin_addr,
ipaddr,
INET6_ADDRSTRLEN);
char netmask[INET6_ADDRSTRLEN];
auto net_addr = reinterpret_cast<struct sockaddr_in *>(ifa->ifa_netmask);
netmask_ = inet_ntop(family_, &net_addr->sin_addr, netmask, INET6_ADDRSTRLEN);
cidrRaw = net_addr->sin_addr.s_addr;
unsigned int cidr = 0;
while (cidrRaw) {
cidr += cidrRaw & 1;
cidrRaw >>= 1;
}
cidr_ = cidr;
break;
}
ifa = ifa->ifa_next;
}
freeifaddrs(ifaddr);
}
int waybar::modules::Network::netlinkRequest(void *req, uint32_t reqlen, uint32_t groups) {
int waybar::modules::Network::netlinkRequest(void *req, uint32_t reqlen, uint32_t groups) const {
struct sockaddr_nl sa = {};
sa.nl_family = AF_NETLINK;
sa.nl_groups = groups;
struct iovec iov = {req, reqlen};
struct msghdr msg = {&sa, sizeof(sa), &iov, 1, nullptr, 0, 0};
return sendmsg(nl_socket_get_fd(info_sock_), &msg, 0);
struct msghdr msg = {
.msg_name = &sa,
.msg_namelen = sizeof(sa),
.msg_iov = &iov,
.msg_iovlen = 1,
};
return sendmsg(nl_socket_get_fd(ev_sock_), &msg, 0);
}
int waybar::modules::Network::netlinkResponse(void *resp, uint32_t resplen, uint32_t groups) {
int waybar::modules::Network::netlinkResponse(void *resp, uint32_t resplen, uint32_t groups) const {
struct sockaddr_nl sa = {};
sa.nl_family = AF_NETLINK;
sa.nl_groups = groups;
struct iovec iov = {resp, resplen};
struct msghdr msg = {&sa, sizeof(sa), &iov, 1, nullptr, 0, 0};
auto ret = recvmsg(nl_socket_get_fd(info_sock_), &msg, 0);
struct msghdr msg = {
.msg_name = &sa,
.msg_namelen = sizeof(sa),
.msg_iov = &iov,
.msg_iovlen = 1,
};
auto ret = recvmsg(nl_socket_get_fd(ev_sock_), &msg, 0);
if (msg.msg_flags & MSG_TRUNC) {
return -1;
}
return ret;
}
int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) {
int ret = 0;
auto net = static_cast<waybar::modules::Network *>(data);
bool need_update = false;
for (nlmsghdr *nh = nlmsg_hdr(msg); NLMSG_OK(nh, ret); nh = NLMSG_NEXT(nh, ret)) {
if (nh->nlmsg_type == RTM_NEWADDR) {
need_update = true;
bool waybar::modules::Network::checkInterface(struct ifinfomsg *rtif, std::string name) {
if (config_["interface"].isString()) {
return config_["interface"].asString() == name ||
wildcardMatch(config_["interface"].asString(), name);
}
// getExternalInterface may need some delay to detect external interface
for (uint8_t tries = 0; tries < MAX_RETRY; tries += 1) {
auto external_iface = getExternalInterface();
if (external_iface > 0) {
return external_iface == rtif->ifi_index;
}
if (nh->nlmsg_type < RTM_NEWADDR) {
auto rtif = static_cast<struct ifinfomsg *>(NLMSG_DATA(nh));
if (rtif->ifi_index == static_cast<int>(net->ifid_)) {
need_update = true;
if (!(rtif->ifi_flags & IFF_RUNNING)) {
net->disconnected();
net->dp.emit();
return NL_SKIP;
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
return false;
}
int waybar::modules::Network::getPreferredIface(int skip_idx) const {
int ifid = -1;
if (config_["interface"].isString()) {
ifid = if_nametoindex(config_["interface"].asCString());
if (ifid > 0) {
return ifid;
} else {
// Try with wildcard
struct ifaddrs *ifaddr, *ifa;
int success = getifaddrs(&ifaddr);
if (success != 0) {
return -1;
}
ifa = ifaddr;
ifid = -1;
while (ifa != nullptr) {
if (ifa->ifa_addr->sa_family == family_ &&
wildcardMatch(config_["interface"].asString(), ifa->ifa_name)) {
ifid = if_nametoindex(ifa->ifa_name);
break;
}
ifa = ifa->ifa_next;
}
freeifaddrs(ifaddr);
return ifid;
}
if (need_update) break;
}
if (net->ifid_ <= 0 && !net->config_["interface"].isString()) {
for (uint8_t i = 0; i < MAX_RETRY; i += 1) {
net->ifid_ = net->getExternalInterface();
if (net->ifid_ > 0) {
break;
}
// Need to wait before get external interface
net->thread_.sleep_for(std::chrono::seconds(1));
// getExternalInterface may need some delay to detect external interface
for (uint8_t tries = 0; tries < MAX_RETRY; tries += 1) {
ifid = getExternalInterface(skip_idx);
if (ifid > 0) {
return ifid;
}
if (net->ifid_ > 0) {
char ifname[IF_NAMESIZE];
if_indextoname(net->ifid_, ifname);
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
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) {
auto net = static_cast<waybar::modules::Network *>(data);
std::lock_guard<std::mutex> lock(net->mutex_);
auto nh = nlmsg_hdr(msg);
auto ifi = static_cast<struct ifinfomsg *>(NLMSG_DATA(nh));
if (nh->nlmsg_type == RTM_DELADDR) {
// Check for valid interface
if (ifi->ifi_index == net->ifid_) {
net->ipaddr_.clear();
net->netmask_.clear();
net->cidr_ = 0;
if (!(ifi->ifi_flags & IFF_RUNNING)) {
net->clearIface();
// Check for a new interface and get info
net->checkNewInterface(ifi);
} else {
net->dp.emit();
}
return NL_OK;
}
} else 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;
need_update = true;
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;
}
}
if (need_update) {
if (net->ifid_ > 0) {
net->getInfo();
} 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;
}
net->dp.emit();
}
return NL_SKIP;
}
@ -464,12 +686,11 @@ int waybar::modules::Network::handleScan(struct nl_msg *msg, void *data) {
}
net->parseEssid(bss);
net->parseSignal(bss);
// TODO(someone): parse quality
return NL_SKIP;
net->parseFreq(bss);
return NL_OK;
}
void waybar::modules::Network::parseEssid(struct nlattr **bss) {
essid_.clear();
if (bss[NL80211_BSS_INFORMATION_ELEMENTS] != nullptr) {
auto ies = static_cast<char *>(nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]));
auto ies_len = nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
@ -504,6 +725,13 @@ void waybar::modules::Network::parseSignal(struct nlattr **bss) {
}
}
void waybar::modules::Network::parseFreq(struct nlattr **bss) {
if (bss[NL80211_BSS_FREQUENCY] != nullptr) {
// in MHz
frequency_ = nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
}
}
bool waybar::modules::Network::associatedOrJoined(struct nlattr **bss) {
if (bss[NL80211_BSS_STATUS] == nullptr) {
return false;
@ -520,7 +748,6 @@ bool waybar::modules::Network::associatedOrJoined(struct nlattr **bss) {
}
auto waybar::modules::Network::getInfo() -> void {
getInterfaceAddress();
struct nl_msg *nl_msg = nlmsg_alloc();
if (nl_msg == nullptr) {
return;
@ -532,5 +759,45 @@ auto waybar::modules::Network::getInfo() -> void {
nlmsg_free(nl_msg);
return;
}
nl_send_sync(sk_, nl_msg);
nl_send_sync(sock_, nl_msg);
}
// https://gist.github.com/rressi/92af77630faf055934c723ce93ae2495
bool waybar::modules::Network::wildcardMatch(const std::string &pattern,
const std::string &text) const {
auto P = int(pattern.size());
auto T = int(text.size());
auto p = 0, fallback_p = -1;
auto t = 0, fallback_t = -1;
while (t < T) {
// Wildcard match:
if (p < P && pattern[p] == '*') {
fallback_p = p++; // starting point after failures
fallback_t = t; // starting point after failures
}
// Simple match:
else if (p < P && (pattern[p] == '?' || pattern[p] == text[t])) {
p++;
t++;
}
// Failure, fall back just after last matched '*':
else if (fallback_p >= 0) {
p = fallback_p + 1; // position just after last matched '*"
t = ++fallback_t; // re-try to match text from here
}
// There were no '*' before, so we fail here:
else {
return false;
}
}
// Consume all '*' at the end of pattern:
while (p < P && pattern[p] == '*') p++;
return p == P;
}

View File

@ -1,19 +1,17 @@
#include "modules/pulseaudio.hpp"
#include <array>
waybar::modules::Pulseaudio::Pulseaudio(const std::string &id, const Json::Value &config)
: ALabel(config, "{volume}%"),
: ALabel(config, "pulseaudio", id, "{volume}%"),
mainloop_(nullptr),
mainloop_api_(nullptr),
context_(nullptr),
scrolling_(false),
sink_idx_(0),
volume_(0),
muted_(false),
scrolling_(false) {
label_.set_name("pulseaudio");
if (!id.empty()) {
label_.get_style_context()->add_class(id);
}
source_idx_(0),
source_volume_(0),
source_muted_(false) {
mainloop_ = pa_threaded_mainloop_new();
if (mainloop_ == nullptr) {
throw std::runtime_error("pa_mainloop_new() failed.");
@ -39,7 +37,7 @@ waybar::modules::Pulseaudio::Pulseaudio(const std::string &id, const Json::Value
// events are configured
if (!config["on-scroll-up"].isString() && !config["on-scroll-down"].isString()) {
event_box_.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK);
event_box_.signal_scroll_event().connect(sigc::mem_fun(*this, &Pulseaudio::handleScroll));
event_box_.signal_scroll_event().connect(sigc::mem_fun(*this, &Pulseaudio::handleVolume));
}
}
@ -58,7 +56,12 @@ void waybar::modules::Pulseaudio::contextStateCb(pa_context *c, void *data) {
case PA_CONTEXT_READY:
pa_context_get_server_info(c, serverInfoCb, 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;
case PA_CONTEXT_FAILED:
pa->mainloop_api_->quit(pa->mainloop_api_, 1);
@ -71,15 +74,15 @@ void waybar::modules::Pulseaudio::contextStateCb(pa_context *c, void *data) {
}
}
bool waybar::modules::Pulseaudio::handleScroll(GdkEventScroll *e) {
bool waybar::modules::Pulseaudio::handleVolume(GdkEventScroll *e) {
// Avoid concurrent scroll event
bool direction_up = false;
uint16_t change = config_["scroll-step"].isUInt() ? config_["scroll-step"].asUInt() * 100 : 100;
pa_cvolume pa_volume = pa_volume_;
if (scrolling_) {
return false;
}
bool direction_up = false;
double volume_tick = (double)PA_VOLUME_NORM / 100;
pa_volume_t change = volume_tick;
pa_cvolume pa_volume = pa_volume_;
scrolling_ = true;
if (e->direction == GDK_SCROLL_UP) {
direction_up = true;
@ -98,12 +101,17 @@ bool waybar::modules::Pulseaudio::handleScroll(GdkEventScroll *e) {
}
}
// isDouble returns true for integers as well, just in case
if (config_["scroll-step"].isDouble()) {
change = round(config_["scroll-step"].asDouble() * volume_tick);
}
if (direction_up) {
if (volume_ + 1 < 100) {
pa_cvolume_inc(&pa_volume, change);
}
} else {
if (volume_ - 1 > 0) {
if (volume_ - 1 >= 0) {
pa_cvolume_dec(&pa_volume, change);
}
}
@ -122,6 +130,8 @@ void waybar::modules::Pulseaudio::subscribeCb(pa_context * conte
unsigned facility = type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK;
if (facility == PA_SUBSCRIPTION_EVENT_SINK) {
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);
}
}
@ -135,6 +145,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.
*/
@ -161,6 +188,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 *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 = {
@ -201,8 +229,18 @@ auto waybar::modules::Pulseaudio::update() -> void {
label_.get_style_context()->remove_class("bluetooth");
}
}
label_.set_markup(fmt::format(
format, fmt::arg("volume", volume_), fmt::arg("icon", getIcon(volume_, getPortIcon()))));
// TODO: find a better way to split source/sink
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_);
if (tooltipEnabled()) {
label_.set_tooltip_text(desc_);

View File

@ -1,5 +1,6 @@
#include "modules/sni/host.hpp"
#include <iostream>
#include <spdlog/spdlog.h>
#include <fmt/ostream.h>
namespace waybar::modules::SNI {
@ -63,14 +64,14 @@ void Host::proxyReady(GObject* src, GAsyncResult* res, gpointer data) {
GError* error = nullptr;
SnWatcher* watcher = sn_watcher_proxy_new_finish(res, &error);
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);
return;
}
auto host = static_cast<SNI::Host*>(data);
host->watcher_ = watcher;
if (error != nullptr) {
std::cerr << error->message << std::endl;
spdlog::error("Host: {}", error->message);
g_error_free(error);
return;
}
@ -82,13 +83,13 @@ void Host::registerHost(GObject* src, GAsyncResult* res, gpointer data) {
GError* error = nullptr;
sn_watcher_call_register_host_finish(SN_WATCHER(src), res, &error);
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);
return;
}
auto host = static_cast<SNI::Host*>(data);
if (error != nullptr) {
std::cerr << error->message << std::endl;
spdlog::error("Host: {}", error->message);
g_error_free(error);
return;
}
@ -130,8 +131,13 @@ std::tuple<std::string, std::string> Host::getBusNameAndObjectPath(const std::st
void Host::addRegisteredItem(std::string service) {
auto [bus_name, object_path] = getBusNameAndObjectPath(service);
items_.emplace_back(new Item(bus_name, object_path, config_));
on_add_(items_.back());
auto it = std::find_if(items_.begin(), items_.end(), [&bus_name, &object_path](const auto& item) {
return bus_name == item->bus_name && object_path == item->object_path;
});
if (it == items_.end()) {
items_.emplace_back(new Item(bus_name, object_path, config_));
on_add_(items_.back());
}
}
}
} // namespace waybar::modules::SNI

View File

@ -1,6 +1,32 @@
#include "modules/sni/item.hpp"
#include <glibmm/main.h>
#include <iostream>
#include <spdlog/spdlog.h>
#include "modules/sni/item.hpp"
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 {
@ -47,23 +73,16 @@ void Item::proxyReady(Glib::RefPtr<Gio::AsyncResult>& result) {
this->proxy_->signal_signal().connect(sigc::mem_fun(*this, &Item::onSignal));
if (this->id.empty() || this->category.empty() || this->status.empty()) {
std::cerr << "Invalid Status Notifier Item: " + this->bus_name + "," + this->object_path
<< std::endl;
spdlog::error("Invalid Status Notifier Item: {}, {}", bus_name, object_path);
return;
}
this->updateImage();
// this->event_box.set_tooltip_text(this->title);
} catch (const Glib::Error& err) {
g_error("Failed to create DBus Proxy for %s %s: %s",
bus_name.c_str(),
object_path.c_str(),
err.what().c_str());
spdlog::error("Failed to create DBus Proxy for {} {}: {}", bus_name, object_path, err.what());
} catch (const std::exception& err) {
g_error("Failed to create DBus Proxy for %s %s: %s",
bus_name.c_str(),
object_path.c_str(),
err.what());
spdlog::error("Failed to create DBus Proxy for {} {}: {}", bus_name, object_path, err.what());
}
}
@ -73,41 +92,57 @@ T get_variant(Glib::VariantBase& value) {
}
void Item::setProperty(const Glib::ustring& name, Glib::VariantBase& value) {
if (name == "Category") {
category = get_variant<std::string>(value);
} else if (name == "Id") {
id = get_variant<std::string>(value);
} else if (name == "Title") {
title = get_variant<std::string>(value);
} else if (name == "Status") {
status = get_variant<std::string>(value);
} else if (name == "WindowId") {
window_id = get_variant<int32_t>(value);
} else if (name == "IconName") {
icon_name = get_variant<std::string>(value);
} else if (name == "IconPixmap") {
icon_pixmap = this->extractPixBuf(value.gobj());
} else if (name == "OverlayIconName") {
overlay_icon_name = get_variant<std::string>(value);
} else if (name == "OverlayIconPixmap") {
// TODO: overlay_icon_pixmap
} else if (name == "AttentionIconName") {
attention_icon_name = get_variant<std::string>(value);
} else if (name == "AttentionIconPixmap") {
// TODO: attention_icon_pixmap
} else if (name == "AttentionMovieName") {
attention_movie_name = get_variant<std::string>(value);
} else if (name == "ToolTip") {
// TODO: tooltip
} else if (name == "IconThemePath") {
icon_theme_path = get_variant<std::string>(value);
if (!icon_theme_path.empty()) {
icon_theme->set_search_path({icon_theme_path});
try {
spdlog::trace("Set tray item property: {}.{} = {}", id.empty() ? bus_name : id, name, value);
if (name == "Category") {
category = get_variant<std::string>(value);
} else if (name == "Id") {
id = get_variant<std::string>(value);
} else if (name == "Title") {
title = get_variant<std::string>(value);
} else if (name == "Status") {
status = get_variant<std::string>(value);
} else if (name == "WindowId") {
window_id = get_variant<int32_t>(value);
} else if (name == "IconName") {
icon_name = get_variant<std::string>(value);
} else if (name == "IconPixmap") {
icon_pixmap = this->extractPixBuf(value.gobj());
} else if (name == "OverlayIconName") {
overlay_icon_name = get_variant<std::string>(value);
} else if (name == "OverlayIconPixmap") {
// TODO: overlay_icon_pixmap
} else if (name == "AttentionIconName") {
attention_icon_name = get_variant<std::string>(value);
} else if (name == "AttentionIconPixmap") {
// TODO: attention_icon_pixmap
} else if (name == "AttentionMovieName") {
attention_movie_name = get_variant<std::string>(value);
} else if (name == "ToolTip") {
// TODO: tooltip
} else if (name == "IconThemePath") {
icon_theme_path = get_variant<std::string>(value);
if (!icon_theme_path.empty()) {
icon_theme->set_search_path({icon_theme_path});
}
} else if (name == "Menu") {
menu = get_variant<std::string>(value);
} else if (name == "ItemIsMenu") {
item_is_menu = get_variant<bool>(value);
}
} else if (name == "Menu") {
menu = get_variant<std::string>(value);
} else if (name == "ItemIsMenu") {
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());
}
}
@ -132,7 +167,7 @@ void Item::processUpdatedProperties(Glib::RefPtr<Gio::AsyncResult>& _result) {
for (const auto& [name, value] : properties) {
Glib::VariantBase old_value;
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);
setProperty(name, const_cast<Glib::VariantBase&>(value));
}
@ -141,14 +176,15 @@ void Item::processUpdatedProperties(Glib::RefPtr<Gio::AsyncResult>& _result) {
this->updateImage();
// this->event_box.set_tooltip_text(this->title);
} 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) {
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,
const Glib::VariantContainerBase& arguments) {
spdlog::trace("Tray item '{}' got signal {}", id, signal_name);
if (!update_pending_ && signal_name.compare(0, 3, "New") == 0) {
/* Debounce signals and schedule update of all properties.
* Based on behavior of Plasma dataengine for StatusNotifierItem.
@ -235,7 +271,7 @@ void Item::updateImage() {
image.set(getIconByName(icon_name, icon_size));
}
} 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) {
// An icon extracted may be the wrong size for the tray
@ -276,39 +312,38 @@ Glib::RefPtr<Gdk::Pixbuf> Item::getIconByName(const std::string& name, int reque
name.c_str(), tmp_size, Gtk::IconLookupFlags::ICON_LOOKUP_FORCE_SIZE);
}
void Item::onMenuDestroyed(Item* self) {
self->gtk_menu = nullptr;
self->dbus_menu = nullptr;
void Item::onMenuDestroyed(Item* self, GObject* old_menu_pointer) {
if (old_menu_pointer == reinterpret_cast<GObject*>(self->dbus_menu)) {
self->gtk_menu = nullptr;
self->dbus_menu = nullptr;
}
}
bool Item::makeMenu(GdkEventButton* const& ev) {
if (gtk_menu == nullptr) {
if (!menu.empty()) {
dbus_menu = dbusmenu_gtkmenu_new(bus_name.data(), menu.data());
if (dbus_menu != nullptr) {
g_object_ref_sink(G_OBJECT(dbus_menu));
g_object_weak_ref(G_OBJECT(dbus_menu), (GWeakNotify)onMenuDestroyed, this);
gtk_menu = Glib::wrap(GTK_MENU(dbus_menu));
gtk_menu->attach_to_widget(event_box);
}
void Item::makeMenu(GdkEventButton* const& ev) {
if (gtk_menu == nullptr && !menu.empty()) {
dbus_menu = dbusmenu_gtkmenu_new(bus_name.data(), menu.data());
if (dbus_menu != nullptr) {
g_object_ref_sink(G_OBJECT(dbus_menu));
g_object_weak_ref(G_OBJECT(dbus_menu), (GWeakNotify)onMenuDestroyed, this);
gtk_menu = Glib::wrap(GTK_MENU(dbus_menu));
gtk_menu->attach_to_widget(event_box);
}
}
if (gtk_menu != nullptr) {
#if GTK_CHECK_VERSION(3, 22, 0)
gtk_menu->popup_at_pointer(reinterpret_cast<GdkEvent*>(ev));
#else
gtk_menu->popup(ev->button, ev->time);
#endif
return true;
}
return false;
}
bool Item::handleClick(GdkEventButton* const& ev) {
auto parameters = Glib::VariantContainerBase::create_tuple(
{Glib::Variant<int>::create(ev->x), Glib::Variant<int>::create(ev->y)});
if ((ev->button == 1 && item_is_menu) || ev->button == 3) {
if (!makeMenu(ev)) {
makeMenu(ev);
if (gtk_menu != nullptr) {
#if GTK_CHECK_VERSION(3, 22, 0)
gtk_menu->popup_at_pointer(reinterpret_cast<GdkEvent*>(ev));
#else
gtk_menu->popup(ev->button, ev->time);
#endif
return true;
} else {
proxy_->call("ContextMenu", parameters);
return true;
}
@ -322,4 +357,4 @@ bool Item::handleClick(GdkEventButton* const& ev) {
return false;
}
} // namespace waybar::modules::SNI
} // namespace waybar::modules::SNI

View File

@ -1,14 +1,18 @@
#include "modules/sni/tray.hpp"
#include <iostream>
#include <spdlog/spdlog.h>
namespace waybar::modules::SNI {
Tray::Tray(const std::string& id, const Bar& bar, const Json::Value& config)
: config_(config),
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),
std::bind(&Tray::onRemove, this, std::placeholders::_1)) {
box_.set_name("tray");
spdlog::warn(
"For a functionnal tray you must have libappindicator-* installed and export "
"XDG_CURRENT_DESKTOP=Unity");
if (!id.empty()) {
box_.get_style_context()->add_class(id);
}
@ -39,4 +43,4 @@ auto Tray::update() -> void {
Tray::operator Gtk::Widget&() { return box_; }
}
} // namespace waybar::modules::SNI

View File

@ -1,24 +1,19 @@
#include "modules/sni/watcher.hpp"
#include <iostream>
#include <spdlog/spdlog.h>
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,
"org.kde.StatusNotifierWatcher",
sigc::mem_fun(*this, &Watcher::busAcquired),
Gio::DBus::SlotNameAcquired(), Gio::DBus::SlotNameLost(),
Gio::DBus::BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT |
Gio::DBus::BUS_NAME_OWNER_FLAGS_REPLACE)),
watcher_id_(id),
watcher_(sn_watcher_skeleton_new()) {}
Watcher::~Watcher() {
if (bus_name_id_ != 0) {
Gio::DBus::unown_name(bus_name_id_);
bus_name_id_ = 0;
}
if (hosts_ != nullptr) {
g_slist_free_full(hosts_, gfWatchFree);
hosts_ = nullptr;
@ -28,7 +23,8 @@ Watcher::~Watcher() {
g_slist_free_full(items_, gfWatchFree);
items_ = nullptr;
}
g_dbus_interface_skeleton_unexport(G_DBUS_INTERFACE_SKELETON(watcher_));
auto iface = G_DBUS_INTERFACE_SKELETON(watcher_);
g_dbus_interface_skeleton_unexport(iface);
}
void Watcher::busAcquired(const Glib::RefPtr<Gio::DBus::Connection>& conn, Glib::ustring name) {
@ -38,7 +34,7 @@ void Watcher::busAcquired(const Glib::RefPtr<Gio::DBus::Connection>& conn, Glib:
if (error != nullptr) {
// Don't print an error when a watcher is already present
if (error->code != 2) {
std::cerr << error->message << std::endl;
spdlog::error("Watcher {}: {}", watcher_id_, error->message);
}
g_error_free(error);
return;
@ -197,4 +193,4 @@ void Watcher::updateRegisteredItems(SnWatcher* obj) {
sn_watcher_set_registered_items(obj, items);
g_variant_unref(variant);
g_free(items);
}
}

View File

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

View File

@ -1,13 +1,10 @@
#include "modules/sway/window.hpp"
#include <spdlog/spdlog.h>
namespace waybar::modules::sway {
Window::Window(const std::string& id, const Bar& bar, const Json::Value& config)
: ALabel(config, "{}"), bar_(bar), windowId_(-1) {
label_.set_name("window");
if (!id.empty()) {
label_.get_style_context()->add_class(id);
}
: ALabel(config, "window", id, "{}"), bar_(bar), windowId_(-1) {
if (label_.get_max_width_chars() == -1) {
label_.set_hexpand(true);
label_.set_ellipsize(Pango::EllipsizeMode::ELLIPSIZE_END);
@ -25,6 +22,7 @@ void Window::onEvent(const struct Ipc::ipc_response& res) { getTree(); }
void Window::onCmd(const struct Ipc::ipc_response& res) {
try {
std::lock_guard<std::mutex> lock(mutex_);
auto payload = parser_.parse(res.payload);
auto [nb, id, name, app_id] = getFocusedNode(payload);
if (!app_id_.empty()) {
@ -32,11 +30,15 @@ void Window::onCmd(const struct Ipc::ipc_response& res) {
}
if (nb == 0) {
bar_.window.get_style_context()->remove_class("solo");
bar_.window.get_style_context()->add_class("empty");
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");
bar_.window.get_style_context()->add_class("solo");
if (!app_id.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 {
@ -50,7 +52,7 @@ void Window::onCmd(const struct Ipc::ipc_response& res) {
dp.emit();
}
} catch (const std::exception& e) {
std::cerr << "Window: " << e.what() << std::endl;
spdlog::error("Window: {}", e.what());
}
}
@ -59,7 +61,7 @@ void Window::worker() {
try {
ipc_.handleEvent();
} catch (const std::exception& e) {
std::cerr << "Window: " << e.what() << std::endl;
spdlog::error("Window: {}", e.what());
}
};
}
@ -97,7 +99,7 @@ void Window::getTree() {
try {
ipc_.sendCmd(IPC_GET_TREE);
} catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
spdlog::error("Window: {}", e.what());
}
}

View File

@ -1,4 +1,5 @@
#include "modules/sway/workspaces.hpp"
#include <spdlog/spdlog.h>
namespace waybar::modules::sway {
@ -15,11 +16,22 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value
ipc_.signal_event.connect(sigc::mem_fun(*this, &Workspaces::onEvent));
ipc_.signal_cmd.connect(sigc::mem_fun(*this, &Workspaces::onCmd));
ipc_.sendCmd(IPC_GET_WORKSPACES);
if (config["enable-bar-scroll"].asBool()) {
auto &window = const_cast<Bar &>(bar_).window;
window.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK);
window.signal_scroll_event().connect(sigc::mem_fun(*this, &Workspaces::handleScroll));
}
// Launch worker
worker();
}
void Workspaces::onEvent(const struct Ipc::ipc_response &res) { ipc_.sendCmd(IPC_GET_WORKSPACES); }
void Workspaces::onEvent(const struct Ipc::ipc_response &res) {
try {
ipc_.sendCmd(IPC_GET_WORKSPACES);
} catch (const std::exception &e) {
spdlog::error("Workspaces: {}", e.what());
}
}
void Workspaces::onCmd(const struct Ipc::ipc_response &res) {
if (res.type == IPC_GET_WORKSPACES) {
@ -36,10 +48,53 @@ void Workspaces::onCmd(const struct Ipc::ipc_response &res) {
? workspace["output"].asString() == bar_.output->name
: true;
});
// 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;
}
}
} else {
// Adding to all outputs
Json::Value v;
v["name"] = p_w_name;
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) {
std::cerr << "Workspaces: " << e.what() << std::endl;
spdlog::error("Workspaces: {}", e.what());
}
} else {
if (scrolling_) {
@ -53,7 +108,7 @@ void Workspaces::worker() {
try {
ipc_.handleEvent();
} catch (const std::exception &e) {
std::cerr << "Workspaces: " << e.what() << std::endl;
spdlog::error("Workspaces: {}", e.what());
}
};
}
@ -124,11 +179,20 @@ Gtk::Button &Workspaces::addButton(const Json::Value &node) {
auto &button = pair.first->second;
box_.pack_start(button, false, false, 0);
button.set_relief(Gtk::RELIEF_NONE);
button.signal_clicked().connect([this, pair] {
button.signal_clicked().connect([this, node] {
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) {
std::cerr << e.what() << std::endl;
spdlog::error("Workspaces: {}", e.what());
}
});
if (!config_["disable-scroll"].asBool()) {
@ -194,13 +258,17 @@ bool Workspaces::handleScroll(GdkEventScroll *e) {
return false;
}
}
ipc_.sendCmd(IPC_COMMAND, fmt::format("workspace \"{}\"", name));
try {
ipc_.sendCmd(IPC_COMMAND, fmt::format("workspace \"{}\"", name));
} catch (const std::exception &e) {
spdlog::error("Workspaces: {}", e.what());
}
return true;
}
const std::string Workspaces::getCycleWorkspace(std::vector<Json::Value>::iterator it,
bool prev) const {
if (prev && it == workspaces_.begin()) {
if (prev && it == workspaces_.begin() && !config_["disable-scroll-wraparound"].asBool()) {
return (*(--workspaces_.end()))["name"].asString();
}
if (prev && it != workspaces_.begin())
@ -208,7 +276,11 @@ const std::string Workspaces::getCycleWorkspace(std::vector<Json::Value>::iterat
else if (!prev && it != workspaces_.end())
++it;
if (!prev && it == workspaces_.end()) {
return (*(++workspaces_.begin()))["name"].asString();
if (config_["disable-scroll-wraparound"].asBool()) {
--it;
} else {
return (*(workspaces_.begin()))["name"].asString();
}
}
return (*it)["name"].asString();
}
@ -235,4 +307,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"
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()) {
file_path_ = config_["hwmon-path"].asString();
} else {
@ -12,10 +12,6 @@ waybar::modules::Temperature::Temperature(const std::string& id, const Json::Val
if (!temp.is_open()) {
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] {
dp.emit();
thread_.sleep_for(interval_);
@ -32,8 +28,11 @@ auto waybar::modules::Temperature::update() -> void {
} else {
label_.get_style_context()->remove_class("critical");
}
label_.set_markup(fmt::format(
format, fmt::arg("temperatureC", temperature_c), fmt::arg("temperatureF", temperature_f)));
auto max_temp = config_["critical-threshold"].isInt() ? config_["critical-threshold"].asInt() : 0;
label_.set_markup(fmt::format(format,
fmt::arg("temperatureC", temperature_c),
fmt::arg("temperatureF", temperature_f),
fmt::arg("icon", getIcon(temperature_c, "", max_temp))));
}
std::tuple<uint16_t, uint16_t> waybar::modules::Temperature::getTemperature() {
@ -55,4 +54,4 @@ std::tuple<uint16_t, uint16_t> waybar::modules::Temperature::getTemperature() {
bool waybar::modules::Temperature::isCritical(uint16_t temperature_c) {
return config_["critical-threshold"].isInt() &&
temperature_c >= config_["critical-threshold"].asInt();
}
}

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