Compare commits

...

78 Commits
0.2.2 ... 0.3.0

Author SHA1 Message Date
4d3c2191cb chore: v0.3.0 2019-01-28 19:38:58 +01:00
38fc62ea27 feat: bar id 2019-01-28 19:26:16 +01:00
2f7b2677d7 Merge pull request #155 from Robinhuett/window_ignore_self_fix
fix(window): Fix #154
2019-01-27 12:06:58 +01:00
6d03d9f6a9 fix(window): Fix #154 2019-01-27 11:59:07 +01:00
6f2d784d4e Merge pull request #154 from Robinhuett/window_ignore_self
fix(window): Ignore waybar
2019-01-27 02:09:44 +01:00
ce6816737c fix(window): Ignore waybar
With this, the window title waybar will be ignored so it wont flicker as decribed in #148
2019-01-26 19:02:11 +01:00
a0fd99b112 Merge pull request #152 from jubalh/fmt
Add fmt dependency to README.md
2019-01-25 16:42:41 +01:00
12a48b70ae Add fmt dependency to README.md 2019-01-25 16:38:38 +01:00
aedf133b16 Merge pull request #146 from Alexays/network_alt
fix(Network): format-alt
2019-01-14 09:05:52 +01:00
02aed73295 Merge branch 'master' into network_alt 2019-01-13 22:39:04 +01:00
9348e88592 Merge pull request #130 from Alexays/destructor
refactor: proper modules destruction
2019-01-13 22:37:19 +01:00
eace8c549f fix(Network): format-alt 2019-01-13 22:36:37 +01:00
f8116132a7 fix(Workspaces): check thread is running before parse response 2019-01-13 22:23:09 +01:00
171e0e5ae3 revert: don't disable seq check 2019-01-13 22:22:22 +01:00
30781757e3 revert: prefer nl_send_sync 2019-01-13 22:22:22 +01:00
800d2f388e refactor(network): proper signal strength type 2019-01-13 22:22:22 +01:00
1647e31b48 refactor: free netlink message 2019-01-13 22:22:22 +01:00
399f61df98 refactor: proper modules destruction 2019-01-13 22:22:22 +01:00
8af813c2f1 Merge pull request #140 from Robinhuett/headers
refactor: replace all gtkmm.h includes
2019-01-09 01:00:00 +01:00
29a2ee1744 refactor: Replace all occurencec of gtkmm.h and only use the necessary headers 2019-01-08 21:05:44 +01:00
afa1cc8287 Update README.md 2018-12-29 00:06:59 +01:00
ab78698ffd Merge pull request #139 from cjbassi/master
Remove ws index from sway ws names
2018-12-28 18:45:58 +01:00
8ea0659ee2 Remove ws index from sway ws names 2018-12-28 09:36:02 -08:00
aa6da11ba4 Merge pull request #138 from cjbassi/master
Add install and build-debug commands to makefile
2018-12-28 15:58:51 +01:00
6a5a4881ad Add install and build-debug commands to makefile 2018-12-28 06:37:07 -08:00
3a9bf932b4 Merge pull request #133 from cjbassi/feature/makefile
Add simple makefile
2018-12-28 01:23:23 +01:00
a078be991a Merge branch 'master' into feature/makefile 2018-12-28 01:16:56 +01:00
83ae5ec5c9 Merge pull request #134 from cjbassi/fix/typo
Fix typo
2018-12-28 01:16:31 +01:00
b4d38294a7 Fix typo 2018-12-27 16:03:29 -08:00
7804514c5c Add simple makefile 2018-12-27 15:56:47 -08:00
513278597a Merge pull request #129 from Robinhuett/custom-module-icon
Add format-icons to custom module
2018-12-26 11:42:25 +01:00
4698c9d2cf chore(custom): Change int conversion 2018-12-26 11:35:58 +01:00
11c98f13e3 feat(custom): Add format-icons to custom module
This commit allows custom modules (json only) to set a percentage. This can be displayed either by using {percentage} or by using {icon} with format-icons set.
2018-12-26 03:52:05 +01:00
76bbdd0425 Merge pull request #128 from Robinhuett/wifiicon
Use SignalStrength for format icons
2018-12-25 21:07:25 +01:00
34df2b0695 fix(ALabel): Better fix for getIcon 2018-12-25 21:03:13 +01:00
ad638357b5 feat(network): Use Signal Strength for format-icons 2018-12-25 14:17:34 +01:00
7404f80122 fix(ALabel): getIcon 2018-12-25 14:15:59 +01:00
de0ee9fcb2 fix(battery): adapter status 2018-12-24 12:17:07 +01:00
755fad6bc3 fix(battery): typo 2018-12-24 08:50:58 +01:00
e3c0624c48 fix(battery): typo 2018-12-24 08:38:37 +01:00
87e55ea993 feat(battery): check adapter online as fallback when battery status report unknown 2018-12-24 08:37:10 +01:00
3b8bfb08a4 fix(network): typo 2018-12-22 14:00:56 +01:00
00728fe877 Merge pull request #125 from chep/master
fix(pulseaudio): return puleseaudio port string if unknown
2018-12-20 10:24:55 +01:00
a4062455cd fix(pulseaudio): return puleseaudio port string if unknown 2018-12-20 09:54:10 +01:00
3691d84543 chore: v0.2.3 2018-12-18 17:42:35 +01:00
b554094c7e feat: args && class id 2018-12-18 17:30:54 +01:00
66ad2864c2 fix(Pulseaudio): use markup 2018-12-17 09:00:40 +01:00
c7b0639f32 fix(workspaces): check thread is running 2018-12-09 10:49:28 +01:00
0acc50264e fix(custom): failed getline after closed pipe 2018-12-08 13:57:56 +01:00
1b13f9e38c fix(custom): close endless scripts 2018-12-08 12:58:47 +01:00
7befd27059 fix: clock interval 2018-12-04 09:38:08 +01:00
2ec34e4adb feat: add $HOME to valid path 2018-12-03 20:12:34 +01:00
812a7a9861 fix: typo 2018-12-03 11:13:56 +01:00
1d96d57b75 feat: warn user about stopped endless custom module 2018-12-03 09:35:10 +01:00
691fb88057 refactor(modules): const bar 2018-12-01 00:10:41 +01:00
281bab4943 feat(ci): opensuse 2018-11-30 21:48:42 +01:00
8e93fd3ae0 revert: Update to new Sway IPC protocol 2018-11-29 09:50:00 +01:00
37c7e586d6 Merge pull request #110 from tokyovigilante/ipc-fix
Update to new Sway IPC protocol
2018-11-29 00:40:57 +01:00
3a5a470d90 Update to new Sway IPC protocol 2018-11-28 22:27:06 +00:00
c051b517ca Merge pull request #108 from SibrenVasse/spotify
fix(custom): fix conditional statement
2018-11-26 20:09:21 +01:00
a0cdef569b fix(custom): fix conditional statement 2018-11-26 19:35:03 +01:00
a123711a8d fix(network): disconnected forced interface 2018-11-25 21:32:50 +01:00
391a7de94a fix(window): escape window name 2018-11-25 15:21:00 +01:00
952d877652 Merge pull request #103 from SibrenVasse/spotify
Event based mediaplayer script
2018-11-24 18:13:08 +01:00
5356d25a9b Merge branch 'master' into spotify 2018-11-24 18:11:48 +01:00
65b9911df4 Merge pull request #105 from David96/master
Allow custom plugins that don't execute anything
2018-11-24 18:01:31 +01:00
2aed121903 Allow custom plugins that don't execute anything 2018-11-24 17:24:02 +01:00
08bfdda4cb revert(network): wait interface 2018-11-24 16:01:22 +01:00
8f8ec3b999 feat(Label): on-click-right 2018-11-24 15:56:16 +01:00
a17220054d feat(client): pefix module name on update error 2018-11-24 11:20:03 +01:00
4cc48b3cfd fix(client): catch error on update 2018-11-24 11:13:52 +01:00
087de4e956 refactor(client): lambda to method 2018-11-24 11:04:56 +01:00
2c2a0473f4 feat(client): throw when we don't have required resources files 2018-11-23 19:31:40 +01:00
2e1f8b2fc5 fix(network): check len of netlinkResponse 2018-11-23 17:52:10 +01:00
686bc4828e refactor(network): only get info when there is an interface 2018-11-23 17:46:14 +01:00
f6c2a8d9b7 fix(network): free the message instead of the socket 2018-11-23 17:42:26 +01:00
baa7f52e21 refactor(network): wait for new address 2018-11-23 16:04:29 +01:00
c4c0c01a2f feat(custom): event based mediaplayer script 2018-11-23 12:08:15 +01:00
51 changed files with 2100 additions and 516 deletions

View File

@ -6,6 +6,7 @@ services:
env:
- distro: debian
- distro: archlinux
- distro: opensuse
before_install:
- docker pull alexays/waybar:${distro}

5
Dockerfiles/opensuse Normal file
View File

@ -0,0 +1,5 @@
FROM opensuse/tumbleweed:latest
RUN zypper -n up && \
zypper -n install -t pattern devel_C_C++ && \
zypper -n install git meson clang libinput-devel libwayland-client0 libwayland-cursor0 wayland-protocols-devel wayland-devel Mesa-libEGL-devel Mesa-libGLESv2-devel libgbm-devel libxkbcommon-devel libudev-devel libpixman-1-0-devel gtkmm3-devel jsoncpp-devel

20
Makefile Normal file
View File

@ -0,0 +1,20 @@
.PHONY: build build-debug run clean default install
default: run
build:
meson build
ninja -C build
build-debug:
meson build --buildtype=debug
ninja -C build
install: build
ninja -C build install
run: build
./build/waybar
clean:
rm -rf build

View File

@ -16,7 +16,7 @@
- Custom scripts
- And much more customizations
**Configuration and Customization**
**Configuration and Styling**
[See the wiki for more details](https://github.com/Alexays/Waybar/wiki).
@ -28,6 +28,25 @@ $ cd Waybar
$ meson build
$ ninja -C build
$ ./build/waybar
# If you want to install it
$ ninja -C build install
$ waybar
```
**Dependencies**
```
gtkmm3
jsoncpp
libinput
libsigc++
fmt
wayland
wlroots
libpulse [Pulseaudio module]
libnl [Network module]
sway [Sway modules]
libdbusmenu-gtk3 [Tray module]
```
Contributions welcome! - have fun :)<br>

View File

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

View File

@ -1,6 +1,8 @@
#pragma once
#include <gtkmm.h>
#include <glibmm/dispatcher.h>
#include <gtkmm/box.h>
#include <gtkmm/widget.h>
namespace waybar {

View File

@ -1,7 +1,10 @@
#pragma once
#include <json/json.h>
#include <gtkmm.h>
#include <glibmm/refptr.h>
#include <gtkmm/main.h>
#include <gtkmm/cssprovider.h>
#include <gtkmm/window.h>
#include "wlr-layer-shell-unstable-v1-client-protocol.h"
#include "xdg-output-unstable-v1-client-protocol.h"
#include "IModule.hpp"
@ -15,6 +18,7 @@ class Bar {
public:
Bar(const Client&, std::unique_ptr<struct wl_output *>&&, uint32_t);
Bar(const Bar&) = delete;
~Bar() = default;
auto toggle() -> void;
@ -26,7 +30,6 @@ class Bar {
std::string output_name;
uint32_t wl_name;
bool visible = true;
uint32_t nb_tray_hosts = 0;
private:
static void handleLogicalPosition(void *, struct zxdg_output_v1 *, int32_t,
int32_t);
@ -52,6 +55,10 @@ class Bar {
Glib::RefPtr<Gtk::StyleContext> style_context_;
Glib::RefPtr<Gtk::CssProvider> css_provider_;
struct zxdg_output_v1 *xdg_output_;
Gtk::Box left_;
Gtk::Box center_;
Gtk::Box right_;
Gtk::Box box_;
std::vector<std::unique_ptr<waybar::IModule>> modules_left_;
std::vector<std::unique_ptr<waybar::IModule>> modules_center_;
std::vector<std::unique_ptr<waybar::IModule>> modules_right_;

View File

@ -15,7 +15,7 @@ class Client {
Client(int argc, char *argv[]);
int main(int argc, char *argv[]);
Glib::RefPtr<Gtk::Application> gtk_app;
Gtk::Main gtk_main;
std::string css_file;
std::string config_file;
Glib::RefPtr<Gdk::Display> gdk_display;
@ -27,8 +27,9 @@ class Client {
std::vector<std::unique_ptr<Bar>> bars;
private:
void setupConfigs(const std::string& config, const std::string& style);
void bindInterfaces();
auto setupCss();
const std::string getValidPath(std::vector<std::string> paths);
static void handleGlobal(void *data, struct wl_registry *registry,
uint32_t name, const char *interface, uint32_t version);

View File

@ -27,10 +27,10 @@ class Bar;
class Factory {
public:
Factory(Bar& bar, const Json::Value& config);
Factory(const Bar& bar, const Json::Value& config);
IModule* makeModule(const std::string &name) const;
private:
Bar& bar_;
const Bar& bar_;
const Json::Value& config_;
};

View File

@ -10,7 +10,7 @@
#include <fmt/format.h>
#include <sys/inotify.h>
#include <algorithm>
#include "util/chrono.hpp"
#include "util/sleeper_thread.hpp"
#include "ALabel.hpp"
namespace waybar::modules {
@ -23,20 +23,24 @@ namespace fs = std::filesystem;
class Battery : public ALabel {
public:
Battery(const Json::Value&);
Battery(const std::string&, const Json::Value&);
~Battery();
auto update() -> void;
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;
const std::string getState(uint8_t) const;
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_;
};

View File

@ -2,14 +2,15 @@
#include <fmt/format.h>
#include "fmt/time.h"
#include "util/chrono.hpp"
#include "util/sleeper_thread.hpp"
#include "ALabel.hpp"
namespace waybar::modules {
class Clock : public ALabel {
public:
Clock(const Json::Value&);
Clock(const std::string&, const Json::Value&);
~Clock() = default;
auto update() -> void;
private:
waybar::util::SleeperThread thread_;

View File

@ -6,14 +6,15 @@
#include <vector>
#include <numeric>
#include <iostream>
#include "util/chrono.hpp"
#include "util/sleeper_thread.hpp"
#include "ALabel.hpp"
namespace waybar::modules {
class Cpu : public ALabel {
public:
Cpu(const Json::Value&);
Cpu(const std::string&, const Json::Value&);
~Cpu() = default;
auto update() -> void;
private:
static inline const std::string data_dir_ = "/proc/stat";

View File

@ -2,7 +2,7 @@
#include <fmt/format.h>
#include <iostream>
#include "util/chrono.hpp"
#include "util/sleeper_thread.hpp"
#include "util/command.hpp"
#include "util/json.hpp"
#include "ALabel.hpp"
@ -11,7 +11,8 @@ namespace waybar::modules {
class Custom : public ALabel {
public:
Custom(const std::string, const Json::Value&);
Custom(const std::string&, const Json::Value&);
~Custom();
auto update() -> void;
private:
void delayWorker();
@ -24,9 +25,11 @@ class Custom : public ALabel {
std::string tooltip_;
std::string class_;
std::string prevclass_;
int percentage_;
waybar::util::SleeperThread thread_;
waybar::util::command::res output_;
waybar::util::JsonParser parser_;
FILE* fp_;
};
}

View File

@ -2,14 +2,15 @@
#include <fmt/format.h>
#include <fstream>
#include "util/chrono.hpp"
#include "util/sleeper_thread.hpp"
#include "ALabel.hpp"
namespace waybar::modules {
class Memory : public ALabel {
public:
Memory(const Json::Value&);
Memory(const std::string&, const Json::Value&);
~Memory() = default;
auto update() -> void;
private:
static inline const std::string data_dir_ = "/proc/meminfo";

View File

@ -7,27 +7,30 @@
#include <netlink/genl/genl.h>
#include <netlink/genl/ctrl.h>
#include <linux/nl80211.h>
#include <sys/epoll.h>
#include <fmt/format.h>
#include "util/chrono.hpp"
#include "util/sleeper_thread.hpp"
#include "ALabel.hpp"
namespace waybar::modules {
class Network : public ALabel {
public:
Network(const Json::Value&);
Network(const std::string&, const Json::Value&);
~Network();
auto update() -> void;
private:
static int netlinkRequest(int, void*, uint32_t, uint32_t groups = 0);
static int netlinkResponse(int, void*, uint32_t, uint32_t groups = 0);
static int scanCb(struct nl_msg*, void*);
static int handleEvents(struct nl_msg*, void*);
static int handleScan(struct nl_msg*, void*);
void worker();
void disconnected();
void initNL80211();
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**);
@ -37,9 +40,11 @@ class Network : public ALabel {
waybar::util::SleeperThread thread_timer_;
int ifid_;
sa_family_t family_;
int sock_fd_;
struct sockaddr_nl nladdr_ = {0};
struct nl_sock* sk_ = nullptr;
struct nl_sock* info_sock_ = nullptr;
int efd_;
int ev_fd_;
int nl80211_id_;
std::string essid_;
@ -47,8 +52,8 @@ class Network : public ALabel {
std::string ipaddr_;
std::string netmask_;
int cidr_;
int signal_strength_dbm_;
uint16_t signal_strength_;
int32_t signal_strength_dbm_;
uint8_t signal_strength_;
};
}

View File

@ -10,7 +10,7 @@ namespace waybar::modules {
class Pulseaudio : public ALabel {
public:
Pulseaudio(const Json::Value&);
Pulseaudio(const std::string&, const Json::Value&);
~Pulseaudio();
auto update() -> void;
private:

View File

@ -1,6 +1,7 @@
#pragma once
#include <gtkmm.h>
#include <glibmm/refptr.h>
#include <giomm.h>
#include <json/json.h>
#include <tuple>
#include <dbus-status-notifier-watcher.h>

View File

@ -1,7 +1,11 @@
#pragma once
#include <dbus-status-notifier-item.h>
#include <gtkmm.h>
#include <glibmm/refptr.h>
#include <gtkmm/eventbox.h>
#include <gtkmm/image.h>
#include <gtkmm/icontheme.h>
#include <gtkmm/menu.h>
#include <json/json.h>
#include <libdbusmenu-gtk/dbusmenu-gtk.h>
#ifdef FILESYSTEM_EXPERIMENTAL

View File

@ -1,7 +1,6 @@
#pragma once
#include <fmt/format.h>
#include <thread>
#include "bar.hpp"
#include "util/json.hpp"
#include "IModule.hpp"
@ -12,14 +11,15 @@ namespace waybar::modules::SNI {
class Tray : public IModule {
public:
Tray(Bar& bar, const Json::Value&);
Tray(const std::string&, const Json::Value&);
~Tray() = default;
auto update() -> void;
operator Gtk::Widget &();
private:
void onAdd(std::unique_ptr<Item>& item);
void onRemove(std::unique_ptr<Item>& item);
std::thread thread_;
static inline std::size_t nb_hosts_ = 0;
const Json::Value& config_;
Gtk::Box box_;
SNI::Watcher watcher_ ;

View File

@ -1,6 +1,7 @@
#pragma once
#include <gtkmm.h>
#include <giomm.h>
#include <glibmm/refptr.h>
#include <dbus-status-notifier-watcher.h>
namespace waybar::modules::SNI {

View File

@ -10,33 +10,31 @@
namespace waybar::modules::sway {
class Ipc {
public:
Ipc();
~Ipc();
public:
Ipc();
~Ipc();
struct ipc_response {
uint32_t size;
uint32_t type;
std::string payload;
};
struct ipc_response {
uint32_t size;
uint32_t type;
std::string payload;
};
void connect();
struct ipc_response sendCmd(uint32_t type,
const std::string& payload = "") const;
void subscribe(const std::string& payload) const;
struct ipc_response handleEvent() const;
protected:
static inline const std::string ipc_magic_ = "i3-ipc";
static inline const size_t ipc_header_size_ = ipc_magic_.size() + 8;
struct ipc_response sendCmd(uint32_t type, const std::string &payload = "") const;
void subscribe(const std::string &payload) const;
struct ipc_response handleEvent() const;
const std::string getSocketPath() const;
int open(const std::string&) const;
struct ipc_response send(int fd, uint32_t type,
const std::string& payload = "") const;
struct ipc_response recv(int fd) const;
protected:
static inline const std::string ipc_magic_ = "i3-ipc";
static inline const size_t ipc_header_size_ = ipc_magic_.size() + 8;
int fd_;
int fd_event_;
const std::string getSocketPath() const;
int open(const std::string &) const;
struct ipc_response send(int fd, uint32_t type, const std::string &payload = "") const;
struct ipc_response recv(int fd) const;
int fd_;
int fd_event_;
};
}
} // namespace waybar::modules::sway

View File

@ -3,30 +3,30 @@
#define event_mask(ev) (1u << (ev & 0x7F))
enum ipc_command_type {
// i3 command types - see i3's I3_REPLY_TYPE constants
IPC_COMMAND = 0,
IPC_GET_WORKSPACES = 1,
IPC_SUBSCRIBE = 2,
IPC_GET_OUTPUTS = 3,
IPC_GET_TREE = 4,
IPC_GET_MARKS = 5,
IPC_GET_BAR_CONFIG = 6,
IPC_GET_VERSION = 7,
IPC_GET_BINDING_MODES = 8,
IPC_GET_CONFIG = 9,
IPC_SEND_TICK = 10,
// i3 command types - see i3's I3_REPLY_TYPE constants
IPC_COMMAND = 0,
IPC_GET_WORKSPACES = 1,
IPC_SUBSCRIBE = 2,
IPC_GET_OUTPUTS = 3,
IPC_GET_TREE = 4,
IPC_GET_MARKS = 5,
IPC_GET_BAR_CONFIG = 6,
IPC_GET_VERSION = 7,
IPC_GET_BINDING_MODES = 8,
IPC_GET_CONFIG = 9,
IPC_SEND_TICK = 10,
// sway-specific command types
IPC_GET_INPUTS = 100,
IPC_GET_SEATS = 101,
// sway-specific command types
IPC_GET_INPUTS = 100,
IPC_GET_SEATS = 101,
// Events sent from sway to clients. Events have the highest bits set.
IPC_EVENT_WORKSPACE = ((1<<31) | 0),
IPC_EVENT_OUTPUT = ((1<<31) | 1),
IPC_EVENT_MODE = ((1<<31) | 2),
IPC_EVENT_WINDOW = ((1<<31) | 3),
IPC_EVENT_BARCONFIG_UPDATE = ((1<<31) | 4),
IPC_EVENT_BINDING = ((1<<31) | 5),
IPC_EVENT_SHUTDOWN = ((1<<31) | 6),
IPC_EVENT_TICK = ((1<<31) | 7),
// Events sent from sway to clients. Events have the highest bits set.
IPC_EVENT_WORKSPACE = ((1 << 31) | 0),
IPC_EVENT_OUTPUT = ((1 << 31) | 1),
IPC_EVENT_MODE = ((1 << 31) | 2),
IPC_EVENT_WINDOW = ((1 << 31) | 3),
IPC_EVENT_BARCONFIG_UPDATE = ((1 << 31) | 4),
IPC_EVENT_BINDING = ((1 << 31) | 5),
IPC_EVENT_SHUTDOWN = ((1 << 31) | 6),
IPC_EVENT_TICK = ((1 << 31) | 7),
};

View File

@ -3,7 +3,7 @@
#include <fmt/format.h>
#include "bar.hpp"
#include "client.hpp"
#include "util/chrono.hpp"
#include "util/sleeper_thread.hpp"
#include "util/json.hpp"
#include "ALabel.hpp"
#include "modules/sway/ipc/client.hpp"
@ -12,12 +12,13 @@ namespace waybar::modules::sway {
class Mode : public ALabel {
public:
Mode(waybar::Bar&, const Json::Value&);
Mode(const std::string&, const waybar::Bar&, const Json::Value&);
~Mode() = default;
auto update() -> void;
private:
void worker();
Bar& bar_;
const Bar& bar_;
waybar::util::SleeperThread thread_;
util::JsonParser parser_;
Ipc ipc_;

View File

@ -4,7 +4,7 @@
#include <tuple>
#include "bar.hpp"
#include "client.hpp"
#include "util/chrono.hpp"
#include "util/sleeper_thread.hpp"
#include "util/json.hpp"
#include "ALabel.hpp"
#include "modules/sway/ipc/client.hpp"
@ -13,14 +13,15 @@ namespace waybar::modules::sway {
class Window : public ALabel {
public:
Window(waybar::Bar&, const Json::Value&);
Window(const std::string&, const waybar::Bar&, const Json::Value&);
~Window() = default;
auto update() -> void;
private:
void worker();
std::tuple<int, std::string> getFocusedNode(Json::Value nodes);
void getFocusedWindow();
Bar& bar_;
const Bar& bar_;
waybar::util::SleeperThread thread_;
util::JsonParser parser_;
Ipc ipc_;

View File

@ -3,16 +3,18 @@
#include <fmt/format.h>
#include "bar.hpp"
#include "client.hpp"
#include "util/chrono.hpp"
#include "util/sleeper_thread.hpp"
#include "util/json.hpp"
#include "IModule.hpp"
#include "modules/sway/ipc/client.hpp"
#include <gtkmm/button.h>
namespace waybar::modules::sway {
class Workspaces : public IModule {
public:
Workspaces(waybar::Bar&, const Json::Value&);
Workspaces(const std::string&, const waybar::Bar&, const Json::Value&);
~Workspaces() = default;
auto update() -> void;
operator Gtk::Widget &();
private:
@ -22,17 +24,18 @@ class Workspaces : public IModule {
bool handleScroll(GdkEventScroll*);
std::string getPrevWorkspace();
std::string getNextWorkspace();
std::string trimWorkspaceName(std::string);
Bar& bar_;
const Bar& bar_;
const Json::Value& config_;
Json::Value workspaces_;
waybar::util::SleeperThread thread_;
Gtk::Box box_;
util::JsonParser parser_;
Ipc ipc_;
std::mutex mutex_;
bool scrolling_;
std::unordered_map<std::string, Gtk::Button> buttons_;
Json::Value workspaces_;
Ipc ipc_;
};
}

View File

@ -1,94 +0,0 @@
#pragma once
#include <chrono>
#include <ctime>
#include <functional>
#include <condition_variable>
#include <thread>
#include <gtkmm.h>
namespace waybar::chrono {
using namespace std::chrono;
using clock = std::chrono::system_clock;
using duration = clock::duration;
using time_point = std::chrono::time_point<clock, duration>;
}
namespace waybar::util {
struct SleeperThread {
SleeperThread() = default;
SleeperThread(std::function<void()> func)
: thread_{[this, func] {
while(true) {
{
std::lock_guard<std::mutex> lock(mutex_);
if (!do_run_) {
break;
}
}
func();
}
}}
{}
SleeperThread& operator=(std::function<void()> func)
{
thread_ = std::thread([this, func] {
while(true) {
{
std::lock_guard<std::mutex> lock(mutex_);
if (!do_run_) {
break;
}
}
func();
}
});
return *this;
}
auto sleep_for(chrono::duration dur)
{
auto lock = std::unique_lock(mutex_);
return condvar_.wait_for(lock, dur);
}
auto sleep_until(chrono::time_point time)
{
auto lock = std::unique_lock(mutex_);
return condvar_.wait_until(lock, time);
}
auto wake_up()
{
condvar_.notify_all();
}
auto stop()
{
do_run_ = false;
condvar_.notify_all();
if (thread_.joinable()) {
thread_.detach();
}
}
~SleeperThread()
{
stop();
}
private:
std::thread thread_;
std::condition_variable condvar_;
std::mutex mutex_;
bool do_run_ = true;
};
}

1264
include/util/clara.hpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,8 @@
#pragma once
#include <sys/wait.h>
#include <giomm.h>
#include <unistd.h>
namespace waybar::util::command {

View File

@ -10,7 +10,7 @@ struct JsonParser {
: reader_(builder_.newCharReader())
{}
const Json::Value parse(const std::string data) const
const Json::Value parse(const std::string& data) const
{
Json::Value root;
std::string err;

View File

@ -0,0 +1,76 @@
#pragma once
#include <chrono>
#include <ctime>
#include <functional>
#include <condition_variable>
#include <thread>
namespace waybar::util {
class SleeperThread {
public:
SleeperThread() = default;
SleeperThread(std::function<void()> func)
: thread_{[this, func] {
while (do_run_) func();
}}
{}
SleeperThread& operator=(std::function<void()> func)
{
thread_ = std::thread([this, func] {
while (do_run_) func();
});
return *this;
}
bool isRunning() const
{
return do_run_;
}
auto sleep_for(std::chrono::system_clock::duration dur)
{
std::unique_lock lk(mutex_);
return condvar_.wait_for(lk, dur, [this] { return !do_run_; });
}
auto sleep_until(std::chrono::time_point<std::chrono::system_clock,
std::chrono::system_clock::duration> time_point)
{
std::unique_lock lk(mutex_);
return condvar_.wait_until(lk, time_point, [this] { return !do_run_; });
}
auto wake_up()
{
condvar_.notify_all();
}
auto stop()
{
{
std::lock_guard<std::mutex> lck(mutex_);
do_run_ = false;
}
condvar_.notify_all();
}
~SleeperThread()
{
stop();
if (thread_.joinable()) {
thread_.join();
}
}
private:
std::thread thread_;
std::condition_variable condvar_;
std::mutex mutex_;
bool do_run_ = true;
};
}

View File

@ -1,6 +1,6 @@
project(
'waybar', 'cpp', 'c',
version: '0.2.2',
version: '0.3.0',
license: 'MIT',
default_options : [
'cpp_std=c++17',
@ -22,6 +22,16 @@ else
endif
compiler = meson.get_compiler('cpp')
git = find_program('git', required: false)
if not git.found()
add_project_arguments('-DVERSION="@0@"'.format(meson.project_version()), language: 'cpp')
else
git_commit_hash = run_command([git.path(), 'describe', '--always', '--tags']).stdout().strip()
git_branch = run_command([git.path(), 'rev-parse', '--abbrev-ref', 'HEAD']).stdout().strip()
version = '"@0@ (" __DATE__ ", branch \'@1@\')"'.format(git_commit_hash, git_branch)
add_project_arguments('-DVERSION=@0@'.format(version), language: 'cpp')
endif
if not compiler.has_header('filesystem')
add_project_arguments('-DFILESYSTEM_EXPERIMENTAL', language: 'cpp')

View File

@ -78,8 +78,6 @@
"custom/spotify": {
"format": " {}",
"max-length": 40,
"interval": 30, // Remove this if your script is endless and write in loop
"exec": "$HOME/.config/waybar/mediaplayer.sh 2> /dev/null", // Script in resources folder
"exec-if": "pgrep spotify"
"exec": "$HOME/.config/waybar/mediaplayer.py 2> /dev/null" // Script in resources folder
}
}

View File

@ -0,0 +1,71 @@
#!/usr/bin/env python3
import sys
import signal
import gi
gi.require_version('Playerctl', '2.0')
from gi.repository import Playerctl, GLib
manager = Playerctl.PlayerManager()
loop = GLib.MainLoop()
def on_play(player, status, manager):
on_metadata(player, player.props.metadata, manager)
def on_metadata(player, metadata, manager):
track_info = ''
if player.props.player_name == 'spotify' and \
'mpris:trackid' in metadata.keys() and \
':ad:' in player.props.metadata['mpris:trackid']:
track_info = 'AD PLAYING'
elif player.get_artist() != '' and player.get_title() != '':
track_info = '{artist} - {title}'.format(artist=player.get_artist(),
title=player.get_title())
else:
sys.stdout.write('\n')
sys.stdout.flush()
return
if player.props.status == 'Playing':
sys.stdout.write(track_info + '\n')
else:
sys.stdout.write('' + track_info + '\n')
sys.stdout.flush()
def on_name_appeared(manager, name):
init_player(name)
def on_player_vanished(manager, player):
sys.stdout.write("\n")
sys.stdout.flush()
def init_player(name):
player = Playerctl.Player.new_from_name(name)
player.connect('playback-status', on_play, manager)
player.connect('metadata', on_metadata, manager)
manager.manage_player(player)
on_metadata(player, player.props.metadata, manager)
def signal_handler(sig, frame):
sys.stdout.write("\n")
sys.stdout.flush()
loop.quit()
sys.exit(0)
manager.connect('name-appeared', on_name_appeared)
manager.connect('player-vanished', on_player_vanished)
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
for player in manager.props.player_names:
init_player(player)
loop.run()

View File

@ -1,7 +0,0 @@
#!/bin/sh
player_status=$(playerctl status 2> /dev/null)
if [ "$player_status" = "Playing" ]; then
echo "$(playerctl metadata artist) - $(playerctl metadata title)"
elif [ "$player_status" = "Paused" ]; then
echo "$(playerctl metadata artist) - $(playerctl metadata title)"
fi

View File

@ -21,17 +21,12 @@ waybar::ALabel::ALabel(const Json::Value& config, const std::string format, uint
}
// configure events' user commands
if (config_["on-click"].isString()) {
if (config_["on-click"].isString() || config_["on-click-right"].isString()) {
event_box_.add_events(Gdk::BUTTON_PRESS_MASK);
event_box_.signal_button_press_event().connect(
sigc::mem_fun(*this, &ALabel::handleToggle));
}
if (config_["on-scroll-up"].isString()) {
event_box_.add_events(Gdk::SCROLL_MASK);
event_box_.signal_scroll_event().connect(
sigc::mem_fun(*this, &ALabel::handleScroll));
}
if (config_["on-scroll-down"].isString()) {
if (config_["on-scroll-up"].isString() || config_["on-scroll-down"].isString()) {
event_box_.add_events(Gdk::SCROLL_MASK);
event_box_.signal_scroll_event().connect(
sigc::mem_fun(*this, &ALabel::handleScroll));
@ -45,9 +40,11 @@ auto waybar::ALabel::update() -> void {
bool waybar::ALabel::handleToggle(GdkEventButton* const& e) {
if (config_["on-click"].isString() && e->button == 1) {
waybar::util::command::forkExec(config_["on-click"].asString());
} else if (config_["on-click-right"].isString() && e->button == 3) {
waybar::util::command::forkExec(config_["on-click-right"].asString());
} else {
alt = !alt;
if (alt) {
alt_ = !alt_;
if (alt_) {
format_ = config_["format-alt"].asString();
} else {
format_ = default_format_;
@ -93,7 +90,7 @@ std::string waybar::ALabel::getIcon(uint16_t percentage,
const std::string& alt) {
auto format_icons = config_["format-icons"];
if (format_icons.isObject()) {
if (!alt.empty() && format_icons[alt].isString()) {
if (!alt.empty() && (format_icons[alt].isString() || format_icons[alt].isArray())) {
format_icons = format_icons[alt];
} else {
format_icons = format_icons["default"];

View File

@ -7,7 +7,9 @@ waybar::Bar::Bar(const Client& client,
std::unique_ptr<struct wl_output *> &&p_output, uint32_t p_wl_name)
: client(client), window{Gtk::WindowType::WINDOW_TOPLEVEL},
surface(nullptr), layer_surface(nullptr),
output(std::move(p_output)), wl_name(p_wl_name)
output(std::move(p_output)), wl_name(p_wl_name),
left_(Gtk::ORIENTATION_HORIZONTAL, 0), center_(Gtk::ORIENTATION_HORIZONTAL, 0),
right_(Gtk::ORIENTATION_HORIZONTAL, 0), box_(Gtk::ORIENTATION_HORIZONTAL, 0)
{
static const struct zxdg_output_v1_listener xdgOutputListener = {
.logical_position = handleLogicalPosition,
@ -184,7 +186,13 @@ void waybar::Bar::getModules(const Factory& factory, const std::string& pos)
if (pos == "modules-right") {
modules_right_.emplace_back(module);
}
module->dp.connect([module] { module->update(); });
module->dp.connect([module, &name] {
try {
module->update();
} catch (const std::exception& e) {
std::cerr << name.asString() + ": " + e.what() << std::endl;
}
});
} catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
}
@ -194,28 +202,23 @@ void waybar::Bar::getModules(const Factory& factory, const std::string& pos)
auto waybar::Bar::setupWidgets() -> void
{
auto &left = *Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 0));
auto &center = *Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 0));
auto &right = *Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 0));
auto &box = *Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 0));
window.add(box);
box.pack_start(left, true, true);
box.set_center_widget(center);
box.pack_end(right, true, true);
window.add(box_);
box_.pack_start(left_, true, true);
box_.set_center_widget(center_);
box_.pack_end(right_, true, true);
Factory factory(*this, config_);
getModules(factory, "modules-left");
getModules(factory, "modules-center");
getModules(factory, "modules-right");
for (auto const& module : modules_left_) {
left.pack_start(*module, false, true, 0);
left_.pack_start(*module, false, true, 0);
}
for (auto const& module : modules_center_) {
center.pack_start(*module, true, true, 0);
center_.pack_start(*module, true, true, 0);
}
std::reverse(modules_right_.begin(), modules_right_.end());
for (auto const& module : modules_right_) {
right.pack_end(*module, false, false, 0);
right_.pack_end(*module, false, false, 0);
}
}

View File

@ -1,8 +1,9 @@
#include "client.hpp"
#include "util/clara.hpp"
#include <iostream>
waybar::Client::Client(int argc, char* argv[])
: gtk_app(Gtk::Application::create(argc, argv, "fr.arouillard.waybar")),
: gtk_main(argc, argv),
gdk_display(Gdk::Display::get_default())
{
if (!gdk_display) {
@ -12,36 +13,24 @@ waybar::Client::Client(int argc, char* argv[])
throw std::runtime_error("Bar need to run under Wayland");
}
wl_display = gdk_wayland_display_get_wl_display(gdk_display->gobj());
auto getFirstValidPath = [] (std::vector<std::string> possiblePaths) {
wordexp_t p;
}
for (const std::string &path: possiblePaths) {
if (wordexp(path.c_str(), &p, 0) == 0) {
if (access(*p.we_wordv, F_OK) == 0) {
std::string result = *p.we_wordv;
wordfree(&p);
return result;
}
const std::string waybar::Client::getValidPath(std::vector<std::string> paths)
{
wordexp_t p;
for (const std::string &path: paths) {
if (wordexp(path.c_str(), &p, 0) == 0) {
if (access(*p.we_wordv, F_OK) == 0) {
std::string result = *p.we_wordv;
wordfree(&p);
return result;
}
wordfree(&p);
}
}
return std::string();
};
config_file = getFirstValidPath({
"$XDG_CONFIG_HOME/waybar/config",
"$HOME/waybar/config",
"/etc/xdg/waybar/config",
"./resources/config",
});
css_file = getFirstValidPath({
"$XDG_CONFIG_HOME/waybar/style.css",
"$HOME/waybar/style.css",
"/etc/xdg/waybar/style.css",
"./resources/style.css",
});
std::cout << "Resources files: " + config_file + ", " + css_file << std::endl;
return std::string();
}
void waybar::Client::handleGlobal(void *data, struct wl_registry *registry,
@ -81,6 +70,28 @@ void waybar::Client::handleGlobalRemove(void* data,
}
}
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",
}) : 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()) {
throw std::runtime_error("Missing required resources files");
}
std::cout << "Resources files: " + config_file + ", " + css_file << std::endl;
}
void waybar::Client::bindInterfaces()
{
registry = wl_display_get_registry(wl_display);
@ -96,11 +107,34 @@ void waybar::Client::bindInterfaces()
wl_display_roundtrip(wl_display);
}
int waybar::Client::main(int /*argc*/, char* /*argv*/[])
int waybar::Client::main(int argc, char* argv[])
{
bool show_help = false;
bool show_version = false;
std::string config;
std::string style;
std::string bar_id;
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(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;
return 1;
}
if (show_help) {
std::cout << cli << std::endl;
return 0;
}
if (show_version) {
std::cout << "Waybar v" << VERSION << std::endl;
return 0;
}
setupConfigs(config, style);
bindInterfaces();
gtk_app->hold();
gtk_app->run();
gtk_main.run();
bars.clear();
zxdg_output_manager_v1_destroy(xdg_output_manager);
zwlr_layer_shell_v1_destroy(layer_shell);

View File

@ -1,49 +1,51 @@
#include "factory.hpp"
waybar::Factory::Factory(Bar& bar, const Json::Value& config)
waybar::Factory::Factory(const Bar& bar, const Json::Value& config)
: bar_(bar), config_(config)
{}
waybar::IModule* waybar::Factory::makeModule(const std::string &name) const
{
try {
auto ref = name.substr(0, name.find("#"));
auto hash_pos = name.find("#");
auto ref = name.substr(0, hash_pos);
auto id = hash_pos != std::string::npos ? name.substr(hash_pos + 1) : "";
if (ref == "battery") {
return new waybar::modules::Battery(config_[name]);
return new waybar::modules::Battery(id, config_[name]);
}
#ifdef HAVE_SWAY
if (ref == "sway/mode") {
return new waybar::modules::sway::Mode(bar_, config_[name]);
return new waybar::modules::sway::Mode(id, bar_, config_[name]);
}
if (ref == "sway/workspaces") {
return new waybar::modules::sway::Workspaces(bar_, config_[name]);
return new waybar::modules::sway::Workspaces(id, bar_, config_[name]);
}
if (ref == "sway/window") {
return new waybar::modules::sway::Window(bar_, config_[name]);
return new waybar::modules::sway::Window(id, bar_, config_[name]);
}
#endif
if (ref == "memory") {
return new waybar::modules::Memory(config_[name]);
return new waybar::modules::Memory(id, config_[name]);
}
if (ref == "cpu") {
return new waybar::modules::Cpu(config_[name]);
return new waybar::modules::Cpu(id, config_[name]);
}
if (ref == "clock") {
return new waybar::modules::Clock(config_[name]);
return new waybar::modules::Clock(id, config_[name]);
}
#ifdef HAVE_DBUSMENU
if (ref == "tray") {
return new waybar::modules::SNI::Tray(bar_, config_[name]);
return new waybar::modules::SNI::Tray(id, config_[name]);
}
#endif
#ifdef HAVE_LIBNL
if (ref == "network") {
return new waybar::modules::Network(config_[name]);
return new waybar::modules::Network(id, config_[name]);
}
#endif
#ifdef HAVE_LIBPULSE
if (ref == "pulseaudio") {
return new waybar::modules::Pulseaudio(config_[name]);
return new waybar::modules::Pulseaudio(id, config_[name]);
}
#endif
if (ref.compare(0, 7, "custom/") == 0 && ref.size() > 7) {

View File

@ -15,7 +15,7 @@ int main(int argc, char* argv[])
waybar::client = &c;
std::signal(SIGUSR1, [] (int /*signal*/) {
for (auto& bar : waybar::client->bars) {
(*bar).toggle();
bar->toggle();
}
});

View File

@ -1,21 +1,71 @@
#include "modules/battery.hpp"
waybar::modules::Battery::Battery(const Json::Value& config)
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);
}
getBatteries();
fd_ = inotify_init1(IN_CLOEXEC);
if (fd_ == -1) {
throw std::runtime_error("Unable to listen batteries.");
}
for (auto const& bat : batteries_) {
auto wd = inotify_add_watch(fd_, (bat / "uevent").c_str(), IN_ACCESS);
if (wd != -1) {
wds_.push_back(wd);
}
}
worker();
}
waybar::modules::Battery::~Battery()
{
for (auto wd : wds_) {
inotify_rm_watch(fd_, wd);
}
close(fd_);
}
void waybar::modules::Battery::worker()
{
thread_timer_ = [this] {
dp.emit();
thread_timer_.sleep_for(interval_);
};
thread_ = [this] {
struct inotify_event event = {0};
int nbytes = read(fd_, &event, sizeof(event));
if (nbytes != sizeof(event) || event.mask & IN_IGNORED) {
thread_.stop();
return;
}
// TODO: don't stop timer for now since there is some bugs :?
// thread_timer_.stop();
dp.emit();
};
}
void waybar::modules::Battery::getBatteries()
{
try {
if (config_["bat"].isString()) {
auto dir = data_dir_ / config_["bat"].asString();
if (fs::is_directory(dir) && fs::exists(dir / "capacity")
&& fs::exists(dir / "status") && fs::exists(dir / "uevent")) {
batteries_.push_back(dir);
for (auto const& node : fs::directory_iterator(data_dir_)) {
if (!fs::is_directory(node)) {
continue;
}
} else {
for (auto const& node : fs::directory_iterator(data_dir_)) {
if (fs::is_directory(node) && fs::exists(node / "capacity")
&& fs::exists(node / "status") && fs::exists(node / "uevent")) {
auto dir_name = node.path().filename();
auto bat_defined = config_["bat"].isString();
if (((bat_defined && dir_name == config_["bat"].asString())
|| !bat_defined) && fs::exists(node / "capacity")
&& fs::exists(node / "uevent") && fs::exists(node / "status")) {
batteries_.push_back(node);
}
}
auto adap_defined = config_["adapter"].isString();
if (((adap_defined && dir_name == config_["adapter"].asString())
|| !adap_defined) && fs::exists(node / "online")) {
adapter_ = node;
}
}
} catch (fs::filesystem_error &e) {
@ -27,39 +77,6 @@ waybar::modules::Battery::Battery(const Json::Value& config)
}
throw std::runtime_error("No batteries.");
}
fd_ = inotify_init1(IN_CLOEXEC);
if (fd_ == -1) {
throw std::runtime_error("Unable to listen batteries.");
}
for (auto const& bat : batteries_) {
inotify_add_watch(fd_, (bat / "uevent").c_str(), IN_ACCESS);
}
worker();
}
waybar::modules::Battery::~Battery()
{
close(fd_);
}
void waybar::modules::Battery::worker()
{
// Trigger first values
update();
thread_timer_ = [this] {
thread_.sleep_for(interval_);
dp.emit();
};
thread_ = [this] {
struct inotify_event event = {0};
int nbytes = read(fd_, &event, sizeof(event));
if (nbytes != sizeof(event)) {
return;
}
// TODO: don't stop timer for now since there is some bugs :?
// thread_timer_.stop();
dp.emit();
};
}
const std::tuple<uint8_t, std::string> waybar::modules::Battery::getInfos() const
@ -85,6 +102,19 @@ const std::tuple<uint8_t, std::string> waybar::modules::Battery::getInfos() cons
}
}
const std::string waybar::modules::Battery::getAdapterStatus(uint8_t capacity) const
{
if (!adapter_.empty()) {
bool online;
std::ifstream(adapter_ / "online") >> online;
if (capacity == 100) {
return "Full";
}
return online ? "Charging" : "Discharging";
}
return "Unknown";
}
const std::string waybar::modules::Battery::getState(uint8_t capacity) const
{
// Get current state
@ -101,7 +131,7 @@ const std::string waybar::modules::Battery::getState(uint8_t capacity) const
return a.second < b.second;
});
std::string valid_state;
for (auto state : states) {
for (auto const& state : states) {
if (capacity <= state.second && valid_state.empty()) {
label_.get_style_context()->add_class(state.first);
valid_state = state.first;
@ -115,6 +145,9 @@ const std::string waybar::modules::Battery::getState(uint8_t capacity) const
auto waybar::modules::Battery::update() -> void
{
auto [capacity, status] = getInfos();
if (status == "Unknown") {
status = getAdapterStatus(capacity);
}
label_.set_tooltip_text(status);
std::transform(status.begin(), status.end(), status.begin(), ::tolower);
auto format = format_;
@ -131,10 +164,8 @@ auto waybar::modules::Battery::update() -> void
}
if (format.empty()) {
event_box_.hide();
label_.set_name("");
} else {
event_box_.show();
label_.set_name("battery");
label_.set_markup(fmt::format(format, fmt::arg("capacity", capacity),
fmt::arg("icon", getIcon(capacity))));
}

View File

@ -1,16 +1,19 @@
#include "modules/clock.hpp"
waybar::modules::Clock::Clock(const Json::Value& config)
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);
}
thread_ = [this] {
auto now = waybar::chrono::clock::now();
auto now = std::chrono::system_clock::now();
dp.emit();
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() % 60;
std::chrono::duration_cast<std::chrono::seconds>(time_s.time_since_epoch()).count() % interval_.count();
thread_.sleep_until(timeout - std::chrono::seconds(sub_m - 1));
};
}

View File

@ -1,9 +1,12 @@
#include "modules/cpu.hpp"
waybar::modules::Cpu::Cpu(const Json::Value& config)
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);
}
thread_ = [this] {
dp.emit();
thread_.sleep_for(interval_);
@ -12,16 +15,12 @@ waybar::modules::Cpu::Cpu(const Json::Value& config)
auto waybar::modules::Cpu::update() -> void
{
try {
// TODO: as creating dynamic fmt::arg arrays is buggy we have to calc both
auto cpu_load = getCpuLoad();
auto [cpu_usage, tooltip] = getCpuUsage();
label_.set_tooltip_text(tooltip);
label_.set_markup(fmt::format(format_,
fmt::arg("load", cpu_load), fmt::arg("usage", cpu_usage)));
} catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
}
// TODO: as creating dynamic fmt::arg arrays is buggy we have to calc both
auto cpu_load = getCpuLoad();
auto [cpu_usage, tooltip] = getCpuUsage();
label_.set_tooltip_text(tooltip);
label_.set_markup(fmt::format(format_,
fmt::arg("load", cpu_load), fmt::arg("usage", cpu_usage)));
}
uint16_t waybar::modules::Cpu::getCpuLoad()
@ -39,7 +38,7 @@ std::tuple<uint16_t, std::string> waybar::modules::Cpu::getCpuUsage()
{
if (prev_times_.empty()) {
prev_times_ = parseCpuinfo();
std::this_thread::sleep_for(chrono::milliseconds(100));
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
std::vector<std::tuple<size_t, size_t>> curr_times = parseCpuinfo();
std::string tooltip;

View File

@ -1,16 +1,25 @@
#include "modules/custom.hpp"
waybar::modules::Custom::Custom(const std::string name,
waybar::modules::Custom::Custom(const std::string& name,
const Json::Value& config)
: ALabel(config, "{}"), name_(name)
: ALabel(config, "{}"), name_(name), fp_(nullptr)
{
if (!config_["exec"].isString()) {
throw std::runtime_error(name_ + " has no exec path.");
label_.set_name("custom-" + name_);
if (config_["exec"].isString()) {
if (interval_.count() > 0) {
delayWorker();
} else {
continuousWorker();
}
}
if (interval_.count() > 0) {
delayWorker();
} else {
continuousWorker();
dp.emit();
}
waybar::modules::Custom::~Custom()
{
if (fp_) {
pclose(fp_);
fp_ = nullptr;
}
}
@ -22,8 +31,7 @@ void waybar::modules::Custom::delayWorker()
auto res = waybar::util::command::exec(config_["exec-if"].asString());
if (res.exit_code != 0) {
can_update = false;
label_.hide();
label_.set_name("");
event_box_.hide();
}
}
if (can_update) {
@ -37,17 +45,21 @@ void waybar::modules::Custom::delayWorker()
void waybar::modules::Custom::continuousWorker()
{
auto cmd = config_["exec"].asString();
FILE* fp(popen(cmd.c_str(), "r"));
if (!fp) {
fp_ = popen(cmd.c_str(), "r");
if (!fp_) {
throw std::runtime_error("Unable to open " + cmd);
}
thread_ = [this, fp] {
thread_ = [this] {
char* buff = nullptr;
size_t len = 0;
if (getline(&buff, &len, fp) == -1) {
pclose(fp);
if (getline(&buff, &len, fp_) == -1) {
if (fp_) {
pclose(fp_);
fp_ = nullptr;
}
thread_.stop();
output_ = { 1, "" };
std::cerr << name_ + " just stopped, is it endless?" << std::endl;
dp.emit();
return;
}
@ -66,19 +78,18 @@ void waybar::modules::Custom::continuousWorker()
auto waybar::modules::Custom::update() -> void
{
// Hide label if output is empty
if (output_.out.empty() || output_.exit_code != 0) {
label_.hide();
label_.set_name("");
if (config_["exec"].isString() && (output_.out.empty() || output_.exit_code != 0)) {
event_box_.hide();
} else {
label_.set_name("custom-" + name_);
if (config_["return-type"].asString() == "json") {
parseOutputJson();
} else {
parseOutputRaw();
}
auto str = fmt::format(format_, text_);
auto str = fmt::format(format_, text_,
fmt::arg("icon", getIcon(percentage_)),
fmt::arg("percentage", percentage_));
label_.set_markup(str);
if (text_ == tooltip_) {
label_.set_tooltip_text(str);
@ -96,7 +107,7 @@ auto waybar::modules::Custom::update() -> void
prevclass_ = "";
}
label_.show();
event_box_.show();
}
}
@ -130,6 +141,11 @@ void waybar::modules::Custom::parseOutputJson()
text_ = parsed["text"].asString();
tooltip_ = parsed["tooltip"].asString();
class_ = parsed["class"].asString();
if (!parsed["percentage"].asString().empty() && parsed["percentage"].isUInt()) {
percentage_ = parsed["percentage"].asUInt();
} else {
percentage_ = 0;
}
break;
}
}
}

View File

@ -1,8 +1,12 @@
#include "modules/memory.hpp"
waybar::modules::Memory::Memory(const Json::Value& config)
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);
}
thread_ = [this] {
dp.emit();
thread_.sleep_for(interval_);
@ -17,11 +21,9 @@ auto waybar::modules::Memory::update() -> void
label_.set_markup(fmt::format(format_, used_ram_percentage));
auto used_ram_gigabytes = (memtotal_ - memfree_) / std::pow(1024, 2);
label_.set_tooltip_text(fmt::format("{:.{}f}Gb used", used_ram_gigabytes, 1));
label_.set_name("memory");
label_.show();
event_box_.show();
} else {
label_.set_name("");
label_.hide();
event_box_.hide();
}
}

View File

@ -1,19 +1,16 @@
#include <sys/eventfd.h>
#include "modules/network.hpp"
waybar::modules::Network::Network(const Json::Value& config)
: ALabel(config, "{ifname}", 60), family_(AF_INET),
signal_strength_dbm_(0), signal_strength_(0)
waybar::modules::Network::Network(const std::string& id, const Json::Value& config)
: ALabel(config, "{ifname}", 60), family_(AF_INET), efd_(-1), ev_fd_(-1),
cidr_(-1), signal_strength_dbm_(0), signal_strength_(0)
{
sock_fd_ = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (sock_fd_ < 0) {
throw std::runtime_error("Can't open network socket");
}
nladdr_.nl_family = AF_NETLINK;
nladdr_.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR;
if (bind(sock_fd_, reinterpret_cast<struct sockaddr *>(&nladdr_),
sizeof(nladdr_)) != 0) {
throw std::runtime_error("Can't bind network socket");
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();
@ -26,99 +23,151 @@ waybar::modules::Network::Network(const Json::Value& config)
char ifname[IF_NAMESIZE];
if_indextoname(ifid_, ifname);
ifname_ = ifname;
getInterfaceAddress();
}
}
initNL80211();
label_.set_name("network");
// Trigger first values
getInfo();
update();
dp.emit();
worker();
}
waybar::modules::Network::~Network()
{
close(sock_fd_);
nl_socket_free(sk_);
if (ev_fd_ > -1) {
eventfd_write(ev_fd_, 1);
std::this_thread::sleep_for(std::chrono::milliseconds(150));
close(ev_fd_);
}
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 (sk_ != nullptr) {
nl_close(sk_);
nl_socket_free(sk_);
}
}
void waybar::modules::Network::createInfoSocket()
{
info_sock_ = nl_socket_alloc();
if (nl_connect(info_sock_, NETLINK_ROUTE) != 0) {
throw std::runtime_error("Can't connect network socket");
}
if (nl_socket_add_membership(info_sock_, RTMGRP_LINK) != 0) {
throw std::runtime_error("Can't add membership");
}
if (nl_socket_add_membership(info_sock_, RTMGRP_IPV4_IFADDR) != 0) {
throw std::runtime_error("Can't add membership");
}
nl_socket_disable_seq_check(info_sock_);
nl_socket_set_nonblocking(info_sock_);
nl_socket_modify_cb(info_sock_, NL_CB_MSG_IN, NL_CB_CUSTOM, handleEvents, this);
efd_ = epoll_create1(0);
if (efd_ < 0) {
throw std::runtime_error("Can't create epoll");
}
{
ev_fd_ = eventfd(0, EFD_NONBLOCK);
struct epoll_event event;
event.events = EPOLLIN | EPOLLET;
event.data.fd = ev_fd_;
if (epoll_ctl(efd_, EPOLL_CTL_ADD, ev_fd_, &event) == -1) {
throw std::runtime_error("Can't add epoll event");
}
}
{
auto fd = nl_socket_get_fd(info_sock_);
struct epoll_event event;
event.events = EPOLLIN;
event.data.fd = fd;
if (epoll_ctl(efd_, EPOLL_CTL_ADD, fd, &event) == -1) {
throw std::runtime_error("Can't add epoll event");
}
}
}
void waybar::modules::Network::createEventSocket()
{
sk_ = nl_socket_alloc();
if (genl_connect(sk_) != 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) {
throw std::runtime_error("Can't set callback");
}
nl80211_id_ = genl_ctrl_resolve(sk_, "nl80211");
if (nl80211_id_ < 0) {
throw std::runtime_error("Can't resolve nl80211 interface");
}
}
void waybar::modules::Network::worker()
{
thread_ = [this] {
char buf[4096];
uint64_t len = netlinkResponse(sock_fd_, buf, sizeof(buf),
RTMGRP_LINK | RTMGRP_IPV4_IFADDR);
bool need_update = false;
for (auto nh = reinterpret_cast<struct nlmsghdr *>(buf); NLMSG_OK(nh, len);
nh = NLMSG_NEXT(nh, len)) {
if (nh->nlmsg_type == NLMSG_DONE) {
break;
}
if (nh->nlmsg_type == NLMSG_ERROR) {
continue;
}
if (nh->nlmsg_type < RTM_NEWADDR) {
auto rtif = static_cast<struct ifinfomsg *>(NLMSG_DATA(nh));
if (rtif->ifi_index == static_cast<int>(ifid_)) {
need_update = true;
if (!(rtif->ifi_flags & IFF_RUNNING)) {
disconnected();
}
}
}
}
if (ifid_ <= 0 && !config_["interface"].isString()) {
// Need to wait before get external interface
thread_.sleep_for(std::chrono::seconds(1));
ifid_ = getExternalInterface();
if (ifid_ > 0) {
char ifname[IF_NAMESIZE];
if_indextoname(ifid_, ifname);
ifname_ = ifname;
getInterfaceAddress();
need_update = true;
}
}
if (need_update) {
getInfo();
dp.emit();
}
};
thread_timer_ = [this] {
thread_.sleep_for(interval_);
if (ifid_ > 0) {
getInfo();
dp.emit();
}
thread_timer_.sleep_for(interval_);
};
thread_ = [this] {
struct epoll_event events[16];
int ec = epoll_wait(efd_, events, 16, -1);
if (ec > 0) {
for (auto i = 0; i < ec; i++) {
if (events[i].events & EPOLLIN
&& events[i].data.fd == nl_socket_get_fd(info_sock_)) {
nl_recvmsgs_default(info_sock_);
} else {
thread_.stop();
break;
}
}
} else if (ec == -1) {
thread_.stop();
}
};
}
auto waybar::modules::Network::update() -> void
{
auto format = format_;
if (ifid_ <= 0) {
format = config_["format-disconnected"].isString()
? config_["format-disconnected"].asString() : format;
std::string connectiontype;
if (ifid_ <= 0 || ipaddr_.empty()) {
if (config_["format-disconnected"].isString()) {
default_format_ = config_["format-disconnected"].asString();
}
label_.get_style_context()->add_class("disconnected");
connectiontype = "disconnected";
} else {
if (essid_.empty()) {
format = config_["format-ethernet"].isString()
? config_["format-ethernet"].asString() : format;
if (config_["format-ethernet"].isString()) {
default_format_ = config_["format-ethernet"].asString();
}
connectiontype = "ethernet";
} else {
format = config_["format-wifi"].isString()
? config_["format-wifi"].asString() : format;
if (config_["format-wifi"].isString()) {
default_format_ = config_["format-wifi"].asString();
}
connectiontype = "wifi";
}
label_.get_style_context()->remove_class("disconnected");
}
label_.set_markup(fmt::format(format,
if (!alt_) {
format_ = default_format_;
}
label_.set_markup(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("cidr", cidr_),
fmt::arg("icon", getIcon(signal_strength_, connectiontype))
));
}
@ -130,26 +179,12 @@ void waybar::modules::Network::disconnected()
ipaddr_.clear();
netmask_.clear();
cidr_ = 0;
ifname_.clear();
ifid_ = -1;
}
void waybar::modules::Network::initNL80211()
{
sk_ = nl_socket_alloc();
if (genl_connect(sk_) != 0) {
nl_socket_free(sk_);
throw std::runtime_error("Can't connect to netlink socket");
}
if (nl_socket_modify_cb(sk_, NL_CB_VALID, NL_CB_CUSTOM, scanCb, this) < 0) {
nl_socket_free(sk_);
throw std::runtime_error("Can't connect to netlink socket");
}
nl80211_id_ = genl_ctrl_resolve(sk_, "nl80211");
if (nl80211_id_ < 0) {
nl_socket_free(sk_);
throw std::runtime_error("Can't resolve nl80211 interface");
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
@ -175,7 +210,7 @@ int waybar::modules::Network::getExternalInterface()
rt->rtm_table = RT_TABLE_MAIN;
/* Issue the query. */
if (netlinkRequest(sock_fd_, req, reqlen) < 0) {
if (netlinkRequest(req, reqlen) < 0) {
goto out;
}
@ -185,7 +220,7 @@ int waybar::modules::Network::getExternalInterface()
* consume responses till NLMSG_DONE/NLMSG_ERROR is encountered).
*/
do {
auto len = netlinkResponse(sock_fd_, resp, route_buffer_size);
auto len = netlinkResponse(resp, route_buffer_size);
if (len < 0) {
goto out;
}
@ -305,7 +340,7 @@ void waybar::modules::Network::getInterfaceAddress() {
}
}
int waybar::modules::Network::netlinkRequest(int fd, void *req,
int waybar::modules::Network::netlinkRequest(void *req,
uint32_t reqlen, uint32_t groups)
{
struct sockaddr_nl sa = {};
@ -313,26 +348,61 @@ int waybar::modules::Network::netlinkRequest(int fd, void *req,
sa.nl_groups = groups;
struct iovec iov = { req, reqlen };
struct msghdr msg = { &sa, sizeof(sa), &iov, 1, nullptr, 0, 0 };
return sendmsg(fd, &msg, 0);
return sendmsg(nl_socket_get_fd(info_sock_), &msg, 0);
}
int waybar::modules::Network::netlinkResponse(int fd, void *resp,
int waybar::modules::Network::netlinkResponse(void *resp,
uint32_t resplen, uint32_t groups)
{
int ret;
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 };
ret = recvmsg(fd, &msg, 0);
auto ret = recvmsg(nl_socket_get_fd(info_sock_), &msg, 0);
if (msg.msg_flags & MSG_TRUNC) {
return -1;
}
return ret;
}
int waybar::modules::Network::scanCb(struct nl_msg *msg, void *data) {
int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) {
auto net = static_cast<waybar::modules::Network *>(data);
bool need_update = false;
nlmsghdr *nh = nlmsg_hdr(msg);
if (nh->nlmsg_type == RTM_NEWADDR) {
need_update = true;
}
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();
}
}
}
if (net->ifid_ <= 0 && !net->config_["interface"].isString()) {
// Need to wait before get external interface
net->thread_.sleep_for(std::chrono::seconds(1));
net->ifid_ = net->getExternalInterface();
if (net->ifid_ > 0) {
char ifname[IF_NAMESIZE];
if_indextoname(net->ifid_, ifname);
net->ifname_ = ifname;
need_update = true;
}
}
if (need_update) {
if (net->ifid_ > 0) {
net->getInfo();
}
net->dp.emit();
}
return NL_SKIP;
}
int waybar::modules::Network::handleScan(struct nl_msg *msg, void *data) {
auto net = static_cast<waybar::modules::Network *>(data);
auto gnlh = static_cast<genlmsghdr *>(nlmsg_data(nlmsg_hdr(msg)));
struct nlattr* tb[NL80211_ATTR_MAX + 1];
@ -388,9 +458,8 @@ void waybar::modules::Network::parseEssid(struct nlattr **bss)
void waybar::modules::Network::parseSignal(struct nlattr **bss) {
if (bss[NL80211_BSS_SIGNAL_MBM] != nullptr) {
// signalstrength in dBm
signal_strength_dbm_ =
static_cast<int>(nla_get_u32(bss[NL80211_BSS_SIGNAL_MBM])) / 100;
// signalstrength in dBm from mBm
signal_strength_dbm_ = nla_get_s32(bss[NL80211_BSS_SIGNAL_MBM]) / 100;
// WiFi-hardware usually operates in the range -90 to -20dBm.
const int hardwareMax = -20;
@ -398,6 +467,9 @@ void waybar::modules::Network::parseSignal(struct nlattr **bss) {
signal_strength_ = ((signal_strength_dbm_ - hardwareMin)
/ double{hardwareMax - hardwareMin}) * 100;
}
if (bss[NL80211_BSS_SIGNAL_UNSPEC] != nullptr) {
signal_strength_ = nla_get_u8(bss[NL80211_BSS_SIGNAL_UNSPEC]);
}
}
bool waybar::modules::Network::associatedOrJoined(struct nlattr** bss)
@ -418,9 +490,9 @@ 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) {
nl_socket_free(sk_);
return;
}
if (genlmsg_put(nl_msg, NL_AUTO_PORT, NL_AUTO_SEQ, nl80211_id_, 0, NLM_F_DUMP,

View File

@ -1,6 +1,6 @@
#include "modules/pulseaudio.hpp"
waybar::modules::Pulseaudio::Pulseaudio(const Json::Value &config)
waybar::modules::Pulseaudio::Pulseaudio(const std::string& id, const Json::Value &config)
: ALabel(config, "{volume}%"),
mainloop_(nullptr),
mainloop_api_(nullptr),
@ -10,6 +10,9 @@ waybar::modules::Pulseaudio::Pulseaudio(const Json::Value &config)
muted_(false),
scrolling_(false) {
label_.set_name("pulseaudio");
if (!id.empty()) {
label_.get_style_context()->add_class(id);
}
mainloop_ = pa_threaded_mainloop_new();
if (mainloop_ == nullptr) {
throw std::runtime_error("pa_mainloop_new() failed.");
@ -189,7 +192,7 @@ const std::string waybar::modules::Pulseaudio::getPortIcon() const
return port;
}
}
return "";
return port_name_;
}
auto waybar::modules::Pulseaudio::update() -> void
@ -207,7 +210,7 @@ auto waybar::modules::Pulseaudio::update() -> void
label_.get_style_context()->remove_class("muted");
label_.get_style_context()->add_class("bluetooth");
}
label_.set_label(
label_.set_markup(
fmt::format(format, fmt::arg("volume", volume_),
fmt::arg("icon", getIcon(volume_, getPortIcon()))));
label_.set_tooltip_text(desc_);

View File

@ -2,8 +2,8 @@
#include <iostream>
waybar::modules::SNI::Tray::Tray(Bar& bar, const Json::Value &config)
: config_(config), watcher_(), host_(bar.nb_tray_hosts, config,
waybar::modules::SNI::Tray::Tray(const std::string& id, const Json::Value &config)
: config_(config), watcher_(), host_(nb_hosts_, config,
std::bind(&Tray::onAdd, this, std::placeholders::_1),
std::bind(&Tray::onRemove, this, std::placeholders::_1))
{
@ -11,7 +11,7 @@ waybar::modules::SNI::Tray::Tray(Bar& bar, const Json::Value &config)
if (config_["spacing"].isUInt()) {
box_.set_spacing(config_["spacing"].asUInt());
}
bar.nb_tray_hosts += 1;
nb_hosts_ += 1;
}
void waybar::modules::SNI::Tray::onAdd(std::unique_ptr<Item>& item)

View File

@ -1,11 +1,18 @@
#include "modules/sway/ipc/client.hpp"
waybar::modules::sway::Ipc::Ipc()
: fd_(-1), fd_event_(-1)
{}
{
const std::string& socketPath = getSocketPath();
fd_ = open(socketPath);
fd_event_ = open(socketPath);
}
waybar::modules::sway::Ipc::~Ipc()
{
// To fail the IPC header
write(fd_, "close-sway-ipc", 14);
write(fd_event_, "close-sway-ipc", 14);
close(fd_);
close(fd_event_);
}
@ -53,13 +60,6 @@ int waybar::modules::sway::Ipc::open(const std::string& socketPath) const
return fd;
}
void waybar::modules::sway::Ipc::connect()
{
const std::string& socketPath = getSocketPath();
fd_ = open(socketPath);
fd_event_ = open(socketPath);
}
struct waybar::modules::sway::Ipc::ipc_response
waybar::modules::sway::Ipc::recv(int fd) const
{
@ -69,22 +69,25 @@ struct waybar::modules::sway::Ipc::ipc_response
size_t total = 0;
while (total < ipc_header_size_) {
ssize_t res =
::recv(fd, header.data() + total, ipc_header_size_ - total, 0);
auto res = ::recv(fd, header.data() + total, ipc_header_size_ - total, 0);
if (res <= 0) {
throw std::runtime_error("Unable to receive IPC response");
throw std::runtime_error("Unable to receive IPC header");
}
total += res;
}
auto magic = std::string(header.data(), header.data() + ipc_magic_.size());
if (magic != ipc_magic_) {
throw std::runtime_error("Invalid IPC magic");
}
total = 0;
std::string payload;
payload.reserve(data32[0] + 1);
while (total < data32[0]) {
ssize_t res =
::recv(fd, payload.data() + total, data32[0] - total, 0);
auto res = ::recv(fd, payload.data() + total, data32[0] - total, 0);
if (res < 0) {
throw std::runtime_error("Unable to receive IPC response");
throw std::runtime_error("Unable to receive IPC payload");
}
total += res;
}
@ -131,4 +134,4 @@ struct waybar::modules::sway::Ipc::ipc_response
waybar::modules::sway::Ipc::handleEvent() const
{
return recv(fd_event_);
}
}

View File

@ -1,12 +1,16 @@
#include "modules/sway/mode.hpp"
waybar::modules::sway::Mode::Mode(Bar& bar, const Json::Value& config)
waybar::modules::sway::Mode::Mode(const std::string& id, const Bar& bar, const Json::Value& config)
: ALabel(config, "{}"), bar_(bar)
{
ipc_.connect();
label_.set_name("mode");
if (!id.empty()) {
label_.get_style_context()->add_class(id);
}
ipc_.subscribe("[ \"mode\" ]");
// Launch worker
worker();
dp.emit();
}
void waybar::modules::sway::Mode::worker()
@ -15,16 +19,14 @@ void waybar::modules::sway::Mode::worker()
try {
auto res = ipc_.handleEvent();
auto parsed = parser_.parse(res.payload);
if ((parsed["change"]) != "default" ) {
if (parsed["change"] != "default") {
mode_ = parsed["change"].asString();
dp.emit();
}
else if ((parsed["change"]) == "default" ) {
} else {
mode_.clear();
dp.emit();
}
dp.emit();
} catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
std::cerr << "Mode: " << e.what() << std::endl;
}
};
}
@ -32,12 +34,10 @@ void waybar::modules::sway::Mode::worker()
auto waybar::modules::sway::Mode::update() -> void
{
if (mode_.empty()) {
label_.set_name("");
label_.hide();
event_box_.hide();
} else {
label_.set_name("mode");
label_.set_markup(fmt::format(format_, mode_));
label_.set_tooltip_text(mode_);
label_.show();
event_box_.show();
}
}

View File

@ -1,14 +1,16 @@
#include "modules/sway/window.hpp"
waybar::modules::sway::Window::Window(Bar &bar, const Json::Value& config)
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);
}
if (label_.get_max_width_chars() == -1) {
label_.set_hexpand(true);
label_.set_ellipsize(Pango::EllipsizeMode::ELLIPSIZE_END);
}
ipc_.connect();
ipc_.subscribe("[\"window\",\"workspace\"]");
getFocusedWindow();
// Launch worker
@ -21,9 +23,11 @@ void waybar::modules::sway::Window::worker()
try {
auto res = ipc_.handleEvent();
auto parsed = parser_.parse(res.payload);
// Check for waybar prevents flicker when hovering window module
if ((parsed["change"] == "focus" || parsed["change"] == "title")
&& parsed["container"]["focused"].asBool()) {
window_ = parsed["container"]["name"].asString();
&& parsed["container"]["focused"].asBool()
&& parsed["container"]["name"].asString() != "waybar") {
window_ = Glib::Markup::escape_text(parsed["container"]["name"].asString());
windowId_ = parsed["container"]["id"].asInt();
dp.emit();
} else if ((parsed["change"] == "close"
@ -36,7 +40,7 @@ void waybar::modules::sway::Window::worker()
dp.emit();
}
} catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
std::cerr << "Window: " << e.what() << std::endl;
}
};
}

View File

@ -1,11 +1,13 @@
#include "modules/sway/workspaces.hpp"
waybar::modules::sway::Workspaces::Workspaces(Bar& bar,
waybar::modules::sway::Workspaces::Workspaces(const std::string& id, const Bar& bar,
const Json::Value& config)
: bar_(bar), config_(config), scrolling_(false)
{
box_.set_name("workspaces");
ipc_.connect();
if (!id.empty()) {
box_.get_style_context()->add_class(id);
}
ipc_.subscribe("[ \"workspace\" ]");
// Launch worker
worker();
@ -18,7 +20,7 @@ void waybar::modules::sway::Workspaces::worker()
// Wait for the name of the output
if (!config_["all-outputs"].asBool() && bar_.output_name.empty()) {
while (bar_.output_name.empty()) {
thread_.sleep_for(chrono::milliseconds(150));
thread_.sleep_for(std::chrono::milliseconds(150));
}
} else if (!workspaces_.empty()) {
ipc_.handleEvent();
@ -26,11 +28,13 @@ void waybar::modules::sway::Workspaces::worker()
{
std::lock_guard<std::mutex> lock(mutex_);
auto res = ipc_.sendCmd(IPC_GET_WORKSPACES);
workspaces_ = parser_.parse(res.payload);
if (thread_.isRunning()) {
workspaces_ = parser_.parse(res.payload);
}
}
dp.emit();
} catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
std::cerr << "Workspaces: " << e.what() << std::endl;
}
};
}
@ -84,7 +88,7 @@ auto waybar::modules::sway::Workspaces::update() -> void
if (config_["format"].isString()) {
auto format = config_["format"].asString();
button.set_label(fmt::format(format, fmt::arg("icon", icon),
fmt::arg("name", node["name"].asString()),
fmt::arg("name", trimWorkspaceName(node["name"].asString())),
fmt::arg("index", node["num"].asString())));
} else {
button.set_label(icon);
@ -102,7 +106,7 @@ void waybar::modules::sway::Workspaces::addWorkspace(Json::Value node)
auto icon = getIcon(node["name"].asString(), node);
auto format = config_["format"].isString()
? fmt::format(config_["format"].asString(), fmt::arg("icon", icon),
fmt::arg("name", node["name"].asString()),
fmt::arg("name", trimWorkspaceName(node["name"].asString())),
fmt::arg("index", node["num"].asString()))
: icon;
auto pair = buttons_.emplace(node["name"].asString(), format);
@ -232,3 +236,12 @@ std::string waybar::modules::sway::Workspaces::getNextWorkspace()
waybar::modules::sway::Workspaces::operator Gtk::Widget &() {
return box_;
}
std::string waybar::modules::sway::Workspaces::trimWorkspaceName(std::string name)
{
std::size_t found = name.find(":");
if (found!=std::string::npos) {
return name.substr(found+1);
}
return name;
}