Compare commits

..

20 Commits
0.0.5 ... 0.0.7

Author SHA1 Message Date
55e1905284 fix(Sway): compile without sway 2018-09-10 11:25:53 +02:00
0abaaf2f7f style: fix 2018-09-10 11:16:57 +02:00
f78ef0d491 fix(Meson): optional sway 2018-09-10 11:00:53 +02:00
de5df09fcd fix(Custom): loop script block main loop 2018-09-05 19:20:19 +02:00
7020af7653 feat(Workspaces): urgent, visible, focused icons 2018-09-05 00:16:56 +02:00
28c65c64e6 chore: add default build type 2018-08-30 11:30:20 +02:00
4f75d5e33b fix: config 2018-08-30 00:04:43 +02:00
aa05304139 feat(Pulseadio): config icons 2018-08-29 23:54:23 +02:00
6dd9b5ccc4 feat(Pulseadio): port icons 2018-08-29 23:50:41 +02:00
d0933ab50f fix(thread): check before detach 2018-08-29 21:07:58 +02:00
9a1b8bb831 fix(Custom): only set id when getting an output 2018-08-28 11:10:36 +02:00
53956d9d18 feat(ALabel): Toggleable labels 2018-08-27 01:36:25 +02:00
e9478f548e chore: add mediaplayer script 2018-08-26 21:47:35 +02:00
c8ca8b3725 fix(Custom): hide label when exec-if failed 2018-08-26 21:41:34 +02:00
0ad2bc7516 refactor(Network): clean nl socket 2018-08-24 15:32:06 +02:00
0dba3abc1d fix(custom): do not take the custom module ref 2018-08-21 10:50:09 +02:00
8be67d5008 chore: optional deps 2018-08-20 17:20:02 +02:00
49232eed8d Clean (#31) 2018-08-20 14:50:45 +02:00
b7e3d10fb7 revert(workspaces): ipc command out of update func 2018-08-20 00:19:27 +02:00
8ce33e0c64 fix(window): pick only con title 2018-08-19 20:37:33 +02:00
36 changed files with 577 additions and 398 deletions

View File

@ -7,13 +7,20 @@ namespace waybar {
class ALabel : public IModule { class ALabel : public IModule {
public: public:
ALabel(Json::Value); ALabel(const Json::Value&, const std::string format);
virtual ~ALabel() = default; virtual ~ALabel() = default;
virtual auto update() -> void; virtual auto update() -> void;
virtual std::string getIcon(uint16_t, const std::string& alt = "");
virtual operator Gtk::Widget &(); virtual operator Gtk::Widget &();
protected: protected:
Gtk::EventBox event_box_;
Gtk::Label label_; Gtk::Label label_;
Json::Value config_; const Json::Value& config_;
std::string format_;
private:
bool handleToggle(GdkEventButton* const& ev);
bool alt = false;
const std::string default_format_;
}; };
} }

View File

@ -6,9 +6,10 @@ namespace waybar {
class IModule { class IModule {
public: public:
virtual ~IModule() {} virtual ~IModule() = default;
virtual auto update() -> void = 0; virtual auto update() -> void = 0;
virtual operator Gtk::Widget &() = 0; virtual operator Gtk::Widget &() = 0;
Glib::Dispatcher dp; // Hmmm Maybe I should create an abstract class ?
}; };
} }

View File

@ -13,12 +13,12 @@ class Factory;
class Bar { class Bar {
public: public:
Bar(Client&, std::unique_ptr<struct wl_output *>&&, uint32_t wl_name); Bar(const Client&, std::unique_ptr<struct wl_output *>&&, uint32_t);
Bar(const Bar&) = delete; Bar(const Bar&) = delete;
auto toggle() -> void; auto toggle() -> void;
Client& client; const Client& client;
Gtk::Window window; Gtk::Window window;
struct wl_surface *surface; struct wl_surface *surface;
struct zwlr_layer_surface_v1 *layer_surface; struct zwlr_layer_surface_v1 *layer_surface;
@ -43,7 +43,7 @@ class Bar {
auto setupConfig() -> void; auto setupConfig() -> void;
auto setupWidgets() -> void; auto setupWidgets() -> void;
auto setupCss() -> void; auto setupCss() -> void;
void getModules(Factory factory, const std::string& pos); void getModules(const Factory&, const std::string&);
uint32_t width_ = 0; uint32_t width_ = 0;
uint32_t height_ = 30; uint32_t height_ = 30;

View File

@ -2,24 +2,32 @@
#include <json/json.h> #include <json/json.h>
#include "modules/clock.hpp" #include "modules/clock.hpp"
#ifdef HAVE_SWAY
#include "modules/sway/workspaces.hpp" #include "modules/sway/workspaces.hpp"
#include "modules/sway/window.hpp" #include "modules/sway/window.hpp"
#endif
#include "modules/battery.hpp" #include "modules/battery.hpp"
#include "modules/memory.hpp" #include "modules/memory.hpp"
#include "modules/cpu.hpp" #include "modules/cpu.hpp"
#ifdef HAVE_LIBNL
#include "modules/network.hpp" #include "modules/network.hpp"
#endif
#ifdef HAVE_LIBPULSE
#include "modules/pulseaudio.hpp" #include "modules/pulseaudio.hpp"
#endif
#include "modules/custom.hpp" #include "modules/custom.hpp"
namespace waybar { namespace waybar {
class Bar;
class Factory { class Factory {
public: public:
Factory(Bar &bar, Json::Value config); Factory(Bar& bar, const Json::Value& config);
IModule* makeModule(const std::string &name); IModule* makeModule(const std::string &name) const;
private: private:
Bar &_bar; Bar& bar_;
Json::Value _config; const Json::Value& config_;
}; };
} }

View File

@ -15,13 +15,13 @@ namespace fs = std::filesystem;
class Battery : public ALabel { class Battery : public ALabel {
public: public:
Battery(Json::Value); Battery(const Json::Value&);
~Battery(); ~Battery();
auto update() -> void; auto update() -> void;
private: private:
std::string getIcon(uint16_t percentage);
static inline const fs::path data_dir_ = "/sys/class/power_supply/"; static inline const fs::path data_dir_ = "/sys/class/power_supply/";
void worker();
util::SleeperThread thread_; util::SleeperThread thread_;
std::vector<fs::path> batteries_; std::vector<fs::path> batteries_;

View File

@ -9,7 +9,7 @@ namespace waybar::modules {
class Clock : public ALabel { class Clock : public ALabel {
public: public:
Clock(Json::Value); Clock(const Json::Value&);
auto update() -> void; auto update() -> void;
private: private:
waybar::util::SleeperThread thread_; waybar::util::SleeperThread thread_;

View File

@ -9,7 +9,7 @@ namespace waybar::modules {
class Cpu : public ALabel { class Cpu : public ALabel {
public: public:
Cpu(Json::Value); Cpu(const Json::Value&);
auto update() -> void; auto update() -> void;
private: private:
waybar::util::SleeperThread thread_; waybar::util::SleeperThread thread_;

View File

@ -10,11 +10,14 @@ namespace waybar::modules {
class Custom : public ALabel { class Custom : public ALabel {
public: public:
Custom(std::string, Json::Value); Custom(const std::string, const Json::Value&);
auto update() -> void; auto update() -> void;
private: private:
std::string name_; void worker();
const std::string name_;
waybar::util::SleeperThread thread_; waybar::util::SleeperThread thread_;
waybar::util::command::res output_;
}; };
} }

View File

@ -9,7 +9,7 @@ namespace waybar::modules {
class Memory : public ALabel { class Memory : public ALabel {
public: public:
Memory(Json::Value); Memory(const Json::Value&);
auto update() -> void; auto update() -> void;
private: private:
waybar::util::SleeperThread thread_; waybar::util::SleeperThread thread_;

View File

@ -13,15 +13,16 @@ namespace waybar::modules {
class Network : public ALabel { class Network : public ALabel {
public: public:
Network(Json::Value); Network(const Json::Value&);
~Network(); ~Network();
auto update() -> void; auto update() -> void;
private: private:
static uint64_t netlinkRequest(int, void*, uint32_t, uint32_t groups = 0); static int netlinkRequest(int, void*, uint32_t, uint32_t groups = 0);
static uint64_t netlinkResponse(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 scanCb(struct nl_msg*, void*);
void disconnected(); void disconnected();
void initNL80211();
int getExternalInterface(); int getExternalInterface();
void parseEssid(struct nlattr**); void parseEssid(struct nlattr**);
void parseSignal(struct nlattr**); void parseSignal(struct nlattr**);
@ -32,7 +33,9 @@ class Network : public ALabel {
int ifid_; int ifid_;
sa_family_t family_; sa_family_t family_;
int sock_fd_; int sock_fd_;
struct sockaddr_nl nladdr_ = {0}; struct sockaddr_nl nladdr_ = {};
struct nl_sock* sk_ = nullptr;
int nl80211_id_;
std::string essid_; std::string essid_;
std::string ifname_; std::string ifname_;

View File

@ -9,7 +9,7 @@ namespace waybar::modules {
class Pulseaudio : public ALabel { class Pulseaudio : public ALabel {
public: public:
Pulseaudio(Json::Value); Pulseaudio(const Json::Value&);
~Pulseaudio(); ~Pulseaudio();
auto update() -> void; auto update() -> void;
private: private:
@ -19,7 +19,7 @@ class Pulseaudio : public ALabel {
static void sinkInfoCb(pa_context*, const pa_sink_info*, int, void*); static void sinkInfoCb(pa_context*, const pa_sink_info*, int, void*);
static void serverInfoCb(pa_context*, const pa_server_info*, void*); static void serverInfoCb(pa_context*, const pa_server_info*, void*);
std::string getIcon(uint16_t); const std::string getPortIcon() const;
pa_threaded_mainloop* mainloop_; pa_threaded_mainloop* mainloop_;
pa_mainloop_api* mainloop_api_; pa_mainloop_api* mainloop_api_;
@ -27,6 +27,7 @@ class Pulseaudio : public ALabel {
uint32_t sink_idx_{0}; uint32_t sink_idx_{0};
uint16_t volume_; uint16_t volume_;
bool muted_; bool muted_;
std::string port_name_;
std::string desc_; std::string desc_;
}; };

View File

@ -1,33 +1,41 @@
#pragma once #pragma once
#include <iostream> #include <iostream>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>
#include "ipc.hpp" #include "ipc.hpp"
/** namespace waybar::modules::sway {
* IPC response including type of IPC response, size of payload and the json
* encoded payload string. class Ipc {
*/ public:
struct ipc_response { Ipc();
uint32_t size; ~Ipc();
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;
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_;
}; };
/** }
* Gets the path to the IPC socket from sway.
*/
std::string getSocketPath(void);
/**
* Opens the sway socket.
*/
int ipcOpenSocket(const std::string &socketPath);
/**
* Issues a single IPC command and returns the buffer. len will be updated with
* the length of the buffer returned from sway.
*/
struct ipc_response ipcSingleCommand(int socketfd, uint32_t type,
const std::string& payload);
/**
* Receives a single IPC response and returns an ipc_response.
*/
struct ipc_response ipcRecvResponse(int socketfd);

View File

@ -1,6 +1,6 @@
#pragma once #pragma once
#define event_mask(ev) (1 << (ev & 0x7F)) #define event_mask(ev) (1u << (ev & 0x7F))
enum ipc_command_type { enum ipc_command_type {
// i3 command types - see i3's I3_REPLY_TYPE constants // i3 command types - see i3's I3_REPLY_TYPE constants

View File

@ -6,23 +6,23 @@
#include "util/chrono.hpp" #include "util/chrono.hpp"
#include "util/json.hpp" #include "util/json.hpp"
#include "ALabel.hpp" #include "ALabel.hpp"
#include "modules/sway/ipc/client.hpp"
namespace waybar::modules::sway { namespace waybar::modules::sway {
class Window : public ALabel { class Window : public ALabel {
public: public:
Window(waybar::Bar&, Json::Value); Window(waybar::Bar&, const Json::Value&);
~Window();
auto update() -> void; auto update() -> void;
private: private:
void worker();
std::string getFocusedNode(Json::Value nodes); std::string getFocusedNode(Json::Value nodes);
void getFocusedWindow(); void getFocusedWindow();
Bar& bar_; Bar& bar_;
waybar::util::SleeperThread thread_; waybar::util::SleeperThread thread_;
util::JsonParser parser_; util::JsonParser parser_;
int ipcfd_; Ipc ipc_;
int ipc_eventfd_;
std::string window_; std::string window_;
}; };

View File

@ -6,24 +6,25 @@
#include "util/chrono.hpp" #include "util/chrono.hpp"
#include "util/json.hpp" #include "util/json.hpp"
#include "IModule.hpp" #include "IModule.hpp"
#include "modules/sway/ipc/client.hpp"
namespace waybar::modules::sway { namespace waybar::modules::sway {
class Workspaces : public IModule { class Workspaces : public IModule {
public: public:
Workspaces(waybar::Bar&, Json::Value); Workspaces(waybar::Bar&, const Json::Value&);
~Workspaces();
auto update() -> void; auto update() -> void;
operator Gtk::Widget &(); operator Gtk::Widget &();
private: private:
void worker();
void addWorkspace(Json::Value); void addWorkspace(Json::Value);
std::string getIcon(std::string); std::string getIcon(std::string, Json::Value);
bool handleScroll(GdkEventScroll*); bool handleScroll(GdkEventScroll*);
int getPrevWorkspace(); int getPrevWorkspace();
int getNextWorkspace(); int getNextWorkspace();
Bar& bar_; Bar& bar_;
Json::Value config_; const Json::Value& config_;
waybar::util::SleeperThread thread_; waybar::util::SleeperThread thread_;
Gtk::Box box_; Gtk::Box box_;
util::JsonParser parser_; util::JsonParser parser_;
@ -31,8 +32,7 @@ class Workspaces : public IModule {
bool scrolling_; bool scrolling_;
std::unordered_map<int, Gtk::Button> buttons_; std::unordered_map<int, Gtk::Button> buttons_;
Json::Value workspaces_; Json::Value workspaces_;
int ipcfd_; Ipc ipc_;
int ipc_eventfd_;
}; };
} }

View File

@ -74,15 +74,11 @@ struct SleeperThread {
{ {
do_run_ = false; do_run_ = false;
condvar_.notify_all(); condvar_.notify_all();
auto native_handle = thread_.native_handle();
pthread_cancel(native_handle);
if (thread_.joinable()) { if (thread_.joinable()) {
thread_.join(); thread_.detach();
} }
} }
sigc::signal<void> sig_update;
private: private:
std::thread thread_; std::thread thread_;
std::condition_variable condvar_; std::condition_variable condvar_;

View File

@ -4,12 +4,12 @@
namespace waybar::util::command { namespace waybar::util::command {
struct cmd_res { struct res {
int exit_code; int exit_code;
std::string out; std::string out;
}; };
inline struct cmd_res exec(const std::string cmd) inline struct res exec(const std::string cmd)
{ {
FILE* fp(popen(cmd.c_str(), "r")); FILE* fp(popen(cmd.c_str(), "r"));
if (!fp) { if (!fp) {

View File

@ -7,32 +7,25 @@ namespace waybar::util {
struct JsonParser { struct JsonParser {
JsonParser() JsonParser()
: _reader(_builder.newCharReader()) : reader_(builder_.newCharReader())
{} {}
Json::Value parse(const std::string data) const Json::Value parse(const std::string data) const
{ {
Json::Value root; Json::Value root;
std::string err; std::string err;
if (_reader == nullptr) {
throw std::runtime_error("Unable to parse");
}
bool res = bool res =
_reader->parse(data.c_str(), data.c_str() + data.size(), &root, &err); reader_->parse(data.c_str(), data.c_str() + data.size(), &root, &err);
if (!res) if (!res)
throw std::runtime_error(err); throw std::runtime_error(err);
return root; return root;
} }
~JsonParser() ~JsonParser() = default;
{
delete _reader;
_reader = nullptr;
}
private: private:
Json::CharReaderBuilder _builder; Json::CharReaderBuilder builder_;
Json::CharReader *_reader; std::unique_ptr<Json::CharReader> const reader_;
}; };
} }

View File

@ -2,7 +2,10 @@ project(
'waybar', 'cpp', 'c', 'waybar', 'cpp', 'c',
version: '0.0.5', version: '0.0.5',
license: 'MIT', license: 'MIT',
default_options : ['cpp_std=c++17'], default_options : [
'cpp_std=c++17',
'buildtype=release'
],
) )
cpp_args = [] cpp_args = []
@ -31,14 +34,44 @@ wlroots = dependency('wlroots', fallback: ['wlroots', 'wlroots'])
gtkmm = dependency('gtkmm-3.0') gtkmm = dependency('gtkmm-3.0')
jsoncpp = dependency('jsoncpp') jsoncpp = dependency('jsoncpp')
sigcpp = dependency('sigc++-2.0') sigcpp = dependency('sigc++-2.0')
libnl = dependency('libnl-3.0') libnl = dependency('libnl-3.0', required: false)
libnlgen = dependency('libnl-genl-3.0') libnlgen = dependency('libnl-genl-3.0', required: false)
libpulse = dependency('libpulse') libpulse = dependency('libpulse', required: false)
src_files = files(
'src/factory.cpp',
'src/ALabel.cpp',
'src/modules/memory.cpp',
'src/modules/battery.cpp',
'src/modules/clock.cpp',
'src/modules/custom.cpp',
'src/modules/cpu.cpp',
'src/main.cpp',
'src/bar.cpp',
'src/client.cpp'
)
if find_program('sway', required : false).found()
add_project_arguments('-DHAVE_SWAY', language: 'cpp')
src_files += [
'src/modules/sway/ipc/client.cpp',
'src/modules/sway/window.cpp',
'src/modules/sway/workspaces.cpp'
]
endif
if libnl.found() and libnlgen.found()
add_project_arguments('-DHAVE_LIBNL', language: 'cpp')
src_files += 'src/modules/network.cpp'
endif
if libpulse.found()
add_project_arguments('-DHAVE_LIBPULSE', language: 'cpp')
src_files += 'src/modules/pulseaudio.cpp'
endif
subdir('protocol') subdir('protocol')
src_files = run_command('find', './src', '-name', '*.cpp').stdout().strip().split('\n')
executable( executable(
'waybar', 'waybar',
src_files, src_files,

View File

@ -1,5 +1,5 @@
{ {
// "layer": "top", // Waybar at top layer "layer": "top", // Waybar at top layer
// "position": "bottom", // Waybar at the bottom of your screen // "position": "bottom", // Waybar at the bottom of your screen
// "height": 30, // Waybar height // "height": 30, // Waybar height
// "width": 1280, // Waybar width // "width": 1280, // Waybar width
@ -8,20 +8,26 @@
"modules-center": ["sway/window"], "modules-center": ["sway/window"],
"modules-right": ["pulseaudio", "network", "cpu", "memory", "battery", "clock"], "modules-right": ["pulseaudio", "network", "cpu", "memory", "battery", "clock"],
// Modules configuration // Modules configuration
"sway/workspaces": { // "sway/workspaces": {
// "disable-scroll": true, // "disable-scroll": true,
// "all-outputs": true, // "all-outputs": true,
// "format-icons": { // "format-icons": {
// "1": "", // "1": "",
// "2": "", // "2": "",
// "3": "", // "3": "",
// "4": "", // "4": "",
// "5": "" // "5": "",
// } // "urgent": "",
}, // "focused": "",
// "default": ""
// }
// },
"sway/window": { "sway/window": {
"max-length": 50 "max-length": 50
}, },
"clock": {
"format-alt": "{:%Y-%m-%d}"
},
"cpu": { "cpu": {
"format": "{}% " "format": "{}% "
}, },
@ -40,13 +46,22 @@
}, },
"pulseaudio": { "pulseaudio": {
"format": "{volume}% {icon}", "format": "{volume}% {icon}",
"format-bluetooth": "{volume}% {icon}",
"format-muted": "", "format-muted": "",
"format-icons": ["", ""] "format-icons": {
"headphones": "",
"handsfree": "",
"headset": "",
"phone": "",
"portable": "",
"car": "",
"default": ["", ""]
}
}, },
"custom/spotify": { "custom/spotify": {
"format": " {}", "format": " {}",
"max-length": 40, "max-length": 40,
"exec": "$HOME/.bin/mediaplayer.sh", "exec": "$HOME/.config/waybar/mediaplayer.sh", // Script in resources folder
"exec-if": "pgrep spotify" "exec-if": "pgrep spotify"
} }
} }

7
resources/mediaplayer.sh Executable file
View File

@ -0,0 +1,7 @@
#!/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

@ -1,12 +1,22 @@
#include "ALabel.hpp" #include "ALabel.hpp"
waybar::ALabel::ALabel(Json::Value config) #include <iostream>
: config_(std::move(config))
waybar::ALabel::ALabel(const Json::Value& config, const std::string format)
: config_(config),
format_(config_["format"] ? config_["format"].asString() : format),
default_format_(format_)
{ {
event_box_.add(label_);
if (config_["max-length"]) { if (config_["max-length"]) {
label_.set_max_width_chars(config_["max-length"].asUInt()); label_.set_max_width_chars(config_["max-length"].asUInt());
label_.set_ellipsize(Pango::EllipsizeMode::ELLIPSIZE_END); label_.set_ellipsize(Pango::EllipsizeMode::ELLIPSIZE_END);
} }
if (config_["format-alt"]) {
event_box_.add_events(Gdk::BUTTON_PRESS_MASK);
event_box_.signal_button_press_event()
.connect(sigc::mem_fun(*this, &ALabel::handleToggle));
}
} }
auto waybar::ALabel::update() -> void auto waybar::ALabel::update() -> void
@ -14,6 +24,39 @@ auto waybar::ALabel::update() -> void
// Nothing here // Nothing here
} }
waybar::ALabel::operator Gtk::Widget &() { bool waybar::ALabel::handleToggle(GdkEventButton* const& /*ev*/)
return label_; {
alt = !alt;
if (alt) {
format_ = config_["format-alt"].asString();
} else {
format_ = default_format_;
}
dp.emit();
return true;
}
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]) {
format_icons = format_icons[alt];
} else {
format_icons = format_icons["default"];
}
}
if (format_icons.isArray()) {
auto size = format_icons.size();
auto idx = std::clamp(percentage / (100 / size), 0U, size - 1);
format_icons = format_icons[idx];
}
if (format_icons.isString()) {
return format_icons.asString();
}
return "";
}
waybar::ALabel::operator Gtk::Widget &() {
return event_box_;
} }

View File

@ -3,10 +3,11 @@
#include "factory.hpp" #include "factory.hpp"
#include "util/json.hpp" #include "util/json.hpp"
waybar::Bar::Bar(Client &client, waybar::Bar::Bar(const Client& client,
std::unique_ptr<struct wl_output *> &&p_output, uint32_t p_wl_name) std::unique_ptr<struct wl_output *> &&p_output, uint32_t p_wl_name)
: client(client), window{Gtk::WindowType::WINDOW_TOPLEVEL}, : client(client), window{Gtk::WindowType::WINDOW_TOPLEVEL},
output(std::move(p_output)), wl_name(std::move(p_wl_name)) surface(nullptr), layer_surface(nullptr),
output(std::move(p_output)), wl_name(p_wl_name)
{ {
static const struct zxdg_output_v1_listener xdgOutputListener = { static const struct zxdg_output_v1_listener xdgOutputListener = {
.logical_position = handleLogicalPosition, .logical_position = handleLogicalPosition,
@ -135,13 +136,13 @@ auto waybar::Bar::toggle() -> void
auto waybar::Bar::setupConfig() -> void auto waybar::Bar::setupConfig() -> void
{ {
util::JsonParser parser;
std::ifstream file(client.config_file); std::ifstream file(client.config_file);
if (!file.is_open()) { if (!file.is_open()) {
throw std::runtime_error("Can't open config file"); throw std::runtime_error("Can't open config file");
} }
std::string str((std::istreambuf_iterator<char>(file)), std::string str((std::istreambuf_iterator<char>(file)),
std::istreambuf_iterator<char>()); std::istreambuf_iterator<char>());
util::JsonParser parser;
config_ = parser.parse(str); config_ = parser.parse(str);
} }
@ -150,7 +151,7 @@ auto waybar::Bar::setupCss() -> void
css_provider_ = Gtk::CssProvider::create(); css_provider_ = Gtk::CssProvider::create();
style_context_ = Gtk::StyleContext::create(); style_context_ = Gtk::StyleContext::create();
// load our css file, wherever that may be hiding // Load our css file, wherever that may be hiding
if (css_provider_->load_from_path(client.css_file)) { if (css_provider_->load_from_path(client.css_file)) {
Glib::RefPtr<Gdk::Screen> screen = window.get_screen(); Glib::RefPtr<Gdk::Screen> screen = window.get_screen();
style_context_->add_provider_for_screen(screen, css_provider_, style_context_->add_provider_for_screen(screen, css_provider_,
@ -158,20 +159,22 @@ auto waybar::Bar::setupCss() -> void
} }
} }
void waybar::Bar::getModules(Factory factory, const std::string& pos) void waybar::Bar::getModules(const Factory& factory, const std::string& pos)
{ {
if (config_[pos]) { if (config_[pos]) {
for (const auto &name : config_[pos]) { for (const auto &name : config_[pos]) {
try { try {
auto module = factory.makeModule(name.asString());
if (pos == "modules-left") { if (pos == "modules-left") {
modules_left_.emplace_back(factory.makeModule(name.asString())); modules_left_.emplace_back(module);
} }
if (pos == "modules-center") { if (pos == "modules-center") {
modules_center_.emplace_back(factory.makeModule(name.asString())); modules_center_.emplace_back(module);
} }
if (pos == "modules-right") { if (pos == "modules-right") {
modules_right_.emplace_back(factory.makeModule(name.asString())); modules_right_.emplace_back(module);
} }
module->dp.connect([module] { module->update(); });
} catch (const std::exception& e) { } catch (const std::exception& e) {
std::cerr << e.what() << std::endl; std::cerr << e.what() << std::endl;
} }

View File

@ -1,10 +1,16 @@
#include "client.hpp" #include "client.hpp"
waybar::Client::Client(int argc, char* argv[]) waybar::Client::Client(int argc, char* argv[])
: gtk_app(Gtk::Application::create(argc, argv, "org.alexays.waybar")), : gtk_app(Gtk::Application::create(argc, argv, "fr.arouillard.waybar")),
gdk_display(Gdk::Display::get_default()), gdk_display(Gdk::Display::get_default())
wl_display(gdk_wayland_display_get_wl_display(gdk_display->gobj()))
{ {
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 getFirstValidPath = [] (std::vector<std::string> possiblePaths) { auto getFirstValidPath = [] (std::vector<std::string> possiblePaths) {
wordexp_t p; wordexp_t p;

View File

@ -1,38 +1,44 @@
#include "factory.hpp" #include "factory.hpp"
waybar::Factory::Factory(Bar &bar, Json::Value config) waybar::Factory::Factory(Bar& bar, const Json::Value& config)
: _bar(bar), _config(std::move(config)) : bar_(bar), config_(config)
{} {}
waybar::IModule* waybar::Factory::makeModule(const std::string &name) waybar::IModule* waybar::Factory::makeModule(const std::string &name) const
{ {
try { try {
if (name == "battery") { if (name == "battery") {
return new waybar::modules::Battery(_config[name]); return new waybar::modules::Battery(config_[name]);
} }
#ifdef HAVE_SWAY
if (name == "sway/workspaces") { if (name == "sway/workspaces") {
return new waybar::modules::sway::Workspaces(_bar, _config[name]); return new waybar::modules::sway::Workspaces(bar_, config_[name]);
} }
if (name == "sway/window") { if (name == "sway/window") {
return new waybar::modules::sway::Window(_bar, _config[name]); return new waybar::modules::sway::Window(bar_, config_[name]);
} }
#endif
if (name == "memory") { if (name == "memory") {
return new waybar::modules::Memory(_config[name]); return new waybar::modules::Memory(config_[name]);
} }
if (name == "cpu") { if (name == "cpu") {
return new waybar::modules::Cpu(_config[name]); return new waybar::modules::Cpu(config_[name]);
} }
if (name == "clock") { if (name == "clock") {
return new waybar::modules::Clock(_config[name]); return new waybar::modules::Clock(config_[name]);
} }
#ifdef HAVE_LIBNL
if (name == "network") { if (name == "network") {
return new waybar::modules::Network(_config[name]); return new waybar::modules::Network(config_[name]);
} }
#endif
#ifdef HAVE_LIBPULSE
if (name == "pulseaudio") { if (name == "pulseaudio") {
return new waybar::modules::Pulseaudio(_config[name]); return new waybar::modules::Pulseaudio(config_[name]);
} }
#endif
if (name.compare(0, 7, "custom/") == 0 && name.size() > 7) { if (name.compare(0, 7, "custom/") == 0 && name.size() > 7) {
return new waybar::modules::Custom(name.substr(7), _config[name]); return new waybar::modules::Custom(name.substr(7), config_[name]);
} }
} catch (const std::exception& e) { } catch (const std::exception& e) {
auto err = fmt::format("Disabling module \"{}\", {}", name, e.what()); auto err = fmt::format("Disabling module \"{}\", {}", name, e.what());

View File

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

View File

@ -1,7 +1,7 @@
#include "modules/battery.hpp" #include "modules/battery.hpp"
waybar::modules::Battery::Battery(Json::Value config) waybar::modules::Battery::Battery(const Json::Value& config)
: ALabel(std::move(config)) : ALabel(config, "{capacity}%")
{ {
try { try {
for (auto &node : fs::directory_iterator(data_dir_)) { for (auto &node : fs::directory_iterator(data_dir_)) {
@ -23,25 +23,29 @@ waybar::modules::Battery::Battery(Json::Value config)
for (auto &bat : batteries_) { for (auto &bat : batteries_) {
inotify_add_watch(fd_, (bat / "uevent").c_str(), IN_ACCESS); inotify_add_watch(fd_, (bat / "uevent").c_str(), IN_ACCESS);
} }
label_.set_name("battery");
worker();
}
waybar::modules::Battery::~Battery()
{
close(fd_);
}
void waybar::modules::Battery::worker()
{
// Trigger first values // Trigger first values
update(); update();
label_.set_name("battery");
thread_.sig_update.connect(sigc::mem_fun(*this, &Battery::update));
thread_ = [this] { thread_ = [this] {
struct inotify_event event = {0}; struct inotify_event event = {0};
int nbytes = read(fd_, &event, sizeof(event)); int nbytes = read(fd_, &event, sizeof(event));
if (nbytes != sizeof(event)) { if (nbytes != sizeof(event)) {
return; return;
} }
thread_.sig_update.emit(); dp.emit();
}; };
} }
waybar::modules::Battery::~Battery()
{
close(fd_);
}
auto waybar::modules::Battery::update() -> void auto waybar::modules::Battery::update() -> void
{ {
try { try {
@ -58,9 +62,7 @@ auto waybar::modules::Battery::update() -> void
total += capacity; total += capacity;
} }
uint16_t capacity = total / batteries_.size(); uint16_t capacity = total / batteries_.size();
auto format = config_["format"] label_.set_text(fmt::format(format_, fmt::arg("capacity", capacity),
? config_["format"].asString() : "{capacity}%";
label_.set_text(fmt::format(format, fmt::arg("capacity", capacity),
fmt::arg("icon", getIcon(capacity)))); fmt::arg("icon", getIcon(capacity))));
label_.set_tooltip_text(status); label_.set_tooltip_text(status);
bool charging = status == "Charging"; bool charging = status == "Charging";
@ -79,13 +81,3 @@ auto waybar::modules::Battery::update() -> void
std::cerr << e.what() << std::endl; std::cerr << e.what() << std::endl;
} }
} }
std::string waybar::modules::Battery::getIcon(uint16_t percentage)
{
if (!config_["format-icons"] || !config_["format-icons"].isArray()) {
return "";
}
auto size = config_["format-icons"].size();
auto idx = std::clamp(percentage / (100 / size), 0U, size - 1);
return config_["format-icons"][idx].asString();
}

View File

@ -1,14 +1,13 @@
#include "modules/clock.hpp" #include "modules/clock.hpp"
waybar::modules::Clock::Clock(Json::Value config) waybar::modules::Clock::Clock(const Json::Value& config)
: ALabel(std::move(config)) : ALabel(config, "{:%H:%M}")
{ {
label_.set_name("clock"); label_.set_name("clock");
uint32_t interval = config_["interval"] ? config_["inveral"].asUInt() : 60; uint32_t interval = config_["interval"] ? config_["inveral"].asUInt() : 60;
thread_.sig_update.connect(sigc::mem_fun(*this, &Clock::update));
thread_ = [this, interval] { thread_ = [this, interval] {
auto now = waybar::chrono::clock::now(); auto now = waybar::chrono::clock::now();
thread_.sig_update.emit(); dp.emit();
auto timeout = std::chrono::floor<std::chrono::seconds>(now auto timeout = std::chrono::floor<std::chrono::seconds>(now
+ std::chrono::seconds(interval)); + std::chrono::seconds(interval));
thread_.sleep_until(timeout); thread_.sleep_until(timeout);
@ -18,6 +17,5 @@ waybar::modules::Clock::Clock(Json::Value config)
auto waybar::modules::Clock::update() -> void auto waybar::modules::Clock::update() -> void
{ {
auto localtime = fmt::localtime(std::time(nullptr)); auto localtime = fmt::localtime(std::time(nullptr));
auto format = config_["format"] ? config_["format"].asString() : "{:%H:%M}"; label_.set_text(fmt::format(format_, localtime));
label_.set_text(fmt::format(format, localtime));
} }

View File

@ -1,24 +1,22 @@
#include "modules/cpu.hpp" #include "modules/cpu.hpp"
waybar::modules::Cpu::Cpu(Json::Value config) waybar::modules::Cpu::Cpu(const Json::Value& config)
: ALabel(std::move(config)) : ALabel(config, "{}%")
{ {
label_.set_name("cpu"); label_.set_name("cpu");
uint32_t interval = config_["interval"] ? config_["inveral"].asUInt() : 10; uint32_t interval = config_["interval"] ? config_["inveral"].asUInt() : 10;
thread_.sig_update.connect(sigc::mem_fun(*this, &Cpu::update));
thread_ = [this, interval] { thread_ = [this, interval] {
thread_.sig_update.emit(); dp.emit();
thread_.sleep_for(chrono::seconds(interval)); thread_.sleep_for(chrono::seconds(interval));
}; };
} }
auto waybar::modules::Cpu::update() -> void auto waybar::modules::Cpu::update() -> void
{ {
struct sysinfo info = {0}; struct sysinfo info = {};
if (sysinfo(&info) == 0) { if (sysinfo(&info) == 0) {
float f_load = 1.f / (1u << SI_LOAD_SHIFT); float f_load = 1.f / (1u << SI_LOAD_SHIFT);
uint16_t load = info.loads[0] * f_load * 100 / get_nprocs(); uint16_t load = info.loads[0] * f_load * 100 / get_nprocs();
auto format = config_["format"] ? config_["format"].asString() : "{}%"; label_.set_text(fmt::format(format_, load));
label_.set_text(fmt::format(format, load));
} }
} }

View File

@ -1,11 +1,17 @@
#include "modules/custom.hpp" #include "modules/custom.hpp"
waybar::modules::Custom::Custom(std::string name, Json::Value config) waybar::modules::Custom::Custom(const std::string name,
: ALabel(std::move(config)), name_(std::move(name)) const Json::Value& config)
: ALabel(config, "{}"), name_(name)
{ {
if (!config_["exec"]) { if (!config_["exec"]) {
throw std::runtime_error(name_ + " has no exec path."); throw std::runtime_error(name_ + " has no exec path.");
} }
worker();
}
void waybar::modules::Custom::worker()
{
uint32_t interval = config_["interval"] ? config_["inveral"].asUInt() : 30; uint32_t interval = config_["interval"] ? config_["inveral"].asUInt() : 30;
thread_ = [this, interval] { thread_ = [this, interval] {
bool can_update = true; bool can_update = true;
@ -13,30 +19,29 @@ waybar::modules::Custom::Custom(std::string name, Json::Value config)
auto res = waybar::util::command::exec(config_["exec-if"].asString()); auto res = waybar::util::command::exec(config_["exec-if"].asString());
if (res.exit_code != 0) { if (res.exit_code != 0) {
can_update = false; can_update = false;
label_.hide();
label_.set_name("");
} }
} }
if (can_update) { if (can_update) {
thread_.sig_update.emit(); output_ = waybar::util::command::exec(config_["exec"].asString());
dp.emit();
} }
thread_.sleep_for(chrono::seconds(interval)); thread_.sleep_for(chrono::seconds(interval));
}; };
thread_.sig_update.connect(sigc::mem_fun(*this, &Custom::update));
} }
auto waybar::modules::Custom::update() -> void auto waybar::modules::Custom::update() -> void
{ {
auto res = waybar::util::command::exec(config_["exec"].asString());
// Hide label if output is empty // Hide label if output is empty
if (res.out.empty() || res.exit_code != 0) { if (output_.out.empty() || output_.exit_code != 0) {
label_.hide(); label_.hide();
label_.set_name(""); label_.set_name("");
} else { } else {
label_.set_name("custom-" + name_); label_.set_name("custom-" + name_);
auto format = config_["format"] ? config_["format"].asString() : "{}"; auto str = fmt::format(format_, output_.out);
auto str = fmt::format(format, res.out);
label_.set_text(str); label_.set_text(str);
label_.set_tooltip_text(str); label_.set_tooltip_text(str);
label_.show(); label_.show();
} }
} }

View File

@ -1,26 +1,24 @@
#include "modules/memory.hpp" #include "modules/memory.hpp"
waybar::modules::Memory::Memory(Json::Value config) waybar::modules::Memory::Memory(const Json::Value& config)
: ALabel(std::move(config)) : ALabel(config, "{}%")
{ {
label_.set_name("memory"); label_.set_name("memory");
uint32_t interval = config_["interval"] ? config_["inveral"].asUInt() : 30; uint32_t interval = config_["interval"] ? config_["inveral"].asUInt() : 30;
thread_.sig_update.connect(sigc::mem_fun(*this, &Memory::update));
thread_ = [this, interval] { thread_ = [this, interval] {
thread_.sig_update.emit(); dp.emit();
thread_.sleep_for(chrono::seconds(interval)); thread_.sleep_for(chrono::seconds(interval));
}; };
} }
auto waybar::modules::Memory::update() -> void auto waybar::modules::Memory::update() -> void
{ {
struct sysinfo info = {0}; struct sysinfo info = {};
if (sysinfo(&info) == 0) { if (sysinfo(&info) == 0) {
auto total = info.totalram * info.mem_unit; auto total = info.totalram * info.mem_unit;
auto freeram = info.freeram * info.mem_unit; auto freeram = info.freeram * info.mem_unit;
int used_ram_percentage = 100 * (total - freeram) / total; int used_ram_percentage = 100 * (total - freeram) / total;
auto format = config_["format"] ? config_["format"].asString() : "{}%"; label_.set_text(fmt::format(format_, used_ram_percentage));
label_.set_text(fmt::format(format, used_ram_percentage));
auto used_ram_gigabytes = (total - freeram) / std::pow(1024, 3); auto used_ram_gigabytes = (total - freeram) / std::pow(1024, 3);
label_.set_tooltip_text(fmt::format("{:.{}f}Gb used", used_ram_gigabytes, 1)); label_.set_tooltip_text(fmt::format("{:.{}f}Gb used", used_ram_gigabytes, 1));
} }

View File

@ -1,7 +1,7 @@
#include "modules/network.hpp" #include "modules/network.hpp"
waybar::modules::Network::Network(Json::Value config) waybar::modules::Network::Network(const Json::Value& config)
: ALabel(std::move(config)), family_(AF_INET), : ALabel(config, "{ifname}"), family_(AF_INET),
signal_strength_dbm_(0), signal_strength_(0) signal_strength_dbm_(0), signal_strength_(0)
{ {
sock_fd_ = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); sock_fd_ = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
@ -28,11 +28,11 @@ waybar::modules::Network::Network(Json::Value config)
ifname_ = ifname; ifname_ = ifname;
} }
} }
initNL80211();
label_.set_name("network"); label_.set_name("network");
// Trigger first values // Trigger first values
getInfo(); getInfo();
update(); update();
thread_.sig_update.connect(sigc::mem_fun(*this, &Network::update));
thread_ = [this] { thread_ = [this] {
char buf[4096]; char buf[4096];
uint64_t len = netlinkResponse(sock_fd_, buf, sizeof(buf), uint64_t len = netlinkResponse(sock_fd_, buf, sizeof(buf),
@ -69,7 +69,7 @@ waybar::modules::Network::Network(Json::Value config)
} }
if (need_update) { if (need_update) {
getInfo(); getInfo();
thread_.sig_update.emit(); dp.emit();
} }
}; };
} }
@ -77,11 +77,12 @@ waybar::modules::Network::Network(Json::Value config)
waybar::modules::Network::~Network() waybar::modules::Network::~Network()
{ {
close(sock_fd_); close(sock_fd_);
nl_socket_free(sk_);
} }
auto waybar::modules::Network::update() -> void auto waybar::modules::Network::update() -> void
{ {
auto format = config_["format"] ? config_["format"].asString() : "{ifname}"; auto format = format_;
if (ifid_ <= 0) { if (ifid_ <= 0) {
format = config_["format-disconnected"] format = config_["format-disconnected"]
? config_["format-disconnected"].asString() : format; ? config_["format-disconnected"].asString() : format;
@ -113,23 +114,39 @@ void waybar::modules::Network::disconnected()
ifid_ = -1; 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");
}
}
// Based on https://gist.github.com/Yawning/c70d804d4b8ae78cc698 // Based on https://gist.github.com/Yawning/c70d804d4b8ae78cc698
int waybar::modules::Network::getExternalInterface() int waybar::modules::Network::getExternalInterface()
{ {
static const uint32_t route_buffer_size = 8192;
struct nlmsghdr *hdr = nullptr; struct nlmsghdr *hdr = nullptr;
struct rtmsg *rt = nullptr; struct rtmsg *rt = nullptr;
void *resp = nullptr; char resp[route_buffer_size] = {0};
int ifidx = -1; int ifidx = -1;
/* Allocate space for the request. */ /* Prepare request. */
uint32_t reqlen = NLMSG_SPACE(sizeof(*rt)); uint32_t reqlen = NLMSG_SPACE(sizeof(*rt));
void *req = nullptr; char req[reqlen] = {0};
if ((req = calloc(1, reqlen)) == nullptr) {
goto out; /* ENOBUFS */
}
/* Build the RTM_GETROUTE request. */ /* Build the RTM_GETROUTE request. */
hdr = static_cast<struct nlmsghdr *>(req); hdr = reinterpret_cast<struct nlmsghdr *>(req);
hdr->nlmsg_len = NLMSG_LENGTH(sizeof(*rt)); hdr->nlmsg_len = NLMSG_LENGTH(sizeof(*rt));
hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
hdr->nlmsg_type = RTM_GETROUTE; hdr->nlmsg_type = RTM_GETROUTE;
@ -142,25 +159,19 @@ int waybar::modules::Network::getExternalInterface()
goto out; goto out;
} }
/* Allocate space for the response. */
static const uint32_t route_buffer_size = 8192;
if ((resp = calloc(1, route_buffer_size)) == nullptr) {
goto out; /* ENOBUFS */
}
/* Read the response(s). /* Read the response(s).
* *
* WARNING: All the packets generated by the request must be consumed (as in, * WARNING: All the packets generated by the request must be consumed (as in,
* consume responses till NLMSG_DONE/NLMSG_ERROR is encountered). * consume responses till NLMSG_DONE/NLMSG_ERROR is encountered).
*/ */
do { do {
uint64_t len = netlinkResponse(sock_fd_, resp, route_buffer_size); auto len = netlinkResponse(sock_fd_, resp, route_buffer_size);
if (len < 0) { if (len < 0) {
goto out; goto out;
} }
/* Parse the response payload into netlink messages. */ /* Parse the response payload into netlink messages. */
for (hdr = static_cast<struct nlmsghdr *>(resp); NLMSG_OK(hdr, len); for (hdr = reinterpret_cast<struct nlmsghdr *>(resp); NLMSG_OK(hdr, len);
hdr = NLMSG_NEXT(hdr, len)) { hdr = NLMSG_NEXT(hdr, len)) {
if (hdr->nlmsg_type == NLMSG_DONE) { if (hdr->nlmsg_type == NLMSG_DONE) {
goto out; goto out;
@ -217,7 +228,7 @@ int waybar::modules::Network::getExternalInterface()
break; break;
} }
for (uint32_t i = 0; i < dstlen; i += 1) { for (uint32_t i = 0; i < dstlen; i += 1) {
c |= *(unsigned char *)(RTA_DATA(attr) + i); c |= *((unsigned char *)RTA_DATA(attr) + i);
} }
has_destination = (c == 0); has_destination = (c == 0);
break; break;
@ -241,17 +252,13 @@ int waybar::modules::Network::getExternalInterface()
} while (true); } while (true);
out: out:
if (req != nullptr)
free(req);
if (resp != nullptr)
free(resp);
return ifidx; return ifidx;
} }
uint64_t waybar::modules::Network::netlinkRequest(int fd, void *req, int waybar::modules::Network::netlinkRequest(int fd, void *req,
uint32_t reqlen, uint32_t groups) uint32_t reqlen, uint32_t groups)
{ {
struct sockaddr_nl sa = {0}; struct sockaddr_nl sa = {};
sa.nl_family = AF_NETLINK; sa.nl_family = AF_NETLINK;
sa.nl_groups = groups; sa.nl_groups = groups;
struct iovec iov = { req, reqlen }; struct iovec iov = { req, reqlen };
@ -259,53 +266,54 @@ uint64_t waybar::modules::Network::netlinkRequest(int fd, void *req,
return sendmsg(fd, &msg, 0); return sendmsg(fd, &msg, 0);
} }
uint64_t waybar::modules::Network::netlinkResponse(int fd, void *resp, int waybar::modules::Network::netlinkResponse(int fd, void *resp,
uint32_t resplen, uint32_t groups) uint32_t resplen, uint32_t groups)
{ {
uint64_t ret; int ret;
struct sockaddr_nl sa = {0}; struct sockaddr_nl sa = {};
sa.nl_family = AF_NETLINK; sa.nl_family = AF_NETLINK;
sa.nl_groups = groups; sa.nl_groups = groups;
struct iovec iov = { resp, resplen }; struct iovec iov = { resp, resplen };
struct msghdr msg = { &sa, sizeof(sa), &iov, 1, nullptr, 0, 0 }; struct msghdr msg = { &sa, sizeof(sa), &iov, 1, nullptr, 0, 0 };
ret = recvmsg(fd, &msg, 0); ret = recvmsg(fd, &msg, 0);
if (msg.msg_flags & MSG_TRUNC) if (msg.msg_flags & MSG_TRUNC) {
return -1; return -1;
}
return ret; return ret;
} }
int waybar::modules::Network::scanCb(struct nl_msg *msg, void *data) { int waybar::modules::Network::scanCb(struct nl_msg *msg, void *data) {
auto net = static_cast<waybar::modules::Network *>(data); auto net = static_cast<waybar::modules::Network *>(data);
auto gnlh = static_cast<genlmsghdr *>(nlmsg_data(nlmsg_hdr(msg))); auto gnlh = static_cast<genlmsghdr *>(nlmsg_data(nlmsg_hdr(msg)));
struct nlattr* tb[NL80211_ATTR_MAX + 1]; struct nlattr* tb[NL80211_ATTR_MAX + 1];
struct nlattr* bss[NL80211_BSS_MAX + 1]; struct nlattr* bss[NL80211_BSS_MAX + 1];
struct nla_policy bss_policy[NL80211_BSS_MAX + 1]{}; struct nla_policy bss_policy[NL80211_BSS_MAX + 1]{};
bss_policy[NL80211_BSS_TSF].type = NLA_U64; bss_policy[NL80211_BSS_TSF].type = NLA_U64;
bss_policy[NL80211_BSS_FREQUENCY].type = NLA_U32; bss_policy[NL80211_BSS_FREQUENCY].type = NLA_U32;
bss_policy[NL80211_BSS_BSSID].type = NLA_UNSPEC; bss_policy[NL80211_BSS_BSSID].type = NLA_UNSPEC;
bss_policy[NL80211_BSS_BEACON_INTERVAL].type = NLA_U16; bss_policy[NL80211_BSS_BEACON_INTERVAL].type = NLA_U16;
bss_policy[NL80211_BSS_CAPABILITY].type = NLA_U16; bss_policy[NL80211_BSS_CAPABILITY].type = NLA_U16;
bss_policy[NL80211_BSS_INFORMATION_ELEMENTS].type = NLA_UNSPEC; bss_policy[NL80211_BSS_INFORMATION_ELEMENTS].type = NLA_UNSPEC;
bss_policy[NL80211_BSS_SIGNAL_MBM].type = NLA_U32; bss_policy[NL80211_BSS_SIGNAL_MBM].type = NLA_U32;
bss_policy[NL80211_BSS_SIGNAL_UNSPEC].type = NLA_U8; bss_policy[NL80211_BSS_SIGNAL_UNSPEC].type = NLA_U8;
bss_policy[NL80211_BSS_STATUS].type = NLA_U32; bss_policy[NL80211_BSS_STATUS].type = NLA_U32;
if (nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), nullptr) < 0) { if (nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), nullptr) < 0) {
return NL_SKIP;
}
if (tb[NL80211_ATTR_BSS] == nullptr) {
return NL_SKIP;
}
if (nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS], bss_policy) != 0) {
return NL_SKIP;
}
if (!net->associatedOrJoined(bss)) {
return NL_SKIP;
}
net->parseEssid(bss);
net->parseSignal(bss);
// TODO(someone): parse quality
return NL_SKIP; return NL_SKIP;
}
if (tb[NL80211_ATTR_BSS] == nullptr) {
return NL_SKIP;
}
if (nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS], bss_policy) != 0) {
return NL_SKIP;
}
if (!net->associatedOrJoined(bss)) {
return NL_SKIP;
}
net->parseEssid(bss);
net->parseSignal(bss);
// TODO(someone): parse quality
return NL_SKIP;
} }
void waybar::modules::Network::parseEssid(struct nlattr **bss) void waybar::modules::Network::parseEssid(struct nlattr **bss)
@ -329,62 +337,47 @@ void waybar::modules::Network::parseEssid(struct nlattr **bss)
} }
void waybar::modules::Network::parseSignal(struct nlattr **bss) { void waybar::modules::Network::parseSignal(struct nlattr **bss) {
if (bss[NL80211_BSS_SIGNAL_MBM] != nullptr) { if (bss[NL80211_BSS_SIGNAL_MBM] != nullptr) {
// signalstrength in dBm // signalstrength in dBm
signal_strength_dbm_ = signal_strength_dbm_ =
static_cast<int>(nla_get_u32(bss[NL80211_BSS_SIGNAL_MBM])) / 100; static_cast<int>(nla_get_u32(bss[NL80211_BSS_SIGNAL_MBM])) / 100;
// WiFi-hardware usually operates in the range -90 to -20dBm. // WiFi-hardware usually operates in the range -90 to -20dBm.
const int hardwareMax = -20; const int hardwareMax = -20;
const int hardwareMin = -90; const int hardwareMin = -90;
signal_strength_ = ((signal_strength_dbm_ - hardwareMin) signal_strength_ = ((signal_strength_dbm_ - hardwareMin)
/ double{hardwareMax - hardwareMin}) * 100; / double{hardwareMax - hardwareMin}) * 100;
}
} }
}
bool waybar::modules::Network::associatedOrJoined(struct nlattr** bss) bool waybar::modules::Network::associatedOrJoined(struct nlattr** bss)
{ {
if (bss[NL80211_BSS_STATUS] == nullptr) { if (bss[NL80211_BSS_STATUS] == nullptr) {
return false; return false;
}
auto status = nla_get_u32(bss[NL80211_BSS_STATUS]);
switch (status) {
case NL80211_BSS_STATUS_ASSOCIATED:
case NL80211_BSS_STATUS_IBSS_JOINED:
case NL80211_BSS_STATUS_AUTHENTICATED:
return true;
default:
return false;
}
} }
auto status = nla_get_u32(bss[NL80211_BSS_STATUS]);
switch (status) {
case NL80211_BSS_STATUS_ASSOCIATED:
case NL80211_BSS_STATUS_IBSS_JOINED:
case NL80211_BSS_STATUS_AUTHENTICATED:
return true;
default:
return false;
}
}
auto waybar::modules::Network::getInfo() -> void auto waybar::modules::Network::getInfo() -> void
{ {
struct nl_sock *sk = nl_socket_alloc(); struct nl_msg* nl_msg = nlmsg_alloc();
if (genl_connect(sk) != 0) { if (nl_msg == nullptr) {
nl_socket_free(sk); nl_socket_free(sk_);
return; return;
} }
if (nl_socket_modify_cb(sk, NL_CB_VALID, NL_CB_CUSTOM, scanCb, this) < 0) { if (genlmsg_put(nl_msg, NL_AUTO_PORT, NL_AUTO_SEQ, nl80211_id_, 0, NLM_F_DUMP,
nl_socket_free(sk);
return;
}
const int nl80211_id = genl_ctrl_resolve(sk, "nl80211");
if (nl80211_id < 0) {
nl_socket_free(sk);
return;
}
struct nl_msg *msg = nlmsg_alloc();
if (msg == nullptr) {
nl_socket_free(sk);
return;
}
if (genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, nl80211_id, 0, NLM_F_DUMP,
NL80211_CMD_GET_SCAN, 0) == nullptr NL80211_CMD_GET_SCAN, 0) == nullptr
|| nla_put_u32(msg, NL80211_ATTR_IFINDEX, ifid_) < 0) { || nla_put_u32(nl_msg, NL80211_ATTR_IFINDEX, ifid_) < 0) {
nlmsg_free(msg); nlmsg_free(nl_msg);
return; return;
} }
nl_send_sync(sk, msg); nl_send_sync(sk_, nl_msg);
nl_socket_free(sk);
} }

View File

@ -1,7 +1,7 @@
#include "modules/pulseaudio.hpp" #include "modules/pulseaudio.hpp"
waybar::modules::Pulseaudio::Pulseaudio(Json::Value config) waybar::modules::Pulseaudio::Pulseaudio(const Json::Value& config)
: ALabel(std::move(config)), mainloop_(nullptr), mainloop_api_(nullptr), : ALabel(config, "{volume}%"), mainloop_(nullptr), mainloop_api_(nullptr),
context_(nullptr), sink_idx_(0), volume_(0), muted_(false) context_(nullptr), sink_idx_(0), volume_(0), muted_(false)
{ {
label_.set_name("pulseaudio"); label_.set_name("pulseaudio");
@ -89,7 +89,8 @@ void waybar::modules::Pulseaudio::sinkInfoCb(pa_context* /*context*/,
pa->volume_ = volume * 100.0f; pa->volume_ = volume * 100.0f;
pa->muted_ = i->mute != 0; pa->muted_ = i->mute != 0;
pa->desc_ = i->description; pa->desc_ = i->description;
Glib::signal_idle().connect_once(sigc::mem_fun(*pa, &Pulseaudio::update)); pa->port_name_ = i->active_port->name;
pa->dp.emit();
} }
} }
@ -100,33 +101,48 @@ void waybar::modules::Pulseaudio::sinkInfoCb(pa_context* /*context*/,
void waybar::modules::Pulseaudio::serverInfoCb(pa_context *context, void waybar::modules::Pulseaudio::serverInfoCb(pa_context *context,
const pa_server_info *i, void *data) const pa_server_info *i, void *data)
{ {
pa_context_get_sink_info_by_name(context, i->default_sink_name, pa_context_get_sink_info_by_name(context, i->default_sink_name,
sinkInfoCb, data); sinkInfoCb, data);
}
const std::string waybar::modules::Pulseaudio::getPortIcon() const
{
std::vector<std::string> ports = {
"headphones",
"speaker",
"hdmi",
"headset",
"handsfree",
"portable",
"car",
"hifi",
"phone",
};
for (auto port : ports) {
if (port_name_.find(port) != std::string::npos) {
return port;
}
}
return "";
} }
auto waybar::modules::Pulseaudio::update() -> void auto waybar::modules::Pulseaudio::update() -> void
{ {
auto format = auto format = format_;
config_["format"] ? config_["format"].asString() : "{volume}%"; if (muted_) {
if (muted_) { format =
format = config_["format-muted"] ? config_["format-muted"].asString() : format;
config_["format-muted"] ? config_["format-muted"].asString() : format; label_.get_style_context()->add_class("muted");
label_.get_style_context()->add_class("muted"); } else if (port_name_.find("a2dp_sink") != std::string::npos) {
} else { format = config_["format-bluetooth"]
label_.get_style_context()->remove_class("muted"); ? config_["format-bluetooth"].asString() : format;
} label_.get_style_context()->add_class("bluetooth");
label_.set_label(fmt::format(format, } else {
fmt::arg("volume", volume_), label_.get_style_context()->remove_class("muted");
fmt::arg("icon", getIcon(volume_)))); label_.get_style_context()->add_class("bluetooth");
label_.set_tooltip_text(desc_);
}
std::string waybar::modules::Pulseaudio::getIcon(uint16_t percentage)
{
if (!config_["format-icons"] || !config_["format-icons"].isArray()) {
return "";
} }
auto size = config_["format-icons"].size(); label_.set_label(fmt::format(format,
auto idx = std::clamp(percentage / (100 / size), 0U, size - 1); fmt::arg("volume", volume_),
return config_["format-icons"][idx].asString(); fmt::arg("icon", getIcon(volume_, getPortIcon()))));
label_.set_tooltip_text(desc_);
} }

View File

@ -1,14 +1,17 @@
#define _POSIX_C_SOURCE 200809L
#include "modules/sway/ipc/client.hpp" #include "modules/sway/ipc/client.hpp"
#include <cstdio>
#include <string>
#include <sys/socket.h>
#include <sys/un.h>
static const std::string ipc_magic("i3-ipc"); waybar::modules::sway::Ipc::Ipc()
static const size_t ipc_header_size = ipc_magic.size() + 8; : fd_(-1), fd_event_(-1)
{}
std::string getSocketPath() { waybar::modules::sway::Ipc::~Ipc()
{
close(fd_);
close(fd_event_);
}
const std::string waybar::modules::sway::Ipc::getSocketPath() const
{
const char *env = getenv("SWAYSOCK"); const char *env = getenv("SWAYSOCK");
if (env != nullptr) { if (env != nullptr) {
return std::string(env); return std::string(env);
@ -33,31 +36,41 @@ std::string getSocketPath() {
return str; return str;
} }
int ipcOpenSocket(const std::string &socketPath) { int waybar::modules::sway::Ipc::open(const std::string& socketPath) const
{
struct sockaddr_un addr = {0}; struct sockaddr_un addr = {0};
int socketfd; int fd = -1;
if ((socketfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
throw std::runtime_error("Unable to open Unix socket"); throw std::runtime_error("Unable to open Unix socket");
} }
addr.sun_family = AF_UNIX; addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, socketPath.c_str(), sizeof(addr.sun_path) - 1); strncpy(addr.sun_path, socketPath.c_str(), sizeof(addr.sun_path) - 1);
addr.sun_path[sizeof(addr.sun_path) - 1] = 0; addr.sun_path[sizeof(addr.sun_path) - 1] = 0;
int l = sizeof(struct sockaddr_un); int l = sizeof(struct sockaddr_un);
if (connect(socketfd, reinterpret_cast<struct sockaddr *>(&addr), l) == -1) { if (::connect(fd, reinterpret_cast<struct sockaddr *>(&addr), l) == -1) {
throw std::runtime_error("Unable to connect to " + socketPath); throw std::runtime_error("Unable to connect to Sway");
} }
return socketfd; return fd;
} }
struct ipc_response ipcRecvResponse(int socketfd) { 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
{
std::string header; std::string header;
header.reserve(ipc_header_size); header.reserve(ipc_header_size_);
auto data32 = reinterpret_cast<uint32_t *>(header.data() + ipc_magic.size()); auto data32 = reinterpret_cast<uint32_t *>(header.data() + ipc_magic_.size());
size_t total = 0; size_t total = 0;
while (total < ipc_header_size) { while (total < ipc_header_size_) {
ssize_t res = ssize_t res =
::recv(socketfd, header.data() + total, ipc_header_size - total, 0); ::recv(fd, header.data() + total, ipc_header_size_ - total, 0);
if (res <= 0) { if (res <= 0) {
throw std::runtime_error("Unable to receive IPC response"); throw std::runtime_error("Unable to receive IPC response");
} }
@ -66,32 +79,56 @@ struct ipc_response ipcRecvResponse(int socketfd) {
total = 0; total = 0;
std::string payload; std::string payload;
payload.reserve(data32[0]); payload.reserve(data32[0] + 1);
while (total < data32[0]) { while (total < data32[0]) {
ssize_t res = ssize_t res =
::recv(socketfd, payload.data() + total, data32[0] - total, 0); ::recv(fd, payload.data() + total, data32[0] - total, 0);
if (res < 0) { if (res < 0) {
throw std::runtime_error("Unable to receive IPC response"); throw std::runtime_error("Unable to receive IPC response");
} }
total += res; total += res;
} }
payload[data32[0]] = 0;
return { data32[0], data32[1], &payload.front() }; return { data32[0], data32[1], &payload.front() };
} }
struct ipc_response ipcSingleCommand(int socketfd, uint32_t type, struct waybar::modules::sway::Ipc::ipc_response
const std::string& payload) { waybar::modules::sway::Ipc::send(int fd, uint32_t type,
const std::string& payload) const
{
std::string header; std::string header;
header.reserve(ipc_header_size); header.reserve(ipc_header_size_);
auto data32 = reinterpret_cast<uint32_t *>(header.data() + ipc_magic.size()); auto data32 = reinterpret_cast<uint32_t *>(header.data() + ipc_magic_.size());
memcpy(header.data(), ipc_magic.c_str(), ipc_magic.size()); memcpy(header.data(), ipc_magic_.c_str(), ipc_magic_.size());
data32[0] = payload.size(); data32[0] = payload.size();
data32[1] = type; data32[1] = type;
if (send(socketfd, header.data(), ipc_header_size, 0) == -1) { if (::send(fd, header.data(), ipc_header_size_, 0) == -1) {
throw std::runtime_error("Unable to send IPC header"); throw std::runtime_error("Unable to send IPC header");
} }
if (send(socketfd, payload.c_str(), payload.size(), 0) == -1) { if (::send(fd, payload.c_str(), payload.size(), 0) == -1) {
throw std::runtime_error("Unable to send IPC payload"); throw std::runtime_error("Unable to send IPC payload");
} }
return ipcRecvResponse(socketfd); return recv(fd);
} }
struct waybar::modules::sway::Ipc::ipc_response
waybar::modules::sway::Ipc::sendCmd(uint32_t type,
const std::string& payload) const
{
return send(fd_, type, payload);
}
void waybar::modules::sway::Ipc::subscribe(const std::string& payload) const
{
auto res = send(fd_event_, IPC_SUBSCRIBE, payload);
if (res.payload != "{\"success\": true}") {
throw std::runtime_error("Unable to subscribe ipc event");
}
}
struct waybar::modules::sway::Ipc::ipc_response
waybar::modules::sway::Ipc::handleEvent() const
{
return recv(fd_event_);
}

View File

@ -1,24 +1,26 @@
#include "modules/sway/window.hpp" #include "modules/sway/window.hpp"
#include "modules/sway/ipc/client.hpp"
waybar::modules::sway::Window::Window(Bar &bar, Json::Value config) waybar::modules::sway::Window::Window(Bar &bar, const Json::Value& config)
: ALabel(std::move(config)), bar_(bar) : ALabel(config, "{}"), bar_(bar)
{ {
label_.set_name("window"); label_.set_name("window");
std::string socketPath = getSocketPath(); ipc_.connect();
ipcfd_ = ipcOpenSocket(socketPath); ipc_.subscribe("[ \"window\" ]");
ipc_eventfd_ = ipcOpenSocket(socketPath);
ipcSingleCommand(ipc_eventfd_, IPC_SUBSCRIBE, "[ \"window\" ]");
getFocusedWindow(); getFocusedWindow();
thread_.sig_update.connect(sigc::mem_fun(*this, &Window::update)); // Launch worker
worker();
}
void waybar::modules::sway::Window::worker()
{
thread_ = [this] { thread_ = [this] {
try { try {
auto res = ipcRecvResponse(ipc_eventfd_); auto res = ipc_.handleEvent();
auto parsed = parser_.parse(res.payload); auto parsed = parser_.parse(res.payload);
if ((parsed["change"] == "focus" || parsed["change"] == "title") if ((parsed["change"] == "focus" || parsed["change"] == "title")
&& parsed["container"]["focused"].asBool()) { && parsed["container"]["focused"].asBool()) {
window_ = parsed["container"]["name"].asString(); window_ = parsed["container"]["name"].asString();
thread_.sig_update.emit(); dp.emit();
} }
} catch (const std::exception& e) { } catch (const std::exception& e) {
std::cerr << e.what() << std::endl; std::cerr << e.what() << std::endl;
@ -26,22 +28,16 @@ waybar::modules::sway::Window::Window(Bar &bar, Json::Value config)
}; };
} }
waybar::modules::sway::Window::~Window()
{
close(ipcfd_);
close(ipc_eventfd_);
}
auto waybar::modules::sway::Window::update() -> void auto waybar::modules::sway::Window::update() -> void
{ {
label_.set_text(window_); label_.set_text(fmt::format(format_, window_));
label_.set_tooltip_text(window_); label_.set_tooltip_text(window_);
} }
std::string waybar::modules::sway::Window::getFocusedNode(Json::Value nodes) std::string waybar::modules::sway::Window::getFocusedNode(Json::Value nodes)
{ {
for (auto &node : nodes) { for (auto &node : nodes) {
if (node["focused"].asBool()) { if (node["focused"].asBool() && node["type"] == "con") {
return node["name"].asString(); return node["name"].asString();
} }
auto res = getFocusedNode(node["nodes"]); auto res = getFocusedNode(node["nodes"]);
@ -55,7 +51,7 @@ std::string waybar::modules::sway::Window::getFocusedNode(Json::Value nodes)
void waybar::modules::sway::Window::getFocusedWindow() void waybar::modules::sway::Window::getFocusedWindow()
{ {
try { try {
auto res = ipcSingleCommand(ipcfd_, IPC_GET_TREE, ""); auto res = ipc_.sendCmd(IPC_GET_TREE);
auto parsed = parser_.parse(res.payload); auto parsed = parser_.parse(res.payload);
window_ = getFocusedNode(parsed["nodes"]); window_ = getFocusedNode(parsed["nodes"]);
Glib::signal_idle().connect_once(sigc::mem_fun(*this, &Window::update)); Glib::signal_idle().connect_once(sigc::mem_fun(*this, &Window::update));

View File

@ -1,15 +1,18 @@
#include "modules/sway/workspaces.hpp" #include "modules/sway/workspaces.hpp"
#include "modules/sway/ipc/client.hpp"
waybar::modules::sway::Workspaces::Workspaces(Bar &bar, Json::Value config) waybar::modules::sway::Workspaces::Workspaces(Bar& bar,
: bar_(bar), config_(std::move(config)), scrolling_(false) const Json::Value& config)
: bar_(bar), config_(config), scrolling_(false)
{ {
box_.set_name("workspaces"); box_.set_name("workspaces");
std::string socketPath = getSocketPath(); ipc_.connect();
ipcfd_ = ipcOpenSocket(socketPath); ipc_.subscribe("[ \"workspace\" ]");
ipc_eventfd_ = ipcOpenSocket(socketPath); // Launch worker
ipcSingleCommand(ipc_eventfd_, IPC_SUBSCRIBE, "[ \"workspace\" ]"); worker();
thread_.sig_update.connect(sigc::mem_fun(*this, &Workspaces::update)); }
void waybar::modules::sway::Workspaces::worker()
{
thread_ = [this] { thread_ = [this] {
try { try {
// Wait for the name of the output // Wait for the name of the output
@ -18,27 +21,24 @@ waybar::modules::sway::Workspaces::Workspaces(Bar &bar, Json::Value config)
thread_.sleep_for(chrono::milliseconds(150)); thread_.sleep_for(chrono::milliseconds(150));
} }
} else if (!workspaces_.empty()) { } else if (!workspaces_.empty()) {
ipcRecvResponse(ipc_eventfd_); ipc_.handleEvent();
} }
thread_.sig_update.emit(); {
std::lock_guard<std::mutex> lock(mutex_);
auto res = ipc_.sendCmd(IPC_GET_WORKSPACES);
workspaces_ = parser_.parse(res.payload);
}
dp.emit();
} catch (const std::exception& e) { } catch (const std::exception& e) {
std::cerr << e.what() << std::endl; std::cerr << e.what() << std::endl;
} }
}; };
} }
waybar::modules::sway::Workspaces::~Workspaces()
{
close(ipcfd_);
close(ipc_eventfd_);
}
auto waybar::modules::sway::Workspaces::update() -> void auto waybar::modules::sway::Workspaces::update() -> void
{ {
std::lock_guard<std::mutex> lock(mutex_);
auto res = ipcSingleCommand(ipcfd_, IPC_GET_WORKSPACES, "");
workspaces_ = parser_.parse(res.payload);
bool needReorder = false; bool needReorder = false;
std::lock_guard<std::mutex> lock(mutex_);
for (auto it = buttons_.begin(); it != buttons_.end();) { for (auto it = buttons_.begin(); it != buttons_.end();) {
auto ws = std::find_if(workspaces_.begin(), workspaces_.end(), auto ws = std::find_if(workspaces_.begin(), workspaces_.end(),
[it](auto node) -> bool { return node["num"].asInt() == it->first; }); [it](auto node) -> bool { return node["num"].asInt() == it->first; });
@ -78,6 +78,8 @@ auto waybar::modules::sway::Workspaces::update() -> void
if (needReorder) { if (needReorder) {
box_.reorder_child(button, node["num"].asInt()); box_.reorder_child(button, node["num"].asInt());
} }
auto icon = getIcon(node["name"].asString(), node);
button.set_label(icon);
button.show(); button.show();
} }
} }
@ -88,7 +90,7 @@ auto waybar::modules::sway::Workspaces::update() -> void
void waybar::modules::sway::Workspaces::addWorkspace(Json::Value node) void waybar::modules::sway::Workspaces::addWorkspace(Json::Value node)
{ {
auto icon = getIcon(node["name"].asString()); auto icon = getIcon(node["name"].asString(), node);
auto pair = buttons_.emplace(node["num"].asInt(), icon); auto pair = buttons_.emplace(node["num"].asInt(), icon);
auto &button = pair.first->second; auto &button = pair.first->second;
if (icon != node["name"].asString()) { if (icon != node["name"].asString()) {
@ -100,7 +102,7 @@ void waybar::modules::sway::Workspaces::addWorkspace(Json::Value node)
try { try {
std::lock_guard<std::mutex> lock(mutex_); std::lock_guard<std::mutex> lock(mutex_);
auto cmd = fmt::format("workspace \"{}\"", pair.first->first); auto cmd = fmt::format("workspace \"{}\"", pair.first->first);
ipcSingleCommand(ipcfd_, IPC_COMMAND, cmd); ipc_.sendCmd(IPC_COMMAND, cmd);
} catch (const std::exception& e) { } catch (const std::exception& e) {
std::cerr << e.what() << std::endl; std::cerr << e.what() << std::endl;
} }
@ -123,13 +125,19 @@ void waybar::modules::sway::Workspaces::addWorkspace(Json::Value node)
button.show(); button.show();
} }
std::string waybar::modules::sway::Workspaces::getIcon(std::string name) std::string waybar::modules::sway::Workspaces::getIcon(std::string name,
Json::Value node)
{ {
if (config_["format-icons"][name]) { std::vector<std::string> keys = {
return config_["format-icons"][name].asString(); name, "urgent", "focused", "visible", "default"};
} for (auto const& key : keys) {
if (config_["format-icons"]["default"]) { if (key == "focused" || key == "visible" || key == "urgent") {
return config_["format-icons"]["default"].asString(); if (config_["format-icons"][key] && node[key].asBool()) {
return config_["format-icons"][key].asString();
}
} else if (config_["format-icons"][key]) {
return config_["format-icons"][key].asString();
}
} }
return name; return name;
} }
@ -143,11 +151,13 @@ bool waybar::modules::sway::Workspaces::handleScroll(GdkEventScroll *e)
scrolling_ = true; scrolling_ = true;
int id = -1; int id = -1;
uint16_t idx = 0; uint16_t idx = 0;
std::lock_guard<std::mutex> lock(mutex_); {
for (; idx < workspaces_.size(); idx += 1) { std::lock_guard<std::mutex> lock(mutex_);
if (workspaces_[idx]["focused"].asBool()) { for (; idx < workspaces_.size(); idx += 1) {
id = workspaces_[idx]["num"].asInt(); if (workspaces_[idx]["focused"].asBool()) {
break; id = workspaces_[idx]["num"].asInt();
break;
}
} }
} }
if (id == -1) { if (id == -1) {
@ -170,12 +180,15 @@ bool waybar::modules::sway::Workspaces::handleScroll(GdkEventScroll *e)
id = getPrevWorkspace(); id = getPrevWorkspace();
} }
} }
if (id == workspaces_[idx]["num"].asInt()) { {
scrolling_ = false; std::lock_guard<std::mutex> lock(mutex_);
return false; if (id == workspaces_[idx]["num"].asInt()) {
scrolling_ = false;
return false;
}
ipc_.sendCmd(IPC_COMMAND, fmt::format("workspace \"{}\"", id));
std::this_thread::sleep_for(std::chrono::milliseconds(150));
} }
ipcSingleCommand(ipcfd_, IPC_COMMAND, fmt::format("workspace \"{}\"", id));
std::this_thread::sleep_for(std::chrono::milliseconds(150));
return true; return true;
} }