Compare commits

..

11 Commits
0.0.2 ... 0.0.3

Author SHA1 Message Date
c3bd6da1d0 chore: v0.0.3 2018-08-15 14:50:19 +02:00
e3e099f836 feat(workspaces): icons 2018-08-15 14:48:08 +02:00
767d9dd5b4 fix(workspaces): buttons iterator 2018-08-15 14:30:01 +02:00
d1d51b76aa fix(client): try to fix #20 2018-08-15 01:53:43 +02:00
cee031d2fa Workspaces scroll event (#19) 2018-08-14 11:26:06 +02:00
18c7ad0026 fix(workspaces): lock mutex inside click callback 2018-08-13 23:43:35 +02:00
1555cb71e1 feat(pulseaudio): volume icons 2018-08-13 22:33:07 +02:00
ea9a08d473 refactor(workspaces): listen ipc event 2018-08-13 21:23:43 +02:00
68f9ea3065 fix(battery): add check for sys files 2018-08-13 17:11:47 +02:00
a423f7032d Battery event (#18) 2018-08-13 14:05:13 +02:00
01894f18cd chore: clean headers 2018-08-12 20:25:19 +02:00
29 changed files with 413 additions and 258 deletions

View File

@ -1,5 +1,7 @@
#pragma once #pragma once
#include <gtkmm.h>
namespace waybar { namespace waybar {
class IModule { class IModule {
public: public:

View File

@ -3,16 +3,11 @@
#include <unistd.h> #include <unistd.h>
#include <wordexp.h> #include <wordexp.h>
#include <iostream>
#include <fmt/format.h> #include <fmt/format.h>
#include <gdk/gdk.h> #include <gdk/gdk.h>
#include <gtkmm.h>
#include <wayland-client.h> #include <wayland-client.h>
#include "wlr-layer-shell-unstable-v1-client-protocol.h"
#include <gdk/gdkwayland.h> #include <gdk/gdkwayland.h>
#include "bar.hpp" #include "bar.hpp"
@ -26,11 +21,12 @@ namespace waybar {
Gtk::Main gtk_main; Gtk::Main gtk_main;
Glib::RefPtr<Gdk::Display> gdk_display; Glib::RefPtr<Gdk::Display> gdk_display;
struct wl_display *wlDisplay; struct wl_display *wlDisplay = nullptr;
struct wl_registry *registry; struct wl_registry *registry = nullptr;
struct zwlr_layer_shell_v1 *layer_shell; struct zwlr_layer_shell_v1 *layer_shell = nullptr;
struct zxdg_output_manager_v1 *xdg_output_manager; struct zxdg_output_manager_v1 *xdg_output_manager = nullptr;
struct wl_seat *seat; struct wl_seat *seat = nullptr;
struct wl_output *wlOutput = nullptr;
std::vector<std::unique_ptr<Bar>> bars; std::vector<std::unique_ptr<Bar>> bars;
Client(int argc, char* argv[]); Client(int argc, char* argv[]);

View File

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

View File

@ -3,9 +3,10 @@
#include <json/json.h> #include <json/json.h>
#include <filesystem> #include <filesystem>
#include <fstream> #include <fstream>
#include <gtkmm.h>
#include <iostream> #include <iostream>
#include <fmt/format.h> #include <fmt/format.h>
#include <sys/inotify.h>
#include <algorithm>
#include "util/chrono.hpp" #include "util/chrono.hpp"
#include "IModule.hpp" #include "IModule.hpp"
@ -19,7 +20,7 @@ namespace waybar::modules {
auto update() -> void; auto update() -> void;
operator Gtk::Widget&(); operator Gtk::Widget&();
private: private:
std::string _getIcon(uint32_t percentage); 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/";
std::vector<fs::path> _batteries; std::vector<fs::path> _batteries;
util::SleeperThread _thread; util::SleeperThread _thread;

View File

@ -1,7 +1,6 @@
#pragma once #pragma once
#include <json/json.h> #include <json/json.h>
#include <gtkmm.h>
#include <fmt/format.h> #include <fmt/format.h>
#include "util/chrono.hpp" #include "util/chrono.hpp"
#include "IModule.hpp" #include "IModule.hpp"

View File

@ -1,7 +1,6 @@
#pragma once #pragma once
#include <json/json.h> #include <json/json.h>
#include <gtkmm.h>
#include <fmt/format.h> #include <fmt/format.h>
#include <sys/sysinfo.h> #include <sys/sysinfo.h>
#include "util/chrono.hpp" #include "util/chrono.hpp"

View File

@ -1,7 +1,6 @@
#pragma once #pragma once
#include <json/json.h> #include <json/json.h>
#include <gtkmm.h>
#include <fmt/format.h> #include <fmt/format.h>
#include "util/chrono.hpp" #include "util/chrono.hpp"
#include "IModule.hpp" #include "IModule.hpp"

View File

@ -1,7 +1,6 @@
#pragma once #pragma once
#include <json/json.h> #include <json/json.h>
#include <gtkmm.h>
#include <fmt/format.h> #include <fmt/format.h>
#include <sys/sysinfo.h> #include <sys/sysinfo.h>
#include "util/chrono.hpp" #include "util/chrono.hpp"

View File

@ -5,9 +5,7 @@
#include <netlink/genl/genl.h> #include <netlink/genl/genl.h>
#include <netlink/genl/ctrl.h> #include <netlink/genl/ctrl.h>
#include <linux/nl80211.h> #include <linux/nl80211.h>
#include <iwlib.h> // TODO
#include <json/json.h> #include <json/json.h>
#include <gtkmm.h>
#include <fmt/format.h> #include <fmt/format.h>
#include "util/chrono.hpp" #include "util/chrono.hpp"
#include "IModule.hpp" #include "IModule.hpp"

View File

@ -2,8 +2,8 @@
#include <pulse/pulseaudio.h> #include <pulse/pulseaudio.h>
#include <json/json.h> #include <json/json.h>
#include <gtkmm.h>
#include <fmt/format.h> #include <fmt/format.h>
#include <algorithm>
#include "IModule.hpp" #include "IModule.hpp"
namespace waybar::modules { namespace waybar::modules {
@ -14,6 +14,7 @@ namespace waybar::modules {
auto update() -> void; auto update() -> void;
operator Gtk::Widget &(); operator Gtk::Widget &();
private: private:
std::string _getIcon(uint16_t percentage);
static void _subscribeCb(pa_context *context, static void _subscribeCb(pa_context *context,
pa_subscription_event_type_t type, uint32_t idx, void *data); pa_subscription_event_type_t type, uint32_t idx, void *data);
static void _contextStateCb(pa_context *c, void *data); static void _contextStateCb(pa_context *c, void *data);

View File

@ -4,24 +4,34 @@
#include "bar.hpp" #include "bar.hpp"
#include "client.hpp" #include "client.hpp"
#include "util/chrono.hpp" #include "util/chrono.hpp"
#include "util/json.hpp"
#include "IModule.hpp" #include "IModule.hpp"
namespace waybar::modules { namespace waybar::modules {
class Workspaces : public IModule { class Workspaces : public IModule {
public: public:
Workspaces(waybar::Bar &bar); Workspaces(waybar::Bar &bar, Json::Value config);
auto update() -> void; auto update() -> void;
operator Gtk::Widget &(); operator Gtk::Widget &();
private: private:
void _addWorkspace(Json::Value node); void _addWorkspace(Json::Value node);
Json::Value _getWorkspaces(); std::string _getIcon(std::string name);
Json::Value _getWorkspaces(const std::string data);
bool _handleScroll(GdkEventScroll *e);
int _getPrevWorkspace();
int _getNextWorkspace();
Bar &_bar; Bar &_bar;
Json::Value _config;
waybar::util::SleeperThread _thread; waybar::util::SleeperThread _thread;
Gtk::Box _box; Gtk::Box _box;
util::JsonParser _parser;
std::mutex _mutex;
bool _scrolling;
std::unordered_map<int, Gtk::Button> _buttons; std::unordered_map<int, Gtk::Button> _buttons;
int _ipcSocketfd; Json::Value _workspaces;
int _ipcEventSocketfd; int _ipcfd;
int _ipcEventfd;
}; };
} }

View File

@ -39,7 +39,9 @@ namespace waybar::util {
func(); func();
} while (do_run); } while (do_run);
}} }}
{} {
defined = true;
}
SleeperThread& operator=(std::function<void()> func) SleeperThread& operator=(std::function<void()> func)
{ {
@ -48,6 +50,7 @@ namespace waybar::util {
func(); func();
} while (do_run); } while (do_run);
}); });
defined = true;
return *this; return *this;
} }
@ -72,14 +75,17 @@ namespace waybar::util {
~SleeperThread() ~SleeperThread()
{ {
do_run = false; do_run = false;
if (defined) {
condvar.notify_all(); condvar.notify_all();
thread.join(); thread.join();
} }
}
private: private:
std::thread thread; std::thread thread;
std::condition_variable condvar; std::condition_variable condvar;
std::mutex mutex; std::mutex mutex;
bool defined = false;
bool do_run = true; bool do_run = true;
}; };

34
include/util/json.hpp Normal file
View File

@ -0,0 +1,34 @@
#pragma once
#include <json/json.h>
namespace waybar::util {
struct JsonParser {
JsonParser()
: _reader(_builder.newCharReader())
{}
Json::Value parse(const std::string data)
{
Json::Value root;
std::string err;
bool res =
_reader->parse(data.c_str(), data.c_str() + data.size(), &root, &err);
if (!res)
throw std::runtime_error(err);
return root;
}
~JsonParser()
{
delete _reader;
}
private:
Json::CharReaderBuilder _builder;
Json::CharReader *_reader;
};
}

View File

@ -1,6 +1,6 @@
project( project(
'waybar', 'cpp', 'c', 'waybar', 'cpp', 'c',
version: '0.0.2', version: '0.0.3',
license: 'MIT', license: 'MIT',
default_options : ['cpp_std=c++17'], default_options : ['cpp_std=c++17'],
) )

View File

@ -7,6 +7,15 @@
"modules-left": ["workspaces", "custom/spotify"], "modules-left": ["workspaces", "custom/spotify"],
"modules-right": ["pulseaudio", "network", "cpu", "memory", "battery", "clock"], "modules-right": ["pulseaudio", "network", "cpu", "memory", "battery", "clock"],
// Modules configuration // Modules configuration
"workspaces": {
"format-icons": {
"1": "",
"2": "",
"3": "",
"4": "",
"5": ""
}
},
"cpu": { "cpu": {
"format": "{}% " "format": "{}% "
}, },
@ -14,7 +23,7 @@
"format": "{}% " "format": "{}% "
}, },
"battery": { "battery": {
"format": "{value}% {icon}", "format": "{capacity}% {icon}",
"format-icons": ["", "", "", "", ""] "format-icons": ["", "", "", "", ""]
}, },
"network": { "network": {
@ -22,8 +31,9 @@
"format": "{essid} ({signalStrength}%) " "format": "{essid} ({signalStrength}%) "
}, },
"pulseaudio": { "pulseaudio": {
"format": "{}% ", "format": "{volume}% {icon}",
"format-muted": "" "format-muted": "",
"format-icons": ["", ""]
}, },
"custom/spotify": { "custom/spotify": {
"format": " {}", "format": " {}",

View File

@ -11,33 +11,37 @@ window {
color: white; color: white;
} }
.workspaces button { #workspaces button {
padding: 0 5px; padding: 0 8px;
background: transparent; background: transparent;
color: white; color: white;
border-bottom: 3px solid transparent; border-bottom: 3px solid transparent;
} }
.workspaces button.current { #workspaces button label {
font-size: 12px;
}
#workspaces button.current {
background: #64727D; background: #64727D;
border-bottom: 3px solid white; border-bottom: 3px solid white;
} }
.clock, .battery, .cpu, .memory, .network, .pulseaudio, .custom-spotify { #clock, #battery, #cpu, #memory, #network, #pulseaudio, #custom-spotify {
padding: 0 10px; padding: 0 10px;
margin: 0 5px; margin: 0 5px;
} }
.clock { #clock {
background-color: #64727D; background-color: #64727D;
} }
.battery { #battery {
background-color: #ffffff; background-color: #ffffff;
color: black; color: black;
} }
.battery.charging { #battery.charging {
color: white; color: white;
background-color: #26A65B; background-color: #26A65B;
} }
@ -49,7 +53,7 @@ window {
} }
} }
.battery.warning { #battery.warning {
background: #f53c3c; background: #f53c3c;
color: white; color: white;
animation-name: blink; animation-name: blink;
@ -59,30 +63,30 @@ window {
animation-direction: alternate; animation-direction: alternate;
} }
.cpu { #cpu {
background: #2ecc71; background: #2ecc71;
color: #000000; color: #000000;
} }
.memory { #memory {
background: #9b59b6; background: #9b59b6;
} }
.network { #network {
background: #2980b9; background: #2980b9;
} }
.pulseaudio { #pulseaudio {
background: #f1c40f; background: #f1c40f;
color: black; color: black;
} }
.pulseaudio.muted { #pulseaudio.muted {
background: #90b1b1; background: #90b1b1;
color: #2a5c45; color: #2a5c45;
} }
.custom-spotify { #custom-spotify {
background: #66cc99; background: #66cc99;
color: #2a5c45; color: #2a5c45;
} }

View File

@ -1,10 +1,7 @@
#include <condition_variable>
#include <gdk/gdkwayland.h>
#include <thread>
#include <fstream>
#include "bar.hpp" #include "bar.hpp"
#include "client.hpp" #include "client.hpp"
#include "factory.hpp" #include "factory.hpp"
#include "util/json.hpp"
waybar::Bar::Bar(Client &client, std::unique_ptr<struct wl_output *> &&p_output) waybar::Bar::Bar(Client &client, std::unique_ptr<struct wl_output *> &&p_output)
: client(client), window{Gtk::WindowType::WINDOW_TOPLEVEL}, : client(client), window{Gtk::WindowType::WINDOW_TOPLEVEL},
@ -129,19 +126,13 @@ auto waybar::Bar::toggle() -> void
auto waybar::Bar::_setupConfig() -> void auto waybar::Bar::_setupConfig() -> void
{ {
Json::Value root; util::JsonParser parser;
Json::CharReaderBuilder builder;
Json::CharReader* reader = builder.newCharReader();
std::string err;
std::ifstream file(client.configFile); std::ifstream file(client.configFile);
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>());
bool res = reader->parse(str.c_str(), str.c_str() + str.size(), &_config, &err); _config = parser.parse(str);
delete reader;
if (!res)
throw std::runtime_error(err);
} }
auto waybar::Bar::_setupCss() -> void auto waybar::Bar::_setupCss() -> void
@ -174,21 +165,24 @@ auto waybar::Bar::_setupWidgets() -> void
if (_config["modules-left"]) { if (_config["modules-left"]) {
for (auto name : _config["modules-left"]) { for (auto name : _config["modules-left"]) {
auto &module = factory.makeModule(name.asString()); auto module = factory.makeModule(name.asString());
left.pack_start(module, false, true, 0); if (module)
left.pack_start(*module, false, true, 0);
} }
} }
if (_config["modules-center"]) { if (_config["modules-center"]) {
for (auto name : _config["modules-center"]) { for (auto name : _config["modules-center"]) {
auto &module = factory.makeModule(name.asString()); auto module = factory.makeModule(name.asString());
center.pack_start(module, true, false, 10); if (module)
center.pack_start(*module, true, false, 10);
} }
} }
if (_config["modules-right"]) { if (_config["modules-right"]) {
std::reverse(_config["modules-right"].begin(), _config["modules-right"].end()); std::reverse(_config["modules-right"].begin(), _config["modules-right"].end());
for (auto name : _config["modules-right"]) { for (auto name : _config["modules-right"]) {
auto &module = factory.makeModule(name.asString()); auto module = factory.makeModule(name.asString());
right.pack_end(module, false, false, 0); if (module)
right.pack_end(*module, false, false, 0);
} }
} }
} }

View File

@ -46,9 +46,11 @@ void waybar::Client::_handle_global(void *data, struct wl_registry *registry,
o->layer_shell = (zwlr_layer_shell_v1 *)wl_registry_bind(registry, name, o->layer_shell = (zwlr_layer_shell_v1 *)wl_registry_bind(registry, name,
&zwlr_layer_shell_v1_interface, version); &zwlr_layer_shell_v1_interface, version);
} else if (!strcmp(interface, wl_output_interface.name)) { } else if (!strcmp(interface, wl_output_interface.name)) {
auto output = std::make_unique<struct wl_output *>(); o->wlOutput = (struct wl_output *)wl_registry_bind(registry, name,
*output = (struct wl_output *)wl_registry_bind(registry, name,
&wl_output_interface, version); &wl_output_interface, version);
auto output = std::make_unique<struct wl_output *>();
*output = o->wlOutput;
if (o->xdg_output_manager)
o->bars.emplace_back(std::make_unique<Bar>(*o, std::move(output))); o->bars.emplace_back(std::make_unique<Bar>(*o, std::move(output)));
} else if (!strcmp(interface, wl_seat_interface.name)) { } else if (!strcmp(interface, wl_seat_interface.name)) {
o->seat = (struct wl_seat *)wl_registry_bind(registry, name, o->seat = (struct wl_seat *)wl_registry_bind(registry, name,
@ -58,6 +60,11 @@ void waybar::Client::_handle_global(void *data, struct wl_registry *registry,
o->xdg_output_manager = o->xdg_output_manager =
(struct zxdg_output_manager_v1 *)wl_registry_bind(registry, name, (struct zxdg_output_manager_v1 *)wl_registry_bind(registry, name,
&zxdg_output_manager_v1_interface, ZXDG_OUTPUT_V1_NAME_SINCE_VERSION); &zxdg_output_manager_v1_interface, ZXDG_OUTPUT_V1_NAME_SINCE_VERSION);
if (o->wlOutput) {
auto output = std::make_unique<struct wl_output *>();
*output = o->wlOutput;
o->bars.emplace_back(std::make_unique<Bar>(*o, std::move(output)));
}
} }
} }

View File

@ -4,23 +4,32 @@ waybar::Factory::Factory(Bar &bar, Json::Value config)
: _bar(bar), _config(config) : _bar(bar), _config(config)
{} {}
waybar::IModule &waybar::Factory::makeModule(std::string name) waybar::IModule *waybar::Factory::makeModule(std::string name)
{ {
try {
if (name == "battery") if (name == "battery")
return *new waybar::modules::Battery(_config[name]); return new waybar::modules::Battery(_config[name]);
if (name == "workspaces") if (name == "workspaces")
return *new waybar::modules::Workspaces(_bar); return new waybar::modules::Workspaces(_bar, _config[name]);
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]);
if (name == "network") if (name == "network")
return *new waybar::modules::Network(_config[name]); return new waybar::modules::Network(_config[name]);
if (name == "pulseaudio") if (name == "pulseaudio")
return *new waybar::modules::Pulseaudio(_config[name]); return new waybar::modules::Pulseaudio(_config[name]);
if (!name.compare(0, 7, "custom/") && name.size() > 7) if (!name.compare(0, 7, "custom/") && name.size() > 7)
return *new waybar::modules::Custom(name.substr(7), _config[name]); return new waybar::modules::Custom(name.substr(7), _config[name]);
throw std::runtime_error("Unknown module: " + name); std::cerr << "Unknown module: " + name << std::endl;
} catch (const std::exception& e) {
auto err = fmt::format("Disabling module \"{}\", {}", name, e.what());
std::cerr << err << std::endl;
} catch (...) {
auto err = fmt::format("Disabling module \"{}\", Unknown reason", name);
std::cerr << err << std::endl;
}
return nullptr;
} }

View File

@ -48,10 +48,11 @@ int ipc_open_socket(std::string socket_path) {
} }
struct ipc_response ipc_recv_response(int socketfd) { struct ipc_response ipc_recv_response(int socketfd) {
struct ipc_response response;
char data[ipc_header_size]; char data[ipc_header_size];
uint32_t *data32 = (uint32_t *)(data + sizeof(ipc_magic)); uint32_t *data32 = (uint32_t *)(data + sizeof(ipc_magic));
size_t total = 0; size_t total = 0;
while (total < ipc_header_size) { while (total < ipc_header_size) {
ssize_t received = recv(socketfd, data + total, ipc_header_size - total, 0); ssize_t received = recv(socketfd, data + total, ipc_header_size - total, 0);
if (received <= 0) { if (received <= 0) {
@ -60,8 +61,6 @@ struct ipc_response ipc_recv_response(int socketfd) {
total += received; total += received;
} }
struct ipc_response response;
total = 0; total = 0;
response.size = data32[0]; response.size = data32[0];
response.type = data32[1]; response.type = data32[1];
@ -86,14 +85,10 @@ std::string ipc_single_command(int socketfd, uint32_t type, const char *payload,
data32[0] = *len; data32[0] = *len;
data32[1] = type; data32[1] = type;
if (send(socketfd, data, ipc_header_size, 0) == -1) { if (send(socketfd, 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, *len, 0) == -1)
if (send(socketfd, payload, *len, 0) == -1) {
throw std::runtime_error("Unable to send IPC payload"); throw std::runtime_error("Unable to send IPC payload");
}
struct ipc_response resp = ipc_recv_response(socketfd); struct ipc_response resp = ipc_recv_response(socketfd);
*len = resp.size; *len = resp.size;
return resp.payload; return resp.payload;

View File

@ -1,7 +1,5 @@
#include <gtkmm.h>
#include <wayland-client.hpp>
#include <gdk/gdkwayland.h>
#include <csignal> #include <csignal>
#include <iostream>
#include "client.hpp" #include "client.hpp"
namespace waybar { namespace waybar {

View File

@ -6,65 +6,73 @@ waybar::modules::Battery::Battery(Json::Value config)
try { try {
for (auto &node : fs::directory_iterator(_data_dir)) { for (auto &node : fs::directory_iterator(_data_dir)) {
if (fs::is_directory(node) && fs::exists(node / "capacity") if (fs::is_directory(node) && fs::exists(node / "capacity")
&& fs::exists(node / "status")) { && fs::exists(node / "status") && fs::exists(node / "uevent"))
_batteries.push_back(node); _batteries.push_back(node);
} }
}
} catch (fs::filesystem_error &e) { } catch (fs::filesystem_error &e) {
std::cerr << e.what() << std::endl; throw std::runtime_error(e.what());
} }
if (!_batteries.size()) { if (!_batteries.size())
std::cerr << "No batteries." << std::endl; throw std::runtime_error("No batteries.");
auto fd = inotify_init();
if (fd == -1)
throw std::runtime_error("Unable to listen batteries.");
for (auto &bat : _batteries)
inotify_add_watch(fd, (bat / "uevent").c_str(), IN_ACCESS);
// Trigger first value
update();
_label.set_name("battery");
_thread = [this, fd] {
struct inotify_event event;
int nbytes = read(fd, &event, sizeof(event));
if (nbytes != sizeof(event))
return; return;
}
_label.get_style_context()->add_class("battery");
int interval = _config["interval"] ? _config["inveral"].asInt() : 1;
_thread = [this, interval] {
Glib::signal_idle().connect_once(sigc::mem_fun(*this, &Battery::update)); Glib::signal_idle().connect_once(sigc::mem_fun(*this, &Battery::update));
_thread.sleep_for(chrono::seconds(interval));
}; };
} }
auto waybar::modules::Battery::update() -> void auto waybar::modules::Battery::update() -> void
{ {
try { try {
int total = 0; uint16_t total = 0;
bool charging = false; bool charging = false;
for (auto &bat : _batteries) {
int capacity;
std::string status; std::string status;
for (auto &bat : _batteries) {
uint16_t capacity;
std::ifstream(bat / "capacity") >> capacity; std::ifstream(bat / "capacity") >> capacity;
total += capacity;
std::ifstream(bat / "status") >> status; std::ifstream(bat / "status") >> status;
if (status == "Charging") { if (status == "Charging")
charging = true; charging = true;
total += capacity;
} }
} uint16_t capacity = total / _batteries.size();
auto format = _config["format"] ? _config["format"].asString() : "{}%"; auto format = _config["format"]
auto value = total / _batteries.size(); ? _config["format"].asString() : "{capacity}%";
_label.set_text(fmt::format(format, fmt::arg("value", value), _label.set_text(fmt::format(format, fmt::arg("capacity", capacity),
fmt::arg("icon", _getIcon(value)))); fmt::arg("icon", _getIcon(capacity))));
_label.set_tooltip_text(charging ? "Charging" : "Discharging"); _label.set_tooltip_text(status);
if (charging) if (charging)
_label.get_style_context()->add_class("charging"); _label.get_style_context()->add_class("charging");
else else
_label.get_style_context()->remove_class("charging"); _label.get_style_context()->remove_class("charging");
if (value < 16 && !charging) auto critical = _config["critical"] ? _config["critical"].asUInt() : 15;
if (capacity <= critical && !charging)
_label.get_style_context()->add_class("warning"); _label.get_style_context()->add_class("warning");
else else
_label.get_style_context()->remove_class("warning"); _label.get_style_context()->remove_class("warning");
} catch (std::exception &e) { } catch (const std::exception& e) {
std::cerr << e.what() << std::endl; std::cerr << e.what() << std::endl;
} }
} }
std::string waybar::modules::Battery::_getIcon(uint32_t percentage) std::string waybar::modules::Battery::_getIcon(uint16_t percentage)
{ {
if (!_config["format-icons"] || !_config["format-icons"].isArray()) return ""; if (!_config["format-icons"] || !_config["format-icons"].isArray()) return "";
auto step = 100 / _config["format-icons"].size(); auto size = _config["format-icons"].size();
return _config["format-icons"][percentage / step].asString(); auto idx = std::clamp(percentage / (100 / size), 0U, size - 1);
return _config["format-icons"][idx].asString();
} }
waybar::modules::Battery::operator Gtk::Widget &() waybar::modules::Battery::operator Gtk::Widget &()

View File

@ -3,7 +3,7 @@
waybar::modules::Clock::Clock(Json::Value config) waybar::modules::Clock::Clock(Json::Value config)
: _config(config) : _config(config)
{ {
_label.get_style_context()->add_class("clock"); _label.set_name("clock");
_thread = [this] { _thread = [this] {
Glib::signal_idle().connect_once(sigc::mem_fun(*this, &Clock::update)); Glib::signal_idle().connect_once(sigc::mem_fun(*this, &Clock::update));
auto now = waybar::chrono::clock::now(); auto now = waybar::chrono::clock::now();

View File

@ -3,7 +3,7 @@
waybar::modules::Cpu::Cpu(Json::Value config) waybar::modules::Cpu::Cpu(Json::Value config)
: _config(config) : _config(config)
{ {
_label.get_style_context()->add_class("cpu"); _label.set_name("cpu");
int interval = _config["interval"] ? _config["inveral"].asInt() : 10; int interval = _config["interval"] ? _config["inveral"].asInt() : 10;
_thread = [this, interval] { _thread = [this, interval] {
Glib::signal_idle().connect_once(sigc::mem_fun(*this, &Cpu::update)); Glib::signal_idle().connect_once(sigc::mem_fun(*this, &Cpu::update));

View File

@ -4,10 +4,8 @@
waybar::modules::Custom::Custom(std::string name, Json::Value config) waybar::modules::Custom::Custom(std::string name, Json::Value config)
: _name(name), _config(config) : _name(name), _config(config)
{ {
if (!_config["exec"]) { if (!_config["exec"])
std::cerr << name + " has no exec path." << std::endl; throw std::runtime_error(name + " has no exec path.");
return;
}
int interval = _config["interval"] ? _config["inveral"].asInt() : 30; int interval = _config["interval"] ? _config["inveral"].asInt() : 30;
_thread = [this, interval] { _thread = [this, interval] {
Glib::signal_idle().connect_once(sigc::mem_fun(*this, &Custom::update)); Glib::signal_idle().connect_once(sigc::mem_fun(*this, &Custom::update));
@ -36,10 +34,10 @@ auto waybar::modules::Custom::update() -> void
// Hide label if output is empty // Hide label if output is empty
if (output.empty()) { if (output.empty()) {
_label.get_style_context()->remove_class("custom-" + _name); _label.set_name("");
_label.hide(); _label.hide();
} else { } else {
_label.get_style_context()->add_class("custom-" + _name); _label.set_name("custom-" + _name);
auto format = _config["format"] ? _config["format"].asString() : "{}"; auto format = _config["format"] ? _config["format"].asString() : "{}";
_label.set_text(fmt::format(format, output)); _label.set_text(fmt::format(format, output));
_label.show(); _label.show();

View File

@ -3,7 +3,7 @@
waybar::modules::Memory::Memory(Json::Value config) waybar::modules::Memory::Memory(Json::Value config)
: _config(config) : _config(config)
{ {
_label.get_style_context()->add_class("memory"); _label.set_name("memory");
int interval = _config["interval"] ? _config["inveral"].asInt() : 30; int interval = _config["interval"] ? _config["inveral"].asInt() : 30;
_thread = [this, interval] { _thread = [this, interval] {
Glib::signal_idle().connect_once(sigc::mem_fun(*this, &Memory::update)); Glib::signal_idle().connect_once(sigc::mem_fun(*this, &Memory::update));

View File

@ -1,9 +1,11 @@
#include "modules/network.hpp" #include "modules/network.hpp"
waybar::modules::Network::Network(Json::Value config) waybar::modules::Network::Network(Json::Value config)
: _config(config), _ifid(if_nametoindex(config["interface"].asString().c_str())) : _config(config), _ifid(if_nametoindex(config["interface"].asCString()))
{ {
_label.get_style_context()->add_class("network"); if (_ifid == 0)
throw std::runtime_error("Can't found network interface");
_label.set_name("network");
int interval = _config["interval"] ? _config["inveral"].asInt() : 30; int interval = _config["interval"] ? _config["inveral"].asInt() : 30;
_thread = [this, interval] { _thread = [this, interval] {
Glib::signal_idle().connect_once(sigc::mem_fun(*this, &Network::update)); Glib::signal_idle().connect_once(sigc::mem_fun(*this, &Network::update));
@ -14,7 +16,7 @@ waybar::modules::Network::Network(Json::Value config)
auto waybar::modules::Network::update() -> void auto waybar::modules::Network::update() -> void
{ {
_getInfo(); _getInfo();
auto format = _config["format"] ? _config["format"].asString() : "{}"; auto format = _config["format"] ? _config["format"].asString() : "{essid}";
_label.set_text(fmt::format(format, _label.set_text(fmt::format(format,
fmt::arg("essid", _essid), fmt::arg("essid", _essid),
fmt::arg("signaldBm", _signalStrengthdBm), fmt::arg("signaldBm", _signalStrengthdBm),
@ -103,8 +105,6 @@ bool waybar::modules::Network::_associatedOrJoined(struct nlattr** bss)
auto waybar::modules::Network::_getInfo() -> void auto waybar::modules::Network::_getInfo() -> void
{ {
if (_ifid == 0)
return;
struct nl_sock *sk = nl_socket_alloc(); struct nl_sock *sk = nl_socket_alloc();
if (genl_connect(sk) != 0) { if (genl_connect(sk) != 0) {
nl_socket_free(sk); nl_socket_free(sk);

View File

@ -4,7 +4,7 @@ waybar::modules::Pulseaudio::Pulseaudio(Json::Value config)
: _config(config), _mainloop(nullptr), _mainloop_api(nullptr), : _config(config), _mainloop(nullptr), _mainloop_api(nullptr),
_context(nullptr), _sinkIdx(0), _volume(0), _muted(false) _context(nullptr), _sinkIdx(0), _volume(0), _muted(false)
{ {
_label.get_style_context()->add_class("pulseaudio"); _label.set_name("pulseaudio");
_mainloop = pa_threaded_mainloop_new(); _mainloop = pa_threaded_mainloop_new();
if (!_mainloop) if (!_mainloop)
throw std::runtime_error("pa_mainloop_new() failed."); throw std::runtime_error("pa_mainloop_new() failed.");
@ -97,17 +97,27 @@ void waybar::modules::Pulseaudio::_serverInfoCb(pa_context *context,
auto waybar::modules::Pulseaudio::update() -> void auto waybar::modules::Pulseaudio::update() -> void
{ {
auto format = _config["format"] ? _config["format"].asString() : "{}%"; auto 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 } else
_label.get_style_context()->remove_class("muted"); _label.get_style_context()->remove_class("muted");
_label.set_label(fmt::format(format, _volume)); _label.set_label(fmt::format(format,
fmt::arg("volume", _volume),
fmt::arg("icon", _getIcon(_volume))));
_label.set_tooltip_text(_desc); _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();
auto idx = std::clamp(percentage / (100 / size), 0U, size - 1);
return _config["format-icons"][idx].asString();
}
waybar::modules::Pulseaudio::operator Gtk::Widget &() { waybar::modules::Pulseaudio::operator Gtk::Widget &() {
return _label; return _label;
} }

View File

@ -1,104 +1,182 @@
#include "modules/workspaces.hpp" #include "modules/workspaces.hpp"
#include "ipc/client.hpp" #include "ipc/client.hpp"
waybar::modules::Workspaces::Workspaces(Bar &bar) waybar::modules::Workspaces::Workspaces(Bar &bar, Json::Value config)
: _bar(bar) : _bar(bar), _config(config), _scrolling(false)
{ {
_box.get_style_context()->add_class("workspaces"); _box.set_name("workspaces");
try {
std::string socketPath = get_socketpath(); std::string socketPath = get_socketpath();
_ipcSocketfd = ipc_open_socket(socketPath); _ipcfd = ipc_open_socket(socketPath);
_ipcEventSocketfd = ipc_open_socket(socketPath); _ipcEventfd = ipc_open_socket(socketPath);
const char *subscribe = "[ \"workspace\", \"mode\" ]"; const char *subscribe = "[ \"workspace\" ]";
uint32_t len = strlen(subscribe); uint32_t len = strlen(subscribe);
ipc_single_command(_ipcEventSocketfd, IPC_SUBSCRIBE, subscribe, &len); ipc_single_command(_ipcEventfd, IPC_SUBSCRIBE, subscribe, &len);
_thread = [this] {
try {
if (_bar.outputName.empty()) {
// Wait for the name of the output
while (_bar.outputName.empty())
_thread.sleep_for(chrono::milliseconds(150));
} else
ipc_recv_response(_ipcEventfd);
uint32_t len = 0;
std::lock_guard<std::mutex> lock(_mutex);
auto str = ipc_single_command(_ipcfd, IPC_GET_WORKSPACES, nullptr, &len);
_workspaces = _getWorkspaces(str);
Glib::signal_idle().connect_once(sigc::mem_fun(*this, &Workspaces::update));
} catch (const std::exception& e) { } catch (const std::exception& e) {
std::cerr << e.what() << std::endl; std::cerr << e.what() << std::endl;
return;
} }
_thread = [this] {
Glib::signal_idle().connect_once(sigc::mem_fun(*this, &Workspaces::update));
_thread.sleep_for(chrono::milliseconds(250));
}; };
} }
auto waybar::modules::Workspaces::update() -> void auto waybar::modules::Workspaces::update() -> void
{ {
if (_bar.outputName.empty()) return; std::lock_guard<std::mutex> lock(_mutex);
Json::Value workspaces = _getWorkspaces();
bool needReorder = false; bool needReorder = false;
for (auto it = _buttons.begin(); it != _buttons.end(); ++it) { 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; });
if (ws == workspaces.end()) { if (ws == _workspaces.end()) {
it = _buttons.erase(it); it = _buttons.erase(it);
needReorder = true; needReorder = true;
} else
++it;
} }
} for (auto node : _workspaces) {
for (auto node : workspaces) { if (_bar.outputName != node["output"].asString())
continue;
auto it = _buttons.find(node["num"].asInt()); auto it = _buttons.find(node["num"].asInt());
if (it == _buttons.end() && _bar.outputName == node["output"].asString()) { if (it == _buttons.end()) {
_addWorkspace(node); _addWorkspace(node);
needReorder = true; needReorder = true;
} else { } else {
auto styleContext = it->second.get_style_context(); auto &button = it->second;
bool isCurrent = node["focused"].asBool(); if (node["focused"].asBool())
if (!isCurrent) { button.get_style_context()->add_class("current");
styleContext->remove_class("current"); else
} else if (isCurrent) { button.get_style_context()->remove_class("current");
styleContext->add_class("current");
}
if (needReorder) if (needReorder)
_box.reorder_child(it->second, node["num"].asInt() - 1); _box.reorder_child(button, node["num"].asInt());
it->second.show(); button.show();
} }
} }
if (_scrolling)
_scrolling = false;
} }
void waybar::modules::Workspaces::_addWorkspace(Json::Value node) void waybar::modules::Workspaces::_addWorkspace(Json::Value node)
{ {
auto pair = _buttons.emplace(node["num"].asInt(), node["name"].asString()); auto pair = _buttons.emplace(node["num"].asInt(),
_getIcon(node["name"].asString()));
auto &button = pair.first->second; auto &button = pair.first->second;
_box.pack_start(button, false, false, 0); _box.pack_start(button, false, false, 0);
button.set_relief(Gtk::RELIEF_NONE); button.set_relief(Gtk::RELIEF_NONE);
button.signal_clicked().connect([this, pair] { button.signal_clicked().connect([this, pair] {
try { try {
std::lock_guard<std::mutex> lock(_mutex);
auto value = fmt::format("workspace \"{}\"", pair.first->first); auto value = fmt::format("workspace \"{}\"", pair.first->first);
uint32_t size = value.size(); uint32_t size = value.size();
ipc_single_command(_ipcSocketfd, IPC_COMMAND, value.c_str(), &size); ipc_single_command(_ipcfd, IPC_COMMAND, value.c_str(), &size);
} catch (const std::exception& e) { } catch (const std::exception& e) {
std::cerr << e.what() << std::endl; std::cerr << e.what() << std::endl;
} }
}); });
_box.reorder_child(button, node["num"].asInt() - 1); button.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK);
if (node["focused"].asBool()) { button.signal_scroll_event()
.connect(sigc::mem_fun(*this, &Workspaces::_handleScroll));
_box.reorder_child(button, node["num"].asInt());
if (node["focused"].asBool())
button.get_style_context()->add_class("current"); button.get_style_context()->add_class("current");
}
button.show(); button.show();
} }
Json::Value waybar::modules::Workspaces::_getWorkspaces() std::string waybar::modules::Workspaces::_getIcon(std::string name)
{ {
uint32_t len = 0; if (_config["format-icons"][name])
Json::Value root; return _config["format-icons"][name].asString();
Json::CharReaderBuilder builder; if (_config["format-icons"]["default"])
Json::CharReader* reader = builder.newCharReader(); return _config["format-icons"]["default"].asString();
try { return name;
std::string str = ipc_single_command(_ipcSocketfd, IPC_GET_WORKSPACES, }
nullptr, &len);
std::string err; bool waybar::modules::Workspaces::_handleScroll(GdkEventScroll *e)
bool res = {
reader->parse(str.c_str(), str.c_str() + str.size(), &root, &err); std::lock_guard<std::mutex> lock(_mutex);
delete reader; // Avoid concurrent scroll event
if (!res) { if (_scrolling)
std::cerr << err << std::endl; return false;
return root; _scrolling = true;
int id = -1;
uint16_t idx = 0;
for (; idx < _workspaces.size(); idx += 1)
if (_workspaces[idx]["focused"].asBool()) {
id = _workspaces[idx]["num"].asInt();
break;
} }
if (id == -1) {
_scrolling = false;
return false;
}
if (e->direction == GDK_SCROLL_UP)
id = _getNextWorkspace();
if (e->direction == GDK_SCROLL_DOWN)
id = _getPrevWorkspace();
if (e->direction == GDK_SCROLL_SMOOTH) {
gdouble delta_x, delta_y;
gdk_event_get_scroll_deltas ((const GdkEvent *) e, &delta_x, &delta_y);
if (delta_y < 0)
id = _getNextWorkspace();
else if (delta_y > 0)
id = _getPrevWorkspace();
}
if (id == _workspaces[idx]["num"].asInt()) {
_scrolling = false;
return false;
}
auto value = fmt::format("workspace \"{}\"", id);
uint32_t size = value.size();
ipc_single_command(_ipcfd, IPC_COMMAND, value.c_str(), &size);
std::this_thread::sleep_for(std::chrono::milliseconds(150));
return true;
}
int waybar::modules::Workspaces::_getPrevWorkspace()
{
int current = -1;
for (uint16_t i = 0; i != _workspaces.size(); i += 1)
if (_workspaces[i]["focused"].asBool()) {
current = _workspaces[i]["num"].asInt();
if (i > 0)
return _workspaces[i - 1]["num"].asInt();
return _workspaces[_workspaces.size() - 1]["num"].asInt();
}
return current;
}
int waybar::modules::Workspaces::_getNextWorkspace()
{
int current = -1;
for (uint16_t i = 0; i != _workspaces.size(); i += 1)
if (_workspaces[i]["focused"].asBool()) {
current = _workspaces[i]["num"].asInt();
if (i + 1U < _workspaces.size())
return _workspaces[i + 1]["num"].asInt();
return _workspaces[0]["num"].asInt();
}
return current;
}
Json::Value waybar::modules::Workspaces::_getWorkspaces(const std::string data)
{
Json::Value res;
try {
std::string err;
res = _parser.parse(data);
} catch (const std::exception& e) { } catch (const std::exception& e) {
std::cerr << e.what() << std::endl; std::cerr << e.what() << std::endl;
} }
return root; return res;
} }
waybar::modules::Workspaces::operator Gtk::Widget &() { waybar::modules::Workspaces::operator Gtk::Widget &() {