Merge branch 'master' into list_of_times

This commit is contained in:
Alex
2022-03-08 09:59:22 +01:00
committed by GitHub
55 changed files with 1062 additions and 286 deletions

28
src/AIconLabel.cpp Normal file
View File

@ -0,0 +1,28 @@
#include "AIconLabel.hpp"
#include <gdkmm/pixbuf.h>
namespace waybar {
AIconLabel::AIconLabel(const Json::Value &config, const std::string &name, const std::string &id,
const std::string &format, uint16_t interval, bool ellipsize,
bool enable_click, bool enable_scroll)
: ALabel(config, name, id, format, interval, ellipsize, enable_click, enable_scroll) {
event_box_.remove();
box_.set_orientation(Gtk::Orientation::ORIENTATION_HORIZONTAL);
box_.set_spacing(8);
box_.add(image_);
box_.add(label_);
event_box_.add(box_);
}
auto AIconLabel::update() -> void {
image_.set_visible(image_.get_visible() && iconEnabled());
ALabel::update();
}
bool AIconLabel::iconEnabled() const {
return config_["icon"].isBool() ? config_["icon"].asBool() : true;
}
} // namespace waybar

View File

@ -6,7 +6,9 @@ namespace waybar {
AModule::AModule(const Json::Value& config, const std::string& name, const std::string& id,
bool enable_click, bool enable_scroll)
: name_(std::move(name)), config_(std::move(config)) {
: name_(std::move(name)), config_(std::move(config))
, distance_scrolled_y_(0.0)
, distance_scrolled_x_(0.0) {
// configure events' user commands
if (config_["on-click"].isString() || config_["on-click-middle"].isString() ||
config_["on-click-backward"].isString() || config_["on-click-forward"].isString() ||

View File

@ -735,21 +735,22 @@ void waybar::Bar::getModules(const Factory& factory, const std::string& pos, Gtk
module = factory.makeModule(ref);
}
modules_all_.emplace_back(module);
std::shared_ptr<AModule> module_sp(module);
modules_all_.emplace_back(module_sp);
if (group) {
group->pack_start(*module, false, false);
} else {
if (pos == "modules-left") {
modules_left_.emplace_back(module);
modules_left_.emplace_back(module_sp);
}
if (pos == "modules-center") {
modules_center_.emplace_back(module);
modules_center_.emplace_back(module_sp);
}
if (pos == "modules-right") {
modules_right_.emplace_back(module);
modules_right_.emplace_back(module_sp);
}
}
module->dp.connect([module, &name] {
module->dp.connect([module, name] {
try {
module->update();
} catch (const std::exception& e) {
@ -766,7 +767,11 @@ void waybar::Bar::getModules(const Factory& factory, const std::string& pos, Gtk
auto waybar::Bar::setupWidgets() -> void {
window.add(box_);
box_.pack_start(left_, false, false);
box_.set_center_widget(center_);
if (config["fixed-center"].isBool() ? config["fixed-center"].asBool() : true) {
box_.set_center_widget(center_);
} else {
box_.pack_start(center_, true, false);
}
box_.pack_end(right_, false, false);
// Convert to button code for every module that is used.

View File

@ -94,6 +94,11 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const {
if (ref == "sndio") {
return new waybar::modules::Sndio(id, config_[name]);
}
#endif
#ifdef HAVE_GIO_UNIX
if (ref == "inhibitor") {
return new waybar::modules::Inhibitor(id, bar_, config_[name]);
}
#endif
if (ref == "temperature") {
return new waybar::modules::Temperature(id, config_[name]);

View File

@ -161,7 +161,7 @@ const std::tuple<uint8_t, float, std::string, float> waybar::modules::Battery::g
uint32_t energy_now;
uint32_t energy_full_design;
std::string _status;
std::ifstream(bat / "status") >> _status;
std::getline(std::ifstream(bat / "status"), _status);
// Some battery will report current and charge in μA/μAh.
// Scale these by the voltage to get μW/μWh.

View File

@ -1,17 +1,24 @@
#include "modules/clock.hpp"
#include <time.h>
#include <spdlog/spdlog.h>
#if FMT_VERSION < 60000
#include <fmt/time.h>
#else
#include <fmt/chrono.h>
#endif
#include <ctime>
#include <sstream>
#include <type_traits>
#include "util/ustring_clen.hpp"
#include "util/waybar_time.hpp"
#ifdef HAVE_LANGINFO_1STDAY
#include <langinfo.h>
#include <locale.h>
#endif
using waybar::modules::waybar_time;
using waybar::waybar_time;
waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
: ALabel(config, "clock", id, "{:%H:%M}", 60, false, false, true),
@ -96,7 +103,7 @@ auto waybar::modules::Clock::update() -> void {
// As date dep is not fully compatible, prefer fmt
tzset();
auto localtime = fmt::localtime(std::chrono::system_clock::to_time_t(now));
text = fmt::format(format_, localtime);
text = fmt::format(locale_, format_, localtime);
} else {
text = fmt::format(format_, wtime);
}
@ -114,10 +121,10 @@ auto waybar::modules::Clock::update() -> void {
}
auto tooltip_format = config_["tooltip-format"].asString();
text = fmt::format(tooltip_format, wtime, fmt::arg(kCalendarPlaceholder.c_str(), calendar_lines), fmt::arg(KTimezonedTimeListPlaceholder.c_str(), timezoned_time_lines));
label_.set_tooltip_markup(text);
}
}
label_.set_tooltip_markup(text);
// Call parent update
ALabel::update();
}
@ -255,14 +262,3 @@ auto waybar::modules::Clock::first_day_of_week() -> date::weekday {
#endif
return date::Sunday;
}
template <>
struct fmt::formatter<waybar_time> : fmt::formatter<std::tm> {
template <typename FormatContext>
auto format(const waybar_time& t, FormatContext& ctx) {
#if FMT_VERSION >= 80000
auto& tm_format = specs;
#endif
return format_to(ctx.out(), "{}", date::format(t.locale, fmt::to_string(tm_format), t.ztime));
}
};

View File

@ -62,7 +62,7 @@ auto waybar::modules::Cpu::update() -> void {
double waybar::modules::Cpu::getCpuLoad() {
double load[1];
if (getloadavg(load, 1) != -1) {
return load[0];
return std::ceil(load[0] * 100.0) / 100.0;
}
throw std::runtime_error("Can't get Cpu load");
}

175
src/modules/inhibitor.cpp Normal file
View File

@ -0,0 +1,175 @@
#include "modules/inhibitor.hpp"
#include <gio/gio.h>
#include <gio/gunixfdlist.h>
#include <spdlog/spdlog.h>
namespace {
using DBus = std::unique_ptr<GDBusConnection, void(*)(GDBusConnection*)>;
auto dbus() -> DBus {
GError *error = nullptr;
GDBusConnection* connection =
g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
if (error) {
spdlog::error("g_bus_get_sync() failed: {}", error->message);
g_error_free(error);
connection = nullptr;
}
auto destructor = [](GDBusConnection* connection) {
GError *error = nullptr;
g_dbus_connection_close_sync(connection, nullptr, &error);
if (error) {
spdlog::error(
"g_bus_connection_close_sync failed(): {}",
error->message);
g_error_free(error);
}
};
return DBus{connection, destructor};
}
auto getLocks(const DBus& bus, const std::string& inhibitors) -> int {
GError *error = nullptr;
GUnixFDList* fd_list;
int handle;
auto reply = g_dbus_connection_call_with_unix_fd_list_sync(bus.get(),
"org.freedesktop.login1",
"/org/freedesktop/login1",
"org.freedesktop.login1.Manager",
"Inhibit",
g_variant_new(
"(ssss)",
inhibitors.c_str(),
"waybar",
"Asked by user",
"block"),
G_VARIANT_TYPE("(h)"),
G_DBUS_CALL_FLAGS_NONE,
-1,
nullptr,
&fd_list,
nullptr,
&error);
if (error) {
spdlog::error(
"g_dbus_connection_call_with_unix_fd_list_sync() failed: {}",
error->message);
g_error_free(error);
handle = -1;
} else {
gint index;
g_variant_get(reply, "(h)", &index);
g_variant_unref(reply);
handle = g_unix_fd_list_get(fd_list, index, nullptr);
g_object_unref(fd_list);
}
return handle;
}
auto checkInhibitor(const std::string& inhibitor) -> const std::string& {
static const auto inhibitors = std::array{
"idle",
"shutdown",
"sleep",
"handle-power-key",
"handle-suspend-key",
"handle-hibernate-key",
"handle-lid-switch"
};
if (std::find(inhibitors.begin(), inhibitors.end(), inhibitor)
== inhibitors.end()) {
throw std::runtime_error("invalid logind inhibitor " + inhibitor);
}
return inhibitor;
}
auto getInhibitors(const Json::Value& config) -> std::string {
std::string inhibitors = "idle";
if (config["what"].empty()) {
return inhibitors;
}
if (config["what"].isString()) {
return checkInhibitor(config["what"].asString());
}
if (config["what"].isArray()) {
inhibitors = checkInhibitor(config["what"][0].asString());
for (decltype(config["what"].size()) i = 1; i < config["what"].size(); ++i) {
inhibitors += ":" + checkInhibitor(config["what"][i].asString());
}
return inhibitors;
}
return inhibitors;
}
}
namespace waybar::modules {
Inhibitor::Inhibitor(const std::string& id, const Bar& bar,
const Json::Value& config)
: ALabel(config, "inhibitor", id, "{status}", true),
dbus_(::dbus()),
inhibitors_(::getInhibitors(config)) {
event_box_.add_events(Gdk::BUTTON_PRESS_MASK);
event_box_.signal_button_press_event().connect(
sigc::mem_fun(*this, &Inhibitor::handleToggle));
dp.emit();
}
Inhibitor::~Inhibitor() {
if (handle_ != -1) {
::close(handle_);
}
}
auto Inhibitor::activated() -> bool {
return handle_ != -1;
}
auto Inhibitor::update() -> void {
std::string status_text = activated() ? "activated" : "deactivated";
label_.get_style_context()->remove_class(
activated() ? "deactivated" : "activated");
label_.set_markup(
fmt::format(format_, fmt::arg("status", status_text),
fmt::arg("icon", getIcon(0, status_text))));
label_.get_style_context()->add_class(status_text);
if (tooltipEnabled()) {
label_.set_tooltip_text(status_text);
}
return ALabel::update();
}
auto Inhibitor::handleToggle(GdkEventButton* const& e) -> bool {
if (e->button == 1) {
if (activated()) {
::close(handle_);
handle_ = -1;
} else {
handle_ = ::getLocks(dbus_, inhibitors_);
if (handle_ == -1) {
spdlog::error("cannot get inhibitor locks");
}
}
}
return ALabel::handleToggle(e);
}
} // waybar::modules

View File

@ -1,6 +1,8 @@
#include "modules/keyboard_state.hpp"
#include <errno.h>
#include <filesystem>
#include <spdlog/spdlog.h>
#include <string.h>
extern "C" {
#include <sys/types.h>
@ -8,6 +10,69 @@ extern "C" {
#include <fcntl.h>
}
class errno_error : public std::runtime_error {
public:
int code;
errno_error(int code, const std::string& msg)
: std::runtime_error(getErrorMsg(code, msg.c_str())),
code(code) {}
errno_error(int code, const char* msg)
: std::runtime_error(getErrorMsg(code, msg)),
code(code) {}
private:
static auto getErrorMsg(int err, const char* msg) -> std::string {
std::string error_msg{msg};
error_msg += ": ";
#if (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 32)
// strerrorname_np gets the error code's name; it's nice to have, but it's a recent GNU extension
const auto errno_name = strerrorname_np(err);
error_msg += errno_name;
error_msg += " ";
#endif
const auto errno_str = strerror(err);
error_msg += errno_str;
return error_msg;
}
};
auto openFile(const std::string& path, int flags) -> int {
int fd = open(path.c_str(), flags);
if (fd < 0) {
if (errno == EACCES) {
throw errno_error(errno, "Can't open " + path + " (are you in the input group?)");
} else {
throw errno_error(errno, "Can't open " + path);
}
}
return fd;
}
auto closeFile(int fd) -> void {
int res = close(fd);
if (res < 0) {
throw errno_error(errno, "Can't close file");
}
}
auto openDevice(int fd) -> libevdev* {
libevdev* dev;
int err = libevdev_new_from_fd(fd, &dev);
if (err < 0) {
throw errno_error(-err, "Can't create libevdev device");
}
return dev;
}
auto supportsLockStates(const libevdev* dev) -> bool {
return libevdev_has_event_type(dev, EV_LED)
&& libevdev_has_event_code(dev, EV_LED, LED_NUML)
&& libevdev_has_event_code(dev, EV_LED, LED_CAPSL)
&& libevdev_has_event_code(dev, EV_LED, LED_SCROLLL);
}
waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Bar& bar, const Json::Value& config)
: AModule(config, "keyboard-state", id, false, !config["disable-scroll"].asBool()),
box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0),
@ -48,26 +113,36 @@ waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Bar&
if (config_["device-path"].isString()) {
std::string dev_path = config_["device-path"].asString();
std::tie(fd_, dev_) = openDevice(dev_path);
fd_ = openFile(dev_path, O_NONBLOCK | O_CLOEXEC | O_RDONLY);
dev_ = openDevice(fd_);
} else {
DIR* dev_dir = opendir("/dev/input");
if (dev_dir == nullptr) {
throw std::runtime_error("Failed to open /dev/input");
throw errno_error(errno, "Failed to open /dev/input");
}
dirent *ep;
while ((ep = readdir(dev_dir))) {
if (ep->d_type != DT_CHR) continue;
std::string dev_path = std::string("/dev/input/") + ep->d_name;
int fd = openFile(dev_path.c_str(), O_NONBLOCK | O_CLOEXEC | O_RDONLY);
try {
std::tie(fd_, dev_) = openDevice(dev_path);
spdlog::info("Found device {} at '{}'", libevdev_get_name(dev_), dev_path);
break;
} catch (const std::runtime_error& e) {
continue;
auto dev = openDevice(fd);
if (supportsLockStates(dev)) {
spdlog::info("Found device {} at '{}'", libevdev_get_name(dev), dev_path);
fd_ = fd;
dev_ = dev;
break;
}
} catch (const errno_error& e) {
// ENOTTY just means the device isn't an evdev device, skip it
if (e.code != ENOTTY) {
spdlog::warn(e.what());
}
}
closeFile(fd);
}
if (dev_ == nullptr) {
throw std::runtime_error("Failed to find keyboard device");
throw errno_error(errno, "Failed to find keyboard device");
}
}
@ -79,35 +154,13 @@ waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Bar&
waybar::modules::KeyboardState::~KeyboardState() {
libevdev_free(dev_);
int err = close(fd_);
if (err < 0) {
// Not much we can do, so ignore it.
try {
closeFile(fd_);
} catch (const std::runtime_error& e) {
spdlog::warn(e.what());
}
}
auto waybar::modules::KeyboardState::openDevice(const std::string& path) -> std::pair<int, libevdev*> {
int fd = open(path.c_str(), O_NONBLOCK | O_CLOEXEC | O_RDONLY);
if (fd < 0) {
throw std::runtime_error("Can't open " + path);
}
libevdev* dev;
int err = libevdev_new_from_fd(fd, &dev);
if (err < 0) {
throw std::runtime_error("Can't create libevdev device");
}
if (!libevdev_has_event_type(dev, EV_LED)) {
throw std::runtime_error("Device doesn't support LED events");
}
if (!libevdev_has_event_code(dev, EV_LED, LED_NUML)
|| !libevdev_has_event_code(dev, EV_LED, LED_CAPSL)
|| !libevdev_has_event_code(dev, EV_LED, LED_SCROLLL)) {
throw std::runtime_error("Device doesn't support num lock, caps lock, or scroll lock events");
}
return std::make_pair(fd, dev);
}
auto waybar::modules::KeyboardState::update() -> void {
int err = LIBEVDEV_READ_STATUS_SUCCESS;
while (err == LIBEVDEV_READ_STATUS_SUCCESS) {
@ -117,8 +170,8 @@ auto waybar::modules::KeyboardState::update() -> void {
err = libevdev_next_event(dev_, LIBEVDEV_READ_FLAG_SYNC, &ev);
}
}
if (err != -EAGAIN) {
throw std::runtime_error("Failed to sync evdev device");
if (-err != EAGAIN) {
throw errno_error(-err, "Failed to sync evdev device");
}
int numl = libevdev_get_event_value(dev_, EV_LED, LED_NUML);

View File

@ -12,7 +12,15 @@ auto waybar::modules::Memory::update() -> void {
parseMeminfo();
unsigned long memtotal = meminfo_["MemTotal"];
unsigned long swaptotal = 0;
if (meminfo_.count("SwapTotal")) {
swaptotal = meminfo_["SwapTotal"];
}
unsigned long memfree;
unsigned long swapfree = 0;
if (meminfo_.count("SwapFree")) {
swapfree = meminfo_["SwapFree"];
}
if (meminfo_.count("MemAvailable")) {
// New kernels (3.4+) have an accurate available memory field.
memfree = meminfo_["MemAvailable"] + meminfo_["zfs_size"];
@ -24,9 +32,16 @@ auto waybar::modules::Memory::update() -> void {
if (memtotal > 0 && memfree >= 0) {
auto total_ram_gigabytes = memtotal / std::pow(1024, 2);
auto total_swap_gigabytes = swaptotal / std::pow(1024, 2);
int used_ram_percentage = 100 * (memtotal - memfree) / memtotal;
int used_swap_percentage = 0;
if (swaptotal && swapfree) {
used_swap_percentage = 100 * (swaptotal - swapfree) / swaptotal;
}
auto used_ram_gigabytes = (memtotal - memfree) / std::pow(1024, 2);
auto used_swap_gigabytes = (swaptotal - swapfree) / std::pow(1024, 2);
auto available_ram_gigabytes = memfree / std::pow(1024, 2);
auto available_swap_gigabytes = swapfree / std::pow(1024, 2);
auto format = format_;
auto state = getState(used_ram_percentage);
@ -43,9 +58,13 @@ auto waybar::modules::Memory::update() -> void {
used_ram_percentage,
fmt::arg("icon", getIcon(used_ram_percentage, icons)),
fmt::arg("total", total_ram_gigabytes),
fmt::arg("swapTotal", total_swap_gigabytes),
fmt::arg("percentage", used_ram_percentage),
fmt::arg("swapPercentage", used_swap_percentage),
fmt::arg("used", used_ram_gigabytes),
fmt::arg("avail", available_ram_gigabytes)));
fmt::arg("swapUsed", used_swap_gigabytes),
fmt::arg("avail", available_ram_gigabytes),
fmt::arg("swapAvail", available_swap_gigabytes)));
}
if (tooltipEnabled()) {
@ -54,9 +73,13 @@ auto waybar::modules::Memory::update() -> void {
label_.set_tooltip_text(fmt::format(tooltip_format,
used_ram_percentage,
fmt::arg("total", total_ram_gigabytes),
fmt::arg("swapTotal", total_swap_gigabytes),
fmt::arg("percentage", used_ram_percentage),
fmt::arg("swapPercentage", used_swap_percentage),
fmt::arg("used", used_ram_gigabytes),
fmt::arg("avail", available_ram_gigabytes)));
fmt::arg("swapUsed", used_swap_gigabytes),
fmt::arg("avail", available_ram_gigabytes),
fmt::arg("swapAvail", available_swap_gigabytes)));
} else {
label_.set_tooltip_text(fmt::format("{:.{}f}GiB used", used_ram_gigabytes, 1));
}

View File

@ -129,7 +129,7 @@ void waybar::modules::MPD::setLabel() {
album = getTag(MPD_TAG_ALBUM);
title = getTag(MPD_TAG_TITLE);
date = getTag(MPD_TAG_DATE);
song_pos = mpd_status_get_song_pos(status_.get());
song_pos = mpd_status_get_song_pos(status_.get()) + 1;
volume = mpd_status_get_volume(status_.get());
if (volume < 0) {
volume = 0;

View File

@ -78,7 +78,7 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf
family_(config["family"] == "ipv6" ? AF_INET6 : AF_INET),
efd_(-1),
ev_fd_(-1),
want_route_dump_(false),
want_route_dump_(true),
want_link_dump_(false),
want_addr_dump_(false),
dump_in_progress_(false),
@ -88,7 +88,7 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf
#ifdef WANT_RFKILL
rfkill_{RFKILL_TYPE_WLAN},
#endif
frequency_(0) {
frequency_(0.0) {
// Start with some "text" in the module's label_, update() will then
// update it. Since the text should be different, update() will be able
@ -106,7 +106,7 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf
}
if (!config_["interface"].isString()) {
// "interface" isn't configure, then try to guess the external
// "interface" isn't configured, then try to guess the external
// interface currently used for internet.
want_route_dump_ = true;
} else {
@ -331,12 +331,13 @@ auto waybar::modules::Network::update() -> void {
fmt::arg("essid", essid_),
fmt::arg("signaldBm", signal_strength_dbm_),
fmt::arg("signalStrength", signal_strength_),
fmt::arg("signalStrengthApp", signal_strength_app_),
fmt::arg("ifname", ifname_),
fmt::arg("netmask", netmask_),
fmt::arg("ipaddr", ipaddr_),
fmt::arg("gwaddr", gwaddr_),
fmt::arg("cidr", cidr_),
fmt::arg("frequency", frequency_),
fmt::arg("frequency", fmt::format("{:.1f}", frequency_)),
fmt::arg("icon", getIcon(signal_strength_, state_)),
fmt::arg("bandwidthDownBits", pow_format(bandwidth_down * 8ull / interval_.count(), "b/s")),
fmt::arg("bandwidthUpBits", pow_format(bandwidth_up * 8ull / interval_.count(), "b/s")),
@ -360,12 +361,13 @@ auto waybar::modules::Network::update() -> void {
fmt::arg("essid", essid_),
fmt::arg("signaldBm", signal_strength_dbm_),
fmt::arg("signalStrength", signal_strength_),
fmt::arg("signalStrengthApp", signal_strength_app_),
fmt::arg("ifname", ifname_),
fmt::arg("netmask", netmask_),
fmt::arg("ipaddr", ipaddr_),
fmt::arg("gwaddr", gwaddr_),
fmt::arg("cidr", cidr_),
fmt::arg("frequency", frequency_),
fmt::arg("frequency", fmt::format("{:.1f}", frequency_)),
fmt::arg("icon", getIcon(signal_strength_, state_)),
fmt::arg("bandwidthDownBits",
pow_format(bandwidth_down * 8ull / interval_.count(), "b/s")),
@ -403,7 +405,8 @@ void waybar::modules::Network::clearIface() {
cidr_ = 0;
signal_strength_dbm_ = 0;
signal_strength_ = 0;
frequency_ = 0;
signal_strength_app_.clear();
frequency_ = 0.0;
}
int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) {
@ -470,7 +473,8 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) {
net->essid_.clear();
net->signal_strength_dbm_ = 0;
net->signal_strength_ = 0;
net->frequency_ = 0;
net->signal_strength_app_.clear();
net->frequency_ = 0.0;
}
}
net->carrier_ = carrier.value();
@ -788,13 +792,30 @@ void waybar::modules::Network::parseSignal(struct nlattr **bss) {
if (bss[NL80211_BSS_SIGNAL_MBM] != nullptr) {
// signalstrength in dBm from mBm
signal_strength_dbm_ = nla_get_s32(bss[NL80211_BSS_SIGNAL_MBM]) / 100;
// WiFi-hardware usually operates in the range -90 to -30dBm.
// WiFi-hardware usually operates in the range -90 to -20dBm.
const int hardwareMax = -20;
// If a signal is too strong, it can overwhelm receiving circuity that is designed
// to pick up and process a certain signal level. The following percentage is scaled to
// punish signals that are too strong (>= -45dBm) or too weak (<= -45 dBm).
const int hardwareOptimum = -45;
const int hardwareMin = -90;
const int strength =
((signal_strength_dbm_ - hardwareMin) / double{hardwareMax - hardwareMin}) * 100;
signal_strength_ = std::clamp(strength, 0, 100);
100 - ((abs(signal_strength_dbm_ - hardwareOptimum) / double{hardwareOptimum - hardwareMin}) * 100);
signal_strength_ = std::clamp(strength, 0, 100);
if (signal_strength_dbm_ >= -50) {
signal_strength_app_ = "Great Connectivity";
} else if (signal_strength_dbm_ >= -60) {
signal_strength_app_ = "Good Connectivity";
} else if (signal_strength_dbm_ >= -67) {
signal_strength_app_ = "Streaming";
} else if (signal_strength_dbm_ >= -70) {
signal_strength_app_ = "Web Surfing";
} else if (signal_strength_dbm_ >= -80) {
signal_strength_app_ = "Basic Connectivity";
} else {
signal_strength_app_ = "Poor Connectivity";
}
}
if (bss[NL80211_BSS_SIGNAL_UNSPEC] != nullptr) {
signal_strength_ = nla_get_u8(bss[NL80211_BSS_SIGNAL_UNSPEC]);
@ -803,8 +824,8 @@ void waybar::modules::Network::parseSignal(struct nlattr **bss) {
void waybar::modules::Network::parseFreq(struct nlattr **bss) {
if (bss[NL80211_BSS_FREQUENCY] != nullptr) {
// in MHz
frequency_ = nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
// in GHz
frequency_ = (double) nla_get_u32(bss[NL80211_BSS_FREQUENCY]) / 1000;
}
}

View File

@ -54,7 +54,9 @@ void waybar::modules::Pulseaudio::contextStateCb(pa_context *c, void *data) {
c,
static_cast<enum pa_subscription_mask>(static_cast<int>(PA_SUBSCRIPTION_MASK_SERVER) |
static_cast<int>(PA_SUBSCRIPTION_MASK_SINK) |
static_cast<int>(PA_SUBSCRIPTION_MASK_SOURCE)),
static_cast<int>(PA_SUBSCRIPTION_MASK_SINK_INPUT) |
static_cast<int>(PA_SUBSCRIPTION_MASK_SOURCE) |
static_cast<int>(PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT)),
nullptr,
nullptr);
break;
@ -79,6 +81,13 @@ bool waybar::modules::Pulseaudio::handleScroll(GdkEventScroll *e) {
if (dir == SCROLL_DIR::NONE) {
return true;
}
if (config_["reverse-scrolling"].asInt() == 1){
if (dir == SCROLL_DIR::UP) {
dir = SCROLL_DIR::DOWN;
} else if (dir == SCROLL_DIR::DOWN) {
dir = SCROLL_DIR::UP;
}
}
double volume_tick = static_cast<double>(PA_VOLUME_NORM) / 100;
pa_volume_t change = volume_tick;
pa_cvolume pa_volume = pa_volume_;
@ -114,8 +123,12 @@ void waybar::modules::Pulseaudio::subscribeCb(pa_context * conte
pa_context_get_server_info(context, serverInfoCb, data);
} else if (facility == PA_SUBSCRIPTION_EVENT_SINK) {
pa_context_get_sink_info_by_index(context, idx, sinkInfoCb, data);
} else if (facility == PA_SUBSCRIPTION_EVENT_SINK_INPUT) {
pa_context_get_sink_info_list(context, sinkInfoCb, data);
} else if (facility == PA_SUBSCRIPTION_EVENT_SOURCE) {
pa_context_get_source_info_by_index(context, idx, sourceInfoCb, data);
} else if (facility == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT) {
pa_context_get_source_info_list(context, sourceInfoCb, data);
}
}
@ -272,7 +285,7 @@ auto waybar::modules::Pulseaudio::update() -> void {
fmt::arg("source_desc", source_desc_),
fmt::arg("icon", getIcon(volume_, getPulseIcon()))));
getState(volume_);
if (tooltipEnabled()) {
if (tooltip_format.empty() && config_["tooltip-format"].isString()) {
tooltip_format = config_["tooltip-format"].asString();

View File

@ -8,13 +8,7 @@
#include <fstream>
#include <map>
template <>
struct fmt::formatter<Glib::ustring> : formatter<std::string> {
template <typename FormatContext>
auto format(const Glib::ustring& value, FormatContext& ctx) {
return formatter<std::string>::format(value, ctx);
}
};
#include "util/format.hpp"
template <>
struct fmt::formatter<Glib::VariantBase> : formatter<std::string> {

View File

@ -25,7 +25,11 @@ Tray::Tray(const std::string& id, const Bar& bar, const Json::Value& config)
}
void Tray::onAdd(std::unique_ptr<Item>& item) {
box_.pack_start(item->event_box);
if (config_["reverse-direction"].isBool() && config_["reverse-direction"].asBool()) {
box_.pack_end(item->event_box);
} else {
box_.pack_start(item->event_box);
}
dp.emit();
}

View File

@ -154,7 +154,10 @@ auto Language::init_layouts_map(const std::vector<std::string>& used_layouts) ->
std::map<std::string, int> short_name_to_number_map;
for (const auto& used_layout_name : used_layouts) {
auto used_layout = &layouts_map_.find(used_layout_name)->second;
auto found = layouts_map_.find(used_layout_name);
if (found == layouts_map_.end())
continue;
auto used_layout = &found->second;
auto layouts_with_same_name_list = found_by_short_names[used_layout->short_name];
if (layouts_with_same_name_list.size() < 2) {
continue;

View File

@ -1,11 +1,20 @@
#include "modules/sway/window.hpp"
#include <gdkmm/pixbuf.h>
#include <glibmm/fileutils.h>
#include <glibmm/keyfile.h>
#include <glibmm/miscutils.h>
#include <gtkmm/enums.h>
#include <spdlog/spdlog.h>
#include <filesystem>
#include <regex>
#include <string>
namespace waybar::modules::sway {
Window::Window(const std::string& id, const Bar& bar, const Json::Value& config)
: ALabel(config, "window", id, "{}", 0, true), bar_(bar), windowId_(-1) {
: AIconLabel(config, "window", id, "{}", 0, true), bar_(bar), windowId_(-1) {
ipc_.subscribe(R"(["window","workspace"])");
ipc_.signal_event.connect(sigc::mem_fun(*this, &Window::onEvent));
ipc_.signal_cmd.connect(sigc::mem_fun(*this, &Window::onCmd));
@ -29,12 +38,60 @@ void Window::onCmd(const struct Ipc::ipc_response& res) {
auto payload = parser_.parse(res.payload);
auto output = payload["output"].isString() ? payload["output"].asString() : "";
std::tie(app_nb_, windowId_, window_, app_id_) = getFocusedNode(payload["nodes"], output);
updateAppIcon();
dp.emit();
} catch (const std::exception& e) {
spdlog::error("Window: {}", e.what());
}
}
std::optional<std::string> getDesktopFilePath(const std::string& app_id) {
const auto data_dirs = Glib::get_system_data_dirs();
for (const auto& data_dir : data_dirs) {
const auto desktop_file_path = data_dir + "applications/" + app_id + ".desktop";
if (std::filesystem::exists(desktop_file_path)) {
return desktop_file_path;
}
}
return {};
}
std::optional<Glib::ustring> getIconName(const std::string& app_id) {
const auto desktop_file_path = getDesktopFilePath(app_id);
if (!desktop_file_path.has_value()) {
return {};
}
try {
Glib::KeyFile desktop_file;
desktop_file.load_from_file(desktop_file_path.value());
const auto icon_name = desktop_file.get_string("Desktop Entry", "Icon");
if (icon_name.empty()) {
return {};
}
return icon_name;
} catch (Glib::FileError& error) {
spdlog::warn(
"Error while loading desktop file {}: {}", desktop_file_path.value(), error.what().c_str());
} catch (Glib::KeyFileError& error) {
spdlog::warn(
"Error while loading desktop file {}: {}", desktop_file_path.value(), error.what().c_str());
}
return {};
}
void Window::updateAppIcon() {
if (!iconEnabled()) {
return;
}
const auto icon_name = getIconName(app_id_);
if (icon_name.has_value()) {
image_.set_from_icon_name(icon_name.value(), Gtk::ICON_SIZE_LARGE_TOOLBAR);
image_.set_visible(true);
return;
}
image_.set_visible(false);
}
auto Window::update() -> void {
if (!old_app_id_.empty()) {
bar_.window.get_style_context()->remove_class(old_app_id_);
@ -63,7 +120,7 @@ auto Window::update() -> void {
label_.set_tooltip_text(window_);
}
// Call parent update
ALabel::update();
AIconLabel::update();
}
int leafNodesInWorkspace(const Json::Value& node) {

View File

@ -2,6 +2,7 @@
#include <spdlog/spdlog.h>
#include <algorithm>
#include <cctype>
#include <string>
@ -98,6 +99,7 @@ void Workspaces::onCmd(const struct Ipc::ipc_response &res) {
Json::Value v;
v["name"] = p_w_name;
v["target_output"] = bar_.output->name;
v["num"] = convertWorkspaceNameToNum(p_w_name);
workspaces_.emplace_back(std::move(v));
break;
}
@ -107,57 +109,59 @@ void Workspaces::onCmd(const struct Ipc::ipc_response &res) {
Json::Value v;
v["name"] = p_w_name;
v["target_output"] = "";
v["num"] = convertWorkspaceNameToNum(p_w_name);
workspaces_.emplace_back(std::move(v));
}
}
}
// config option to sort numeric workspace names before others
bool config_numeric_first = config_["numeric-first"].asBool();
// sway has a defined ordering of workspaces that should be preserved in
// the representation displayed by waybar to ensure that commands such
// as "workspace prev" or "workspace next" make sense when looking at
// the workspace representation in the bar.
// Due to waybar's own feature of persistent workspaces unknown to sway,
// custom sorting logic is necessary to make these workspaces appear
// naturally in the list of workspaces without messing up sway's
// sorting. For this purpose, a custom numbering property is created
// that preserves the order provided by sway while inserting numbered
// persistent workspaces at their natural positions.
//
// All of this code assumes that sway provides numbered workspaces first
// and other workspaces are sorted by their creation time.
//
// In a first pass, the maximum "num" value is computed to enqueue
// unnumbered workspaces behind numbered ones when computing the sort
// attribute.
int max_num = -1;
for (auto & workspace : workspaces_) {
max_num = std::max(workspace["num"].asInt(), max_num);
}
for (auto & workspace : workspaces_) {
auto workspace_num = workspace["num"].asInt();
if (workspace_num > -1) {
workspace["sort"] = workspace_num;
} else {
workspace["sort"] = ++max_num;
}
}
std::sort(workspaces_.begin(),
workspaces_.end(),
[config_numeric_first](const Json::Value &lhs, const Json::Value &rhs) {
// the "num" property (integer type):
// The workspace number or -1 for workspaces that do
// not start with a number.
// We could rely on sway providing this property:
//
// auto l = lhs["num"].asInt();
// auto r = rhs["num"].asInt();
//
// We cannot rely on the "num" property as provided by sway
// via IPC, because persistent workspace might not exist in
// sway's view. However, we need this property also for
// not-yet created persistent workspace. As such, we simply
// duplicate sway's logic of assigning the "num" property
// into waybar (see convertWorkspaceNameToNum). This way the
// sorting should work out even when we include workspaces
// that do not currently exist.
[](const Json::Value &lhs, const Json::Value &rhs) {
auto lname = lhs["name"].asString();
auto rname = rhs["name"].asString();
int l = convertWorkspaceNameToNum(lname);
int r = convertWorkspaceNameToNum(rname);
int l = lhs["sort"].asInt();
int r = rhs["sort"].asInt();
if (l == r) {
// in case both integers are the same, lexicographical
// sort. This also covers the case when both don't have a
// number (i.e., l == r == -1).
// In case both integers are the same, lexicographical
// sort. The code above already ensure that this will only
// happend in case of explicitly numbered workspaces.
return lname < rname;
}
// one of the workspaces doesn't begin with a number, so
// num is -1.
if (l < 0 || r < 0) {
if (config_numeric_first) {
return r < 0;
}
return l < 0;
}
// both workspaces have a "num" so let's just compare those
return l < r;
});
}
dp.emit();
} catch (const std::exception &e) {

View File

@ -4,6 +4,7 @@
#include "glibmm/fileutils.h"
#include "glibmm/refptr.h"
#include "util/format.hpp"
#include "util/string.hpp"
#include <algorithm>
#include <cctype>
@ -11,7 +12,9 @@
#include <cstring>
#include <memory>
#include <sstream>
#include <utility>
#include <fmt/core.h>
#include <gdkmm/monitor.h>
#include <gtkmm/icontheme.h>
@ -24,27 +27,6 @@
namespace waybar::modules::wlr {
/* String manipulation methods */
const std::string WHITESPACE = " \n\r\t\f\v";
static std::string ltrim(const std::string& s)
{
size_t start = s.find_first_not_of(WHITESPACE);
return (start == std::string::npos) ? "" : s.substr(start);
}
static std::string rtrim(const std::string& s)
{
size_t end = s.find_last_not_of(WHITESPACE);
return (end == std::string::npos) ? "" : s.substr(0, end + 1);
}
static std::string trim(const std::string& s)
{
return rtrim(ltrim(s));
}
/* Icon loading functions */
static std::vector<std::string> search_prefix()
{
@ -86,8 +68,7 @@ static Glib::RefPtr<Gdk::Pixbuf> load_icon_from_file(std::string icon_path, int
}
}
/* Method 1 - get the correct icon name from the desktop file */
static std::string get_from_desktop_app_info(const std::string &app_id)
static Glib::RefPtr<Gio::DesktopAppInfo> get_app_info_by_name(const std::string& app_id)
{
static std::vector<std::string> prefixes = search_prefix();
@ -103,33 +84,29 @@ static std::string get_from_desktop_app_info(const std::string &app_id)
".desktop"
};
Glib::RefPtr<Gio::DesktopAppInfo> app_info;
for (auto& prefix : prefixes) {
for (auto& folder : app_folders) {
for (auto& suffix : suffixes) {
auto app_info_ = Gio::DesktopAppInfo::create_from_filename(prefix + folder + app_id + suffix);
if (!app_info_) {
continue;
}
for (auto& prefix : prefixes)
for (auto& folder : app_folders)
for (auto& suffix : suffixes)
if (!app_info)
app_info = Gio::DesktopAppInfo::create_from_filename(prefix + folder + app_id + suffix);
return app_info_;
}
}
}
if (app_info && app_info->get_icon())
return app_info->get_icon()->to_string();
return "";
return {};
}
/* Method 2 - use the app_id and check whether there is an icon with this name in the icon theme */
static std::string get_from_icon_theme(const Glib::RefPtr<Gtk::IconTheme>& icon_theme,
const std::string &app_id)
Glib::RefPtr<Gio::DesktopAppInfo> get_desktop_app_info(const std::string &app_id)
{
if (icon_theme->lookup_icon(app_id, 24))
return app_id;
auto app_info = get_app_info_by_name(app_id);
if (app_info) {
return app_info;
}
return "";
}
/* Method 3 - as last resort perform a search for most appropriate desktop info file */
static std::string get_from_desktop_app_info_search(const std::string &app_id)
{
std::string desktop_file = "";
gchar*** desktop_list = g_desktop_app_info_search(app_id.c_str());
@ -151,65 +128,84 @@ static std::string get_from_desktop_app_info_search(const std::string &app_id)
}
g_free(desktop_list);
return get_from_desktop_app_info(desktop_file);
return get_app_info_by_name(desktop_file);
}
static bool image_load_icon(Gtk::Image& image, const Glib::RefPtr<Gtk::IconTheme>& icon_theme,
const std::string &app_id_list, int size)
{
void Task::set_app_info_from_app_id_list(const std::string& app_id_list) {
std::string app_id;
std::istringstream stream(app_id_list);
bool found = false;
/* Wayfire sends a list of app-id's in space separated format, other compositors
* send a single app-id, but in any case this works fine */
while (stream >> app_id)
{
app_info_ = get_desktop_app_info(app_id);
if (app_info_) {
return;
}
auto lower_app_id = app_id;
std::transform(lower_app_id.begin(), lower_app_id.end(), lower_app_id.begin(),
[](char c){ return std::tolower(c); });
app_info_ = get_desktop_app_info(lower_app_id);
if (app_info_) {
return;
}
size_t start = 0, end = app_id.size();
start = app_id.rfind(".", end);
std::string app_name = app_id.substr(start+1, app_id.size());
app_info_ = get_desktop_app_info(app_name);
if (app_info_) {
return;
}
auto lower_app_id = app_id;
std::transform(lower_app_id.begin(), lower_app_id.end(), lower_app_id.begin(),
[](char c){ return std::tolower(c); });
start = app_id.find("-");
app_name = app_id.substr(0, start);
app_info_ = get_desktop_app_info(app_name);
}
}
std::string icon_name = get_from_icon_theme(icon_theme, app_id);
static std::string get_icon_name_from_icon_theme(const Glib::RefPtr<Gtk::IconTheme>& icon_theme,
const std::string &app_id)
{
if (icon_theme->lookup_icon(app_id, 24))
return app_id;
if (icon_name.empty())
icon_name = get_from_icon_theme(icon_theme, lower_app_id);
if (icon_name.empty())
icon_name = get_from_icon_theme(icon_theme, app_name);
if (icon_name.empty())
icon_name = get_from_desktop_app_info(app_id);
if (icon_name.empty())
icon_name = get_from_desktop_app_info(lower_app_id);
if (icon_name.empty())
icon_name = get_from_desktop_app_info(app_name);
if (icon_name.empty())
icon_name = get_from_desktop_app_info_search(app_id);
return "";
}
if (icon_name.empty())
icon_name = "unknown";
bool Task::image_load_icon(Gtk::Image& image, const Glib::RefPtr<Gtk::IconTheme>& icon_theme, Glib::RefPtr<Gio::DesktopAppInfo> app_info, int size)
{
std::string ret_icon_name = "unknown";
if (app_info) {
std::string icon_name = get_icon_name_from_icon_theme(icon_theme, app_info->get_startup_wm_class());
if (!icon_name.empty()) {
ret_icon_name = icon_name;
} else {
if (app_info->get_icon()) {
ret_icon_name = app_info->get_icon()->to_string();
}
}
}
Glib::RefPtr<Gdk::Pixbuf> pixbuf;
Glib::RefPtr<Gdk::Pixbuf> pixbuf;
try {
pixbuf = icon_theme->load_icon(icon_name, size, Gtk::ICON_LOOKUP_FORCE_SIZE);
} catch(...) {
if (Glib::file_test(icon_name, Glib::FILE_TEST_EXISTS))
pixbuf = load_icon_from_file(icon_name, size);
else
pixbuf = {};
}
try {
pixbuf = icon_theme->load_icon(ret_icon_name, size, Gtk::ICON_LOOKUP_FORCE_SIZE);
} catch(...) {
if (Glib::file_test(ret_icon_name, Glib::FILE_TEST_EXISTS))
pixbuf = load_icon_from_file(ret_icon_name, size);
else
pixbuf = {};
}
if (pixbuf) {
image.set(pixbuf);
found = true;
break;
}
}
if (pixbuf) {
image.set(pixbuf);
return true;
}
return found;
return false;
}
/* Task class implementation */
@ -289,13 +285,15 @@ Task::Task(const waybar::Bar &bar, const Json::Value &config, Taskbar *tbar,
content_.show();
button_.add(content_);
with_icon_ = false;
format_before_.clear();
format_after_.clear();
if (config_["format"].isString()) {
/* The user defined a format string, use it */
auto format = config_["format"].asString();
if (format.find("{name}") != std::string::npos) {
with_name_ = true;
}
auto icon_pos = format.find("{icon}");
if (icon_pos == 0) {
@ -402,13 +400,28 @@ void Task::handle_app_id(const char *app_id)
app_id_ = app_id;
hide_if_ignored();
if (!with_icon_)
auto ids_replace_map = tbar_->app_ids_replace_map();
if (ids_replace_map.count(app_id_)) {
auto replaced_id = ids_replace_map[app_id_];
spdlog::debug(fmt::format("Task ({}) [{}] app_id was replaced with {}", id_, app_id_, replaced_id));
app_id_ = replaced_id;
}
if (!with_icon_ && !with_name_) {
return;
}
set_app_info_from_app_id_list(app_id_);
name_ = app_info_ ? app_info_->get_display_name() : app_id;
if (!with_icon_) {
return;
}
int icon_size = config_["icon-size"].isInt() ? config_["icon-size"].asInt() : 16;
bool found = false;
for (auto& icon_theme : tbar_->icon_themes()) {
if (image_load_icon(icon_, icon_theme, app_id_, icon_size)) {
if (image_load_icon(icon_, icon_theme, app_info_, icon_size)) {
found = true;
break;
}
@ -564,14 +577,17 @@ void Task::update()
{
bool markup = config_["markup"].isBool() ? config_["markup"].asBool() : false;
std::string title = title_;
std::string name = name_;
std::string app_id = app_id_;
if (markup) {
title = Glib::Markup::escape_text(title);
name = Glib::Markup::escape_text(name);
app_id = Glib::Markup::escape_text(app_id);
}
if (!format_before_.empty()) {
auto txt = fmt::format(format_before_,
fmt::arg("title", title),
fmt::arg("name", name),
fmt::arg("app_id", app_id),
fmt::arg("state", state_string()),
fmt::arg("short_state", state_string(true))
@ -585,6 +601,7 @@ void Task::update()
if (!format_after_.empty()) {
auto txt = fmt::format(format_after_,
fmt::arg("title", title),
fmt::arg("name", name),
fmt::arg("app_id", app_id),
fmt::arg("state", state_string()),
fmt::arg("short_state", state_string(true))
@ -599,6 +616,7 @@ void Task::update()
if (!format_tooltip_.empty()) {
auto txt = fmt::format(format_tooltip_,
fmt::arg("title", title),
fmt::arg("name", name),
fmt::arg("app_id", app_id),
fmt::arg("state", state_string()),
fmt::arg("short_state", state_string(true))
@ -726,6 +744,15 @@ Taskbar::Taskbar(const std::string &id, const waybar::Bar &bar, const Json::Valu
}
}
// Load app_id remappings
if (config_["app_ids-mapping"].isObject()) {
const Json::Value& mapping = config_["app_ids-mapping"];
const std::vector<std::string> app_ids = config_["app_ids-mapping"].getMemberNames();
for (auto& app_id : app_ids) {
app_ids_replace_map_.emplace(app_id, mapping[app_id].asString());
}
}
icon_themes_.push_back(Gtk::IconTheme::get_default());
}
@ -857,10 +884,10 @@ bool Taskbar::all_outputs() const
return config_["all-outputs"].isBool() && config_["all-outputs"].asBool();
}
std::vector<Glib::RefPtr<Gtk::IconTheme>> Taskbar::icon_themes() const
{
return icon_themes_;
}
const std::unordered_set<std::string> &Taskbar::ignore_list() const { return ignore_list_; }
const std::vector<Glib::RefPtr<Gtk::IconTheme>>& Taskbar::icon_themes() const { return icon_themes_; }
const std::unordered_set<std::string>& Taskbar::ignore_list() const { return ignore_list_; }
const std::map<std::string, std::string>& Taskbar::app_ids_replace_map() const { return app_ids_replace_map_; }
} /* namespace waybar::modules::wlr */