mirror of
https://github.com/rad4day/Waybar.git
synced 2023-12-21 10:22:59 +01:00
Merge branch 'master' into list_of_times
This commit is contained in:
28
src/AIconLabel.cpp
Normal file
28
src/AIconLabel.cpp
Normal 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
|
@ -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() ||
|
||||
|
17
src/bar.cpp
17
src/bar.cpp
@ -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.
|
||||
|
@ -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]);
|
||||
|
@ -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.
|
||||
|
@ -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));
|
||||
}
|
||||
};
|
||||
|
@ -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
175
src/modules/inhibitor.cpp
Normal 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
|
@ -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);
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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> {
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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 */
|
||||
|
Reference in New Issue
Block a user