mirror of
https://github.com/rad4day/Waybar.git
synced 2025-07-20 01:42:38 +02:00
Merge branch 'master' into fix_power_calc
This commit is contained in:
@@ -76,7 +76,7 @@ std::string ALabel::getIcon(uint16_t percentage, const std::string& alt, uint16_
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string ALabel::getIcon(uint16_t percentage, std::vector<std::string>& alts, uint16_t max) {
|
||||
std::string ALabel::getIcon(uint16_t percentage, const std::vector<std::string>& alts, uint16_t max) {
|
||||
auto format_icons = config_["format-icons"];
|
||||
if (format_icons.isObject()) {
|
||||
std::string _alt = "default";
|
||||
|
@@ -234,14 +234,62 @@ std::tuple<const std::string, const std::string> waybar::Client::getConfigs(
|
||||
return {config_file, css_file};
|
||||
}
|
||||
|
||||
auto waybar::Client::setupConfig(const std::string &config_file) -> void {
|
||||
auto waybar::Client::setupConfig(const std::string &config_file, int depth) -> void {
|
||||
if (depth > 100) {
|
||||
throw std::runtime_error("Aborting due to likely recursive include in config files");
|
||||
}
|
||||
std::ifstream file(config_file);
|
||||
if (!file.is_open()) {
|
||||
throw std::runtime_error("Can't open config file");
|
||||
}
|
||||
std::string str((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
|
||||
util::JsonParser parser;
|
||||
config_ = parser.parse(str);
|
||||
Json::Value tmp_config_ = parser.parse(str);
|
||||
if (tmp_config_.isArray()) {
|
||||
for (auto &config_part : tmp_config_) {
|
||||
resolveConfigIncludes(config_part, depth);
|
||||
}
|
||||
} else {
|
||||
resolveConfigIncludes(tmp_config_, depth);
|
||||
}
|
||||
mergeConfig(config_, tmp_config_);
|
||||
}
|
||||
|
||||
auto waybar::Client::resolveConfigIncludes(Json::Value &config, int depth) -> void {
|
||||
Json::Value includes = config["include"];
|
||||
if (includes.isArray()) {
|
||||
for (const auto &include : includes) {
|
||||
spdlog::info("Including resource file: {}", include.asString());
|
||||
setupConfig(getValidPath({include.asString()}), ++depth);
|
||||
}
|
||||
} else if (includes.isString()) {
|
||||
spdlog::info("Including resource file: {}", includes.asString());
|
||||
setupConfig(getValidPath({includes.asString()}), ++depth);
|
||||
}
|
||||
}
|
||||
|
||||
auto waybar::Client::mergeConfig(Json::Value &a_config_, Json::Value &b_config_) -> void {
|
||||
if (!a_config_) {
|
||||
// For the first config
|
||||
a_config_ = b_config_;
|
||||
} else if (a_config_.isObject() && b_config_.isObject()) {
|
||||
for (const auto &key : b_config_.getMemberNames()) {
|
||||
if (a_config_[key].isObject() && b_config_[key].isObject()) {
|
||||
mergeConfig(a_config_[key], b_config_[key]);
|
||||
} else {
|
||||
a_config_[key] = b_config_[key];
|
||||
}
|
||||
}
|
||||
} else if (a_config_.isArray() && b_config_.isArray()) {
|
||||
// This can happen only on the top-level array of a multi-bar config
|
||||
for (Json::Value::ArrayIndex i = 0; i < b_config_.size(); i++) {
|
||||
if (a_config_[i].isObject() && b_config_[i].isObject()) {
|
||||
mergeConfig(a_config_[i], b_config_[i]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
spdlog::error("Cannot merge config, conflicting or invalid JSON types");
|
||||
}
|
||||
}
|
||||
|
||||
auto waybar::Client::setupCss(const std::string &css_file) -> void {
|
||||
@@ -320,7 +368,7 @@ int waybar::Client::main(int argc, char *argv[]) {
|
||||
}
|
||||
wl_display = gdk_wayland_display_get_wl_display(gdk_display->gobj());
|
||||
auto [config_file, css_file] = getConfigs(config, style);
|
||||
setupConfig(config_file);
|
||||
setupConfig(config_file, 0);
|
||||
setupCss(css_file);
|
||||
bindInterfaces();
|
||||
gtk_app->hold();
|
||||
|
@@ -70,6 +70,11 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const {
|
||||
return new waybar::modules::Backlight(id, config_[name]);
|
||||
}
|
||||
#endif
|
||||
#ifdef HAVE_LIBEVDEV
|
||||
if (ref == "keyboard-state") {
|
||||
return new waybar::modules::KeyboardState(id, bar_, config_[name]);
|
||||
}
|
||||
#endif
|
||||
#ifdef HAVE_LIBPULSE
|
||||
if (ref == "pulseaudio") {
|
||||
return new waybar::modules::Pulseaudio(id, config_[name]);
|
||||
|
@@ -196,6 +196,9 @@ 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));
|
||||
}
|
||||
};
|
||||
|
152
src/modules/keyboard_state.cpp
Normal file
152
src/modules/keyboard_state.cpp
Normal file
@@ -0,0 +1,152 @@
|
||||
#include "modules/keyboard_state.hpp"
|
||||
#include <filesystem>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
extern "C" {
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
}
|
||||
|
||||
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),
|
||||
numlock_label_(""),
|
||||
capslock_label_(""),
|
||||
numlock_format_(config_["format"].isString() ? config_["format"].asString()
|
||||
: config_["format"]["numlock"].isString() ? config_["format"]["numlock"].asString()
|
||||
: "{name} {icon}"),
|
||||
capslock_format_(config_["format"].isString() ? config_["format"].asString()
|
||||
: config_["format"]["capslock"].isString() ? config_["format"]["capslock"].asString()
|
||||
: "{name} {icon}"),
|
||||
scrolllock_format_(config_["format"].isString() ? config_["format"].asString()
|
||||
: config_["format"]["scrolllock"].isString() ? config_["format"]["scrolllock"].asString()
|
||||
: "{name} {icon}"),
|
||||
interval_(std::chrono::seconds(config_["interval"].isUInt() ? config_["interval"].asUInt() : 1)),
|
||||
icon_locked_(config_["format-icons"]["locked"].isString()
|
||||
? config_["format-icons"]["locked"].asString()
|
||||
: "locked"),
|
||||
icon_unlocked_(config_["format-icons"]["unlocked"].isString()
|
||||
? config_["format-icons"]["unlocked"].asString()
|
||||
: "unlocked"),
|
||||
fd_(0),
|
||||
dev_(nullptr) {
|
||||
box_.set_name("keyboard-state");
|
||||
if (config_["numlock"].asBool()) {
|
||||
box_.pack_end(numlock_label_, false, false, 0);
|
||||
}
|
||||
if (config_["capslock"].asBool()) {
|
||||
box_.pack_end(capslock_label_, false, false, 0);
|
||||
}
|
||||
if (config_["scrolllock"].asBool()) {
|
||||
box_.pack_end(scrolllock_label_, false, false, 0);
|
||||
}
|
||||
if (!id.empty()) {
|
||||
box_.get_style_context()->add_class(id);
|
||||
}
|
||||
event_box_.add(box_);
|
||||
|
||||
if (config_["device-path"].isString()) {
|
||||
std::string dev_path = config_["device-path"].asString();
|
||||
std::tie(fd_, dev_) = openDevice(dev_path);
|
||||
} else {
|
||||
DIR* dev_dir = opendir("/dev/input");
|
||||
if (dev_dir == nullptr) {
|
||||
throw std::runtime_error("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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
if (dev_ == nullptr) {
|
||||
throw std::runtime_error("Failed to find keyboard device");
|
||||
}
|
||||
}
|
||||
|
||||
thread_ = [this] {
|
||||
dp.emit();
|
||||
thread_.sleep_for(interval_);
|
||||
};
|
||||
}
|
||||
|
||||
waybar::modules::KeyboardState::~KeyboardState() {
|
||||
libevdev_free(dev_);
|
||||
int err = close(fd_);
|
||||
if (err < 0) {
|
||||
// Not much we can do, so ignore it.
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
input_event ev;
|
||||
err = libevdev_next_event(dev_, LIBEVDEV_READ_FLAG_NORMAL, &ev);
|
||||
while (err == LIBEVDEV_READ_STATUS_SYNC) {
|
||||
err = libevdev_next_event(dev_, LIBEVDEV_READ_FLAG_SYNC, &ev);
|
||||
}
|
||||
}
|
||||
if (err != -EAGAIN) {
|
||||
throw std::runtime_error("Failed to sync evdev device");
|
||||
}
|
||||
|
||||
int numl = libevdev_get_event_value(dev_, EV_LED, LED_NUML);
|
||||
int capsl = libevdev_get_event_value(dev_, EV_LED, LED_CAPSL);
|
||||
int scrolll = libevdev_get_event_value(dev_, EV_LED, LED_SCROLLL);
|
||||
|
||||
struct {
|
||||
bool state;
|
||||
Gtk::Label& label;
|
||||
const std::string& format;
|
||||
const char* name;
|
||||
} label_states[] = {
|
||||
{(bool) numl, numlock_label_, numlock_format_, "Num"},
|
||||
{(bool) capsl, capslock_label_, capslock_format_, "Caps"},
|
||||
{(bool) scrolll, scrolllock_label_, scrolllock_format_, "Scroll"},
|
||||
};
|
||||
for (auto& label_state : label_states) {
|
||||
std::string text;
|
||||
text = fmt::format(label_state.format,
|
||||
fmt::arg("icon", label_state.state ? icon_locked_ : icon_unlocked_),
|
||||
fmt::arg("name", label_state.name));
|
||||
label_state.label.set_markup(text);
|
||||
if (label_state.state) {
|
||||
label_state.label.get_style_context()->add_class("locked");
|
||||
} else {
|
||||
label_state.label.get_style_context()->remove_class("locked");
|
||||
}
|
||||
}
|
||||
|
||||
AModule::update();
|
||||
}
|
@@ -1,6 +1,7 @@
|
||||
#include "modules/network.hpp"
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <sys/eventfd.h>
|
||||
#include <linux/if.h>
|
||||
#include <fstream>
|
||||
#include <cassert>
|
||||
#include <optional>
|
||||
@@ -18,6 +19,7 @@ constexpr const char *NETSTAT_FILE =
|
||||
constexpr std::string_view BANDWIDTH_CATEGORY = "IpExt";
|
||||
constexpr std::string_view BANDWIDTH_DOWN_TOTAL_KEY = "InOctets";
|
||||
constexpr std::string_view BANDWIDTH_UP_TOTAL_KEY = "OutOctets";
|
||||
constexpr const char *DEFAULT_FORMAT = "{ifname}";
|
||||
|
||||
std::ifstream netstat(NETSTAT_FILE);
|
||||
std::optional<unsigned long long> read_netstat(std::string_view category, std::string_view key) {
|
||||
@@ -81,7 +83,7 @@ std::optional<unsigned long long> read_netstat(std::string_view category, std::s
|
||||
} // namespace
|
||||
|
||||
waybar::modules::Network::Network(const std::string &id, const Json::Value &config)
|
||||
: ALabel(config, "network", id, "{ifname}", 60),
|
||||
: ALabel(config, "network", id, DEFAULT_FORMAT, 60),
|
||||
ifid_(-1),
|
||||
family_(config["family"] == "ipv6" ? AF_INET6 : AF_INET),
|
||||
efd_(-1),
|
||||
@@ -322,6 +324,10 @@ auto waybar::modules::Network::update() -> void {
|
||||
}
|
||||
if (config_["format-" + state].isString()) {
|
||||
default_format_ = config_["format-" + state].asString();
|
||||
} else if (config_["format"].isString()) {
|
||||
default_format_ = config_["format"].asString();
|
||||
} else {
|
||||
default_format_ = DEFAULT_FORMAT;
|
||||
}
|
||||
if (config_["tooltip-format-" + state].isString()) {
|
||||
tooltip_format = config_["tooltip-format-" + state].asString();
|
||||
@@ -400,6 +406,7 @@ bool waybar::modules::Network::checkInterface(std::string name) {
|
||||
|
||||
void waybar::modules::Network::clearIface() {
|
||||
ifid_ = -1;
|
||||
ifname_.clear();
|
||||
essid_.clear();
|
||||
ipaddr_.clear();
|
||||
netmask_.clear();
|
||||
@@ -431,6 +438,20 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) {
|
||||
return NL_OK;
|
||||
}
|
||||
|
||||
// Check if the interface goes "down" and if we want to detect the
|
||||
// external interface.
|
||||
if (net->ifid_ != -1 && !(ifi->ifi_flags & IFF_UP)
|
||||
&& !net->config_["interface"].isString()) {
|
||||
// The current interface is now down, all the routes associated with
|
||||
// it have been deleted, so start looking for a new default route.
|
||||
spdlog::debug("network: if{} down", net->ifid_);
|
||||
net->clearIface();
|
||||
net->dp.emit();
|
||||
net->want_route_dump_ = true;
|
||||
net->askForStateDump();
|
||||
return NL_OK;
|
||||
}
|
||||
|
||||
for (; RTA_OK(ifla, attrlen); ifla = RTA_NEXT(ifla, attrlen)) {
|
||||
switch (ifla->rta_type) {
|
||||
case IFLA_IFNAME:
|
||||
@@ -572,11 +593,7 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) {
|
||||
bool has_gateway = false;
|
||||
bool has_destination = false;
|
||||
int temp_idx = -1;
|
||||
|
||||
/* If we found the correct answer, skip parsing the attributes. */
|
||||
if (!is_del_event && net->ifid_ != -1) {
|
||||
return NL_OK;
|
||||
}
|
||||
uint32_t priority = 0;
|
||||
|
||||
/* Find the message(s) concerting the main routing table, each message
|
||||
* corresponds to a single routing table entry.
|
||||
@@ -620,52 +637,59 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) {
|
||||
/* The output interface index. */
|
||||
temp_idx = *static_cast<int *>(RTA_DATA(attr));
|
||||
break;
|
||||
case RTA_PRIORITY:
|
||||
priority = *(uint32_t*)RTA_DATA(attr);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* If this is the default route, and we know the interface index,
|
||||
* we can stop parsing this message.
|
||||
*/
|
||||
if (has_gateway && !has_destination && temp_idx != -1) {
|
||||
if (!is_del_event) {
|
||||
net->ifid_ = temp_idx;
|
||||
// Check if we have a default route.
|
||||
if (has_gateway && !has_destination && temp_idx != -1) {
|
||||
// Check if this is the first default route we see, or if this new
|
||||
// route have a higher priority.
|
||||
if (!is_del_event && ((net->ifid_ == -1) || (priority < net->route_priority))) {
|
||||
// Clear if's state for the case were there is a higher priority
|
||||
// route on a different interface.
|
||||
net->clearIface();
|
||||
net->ifid_ = temp_idx;
|
||||
net->route_priority = priority;
|
||||
|
||||
spdlog::debug("network: new default route via if{}", temp_idx);
|
||||
spdlog::debug("network: new default route via if{} metric {}", temp_idx, priority);
|
||||
|
||||
/* Ask ifname associated with temp_idx as well as carrier status */
|
||||
struct ifinfomsg ifinfo_hdr = {
|
||||
.ifi_family = AF_UNSPEC,
|
||||
.ifi_index = temp_idx,
|
||||
};
|
||||
int err;
|
||||
err = nl_send_simple(net->ev_sock_, RTM_GETLINK, NLM_F_REQUEST,
|
||||
&ifinfo_hdr, sizeof (ifinfo_hdr));
|
||||
if (err < 0) {
|
||||
spdlog::error("network: failed to ask link info: {}", err);
|
||||
/* Ask for a dump of all links instead */
|
||||
net->want_link_dump_ = true;
|
||||
}
|
||||
|
||||
/* Also ask for the address. Asking for a addresses of a specific
|
||||
* interface doesn't seems to work so ask for a dump of all
|
||||
* addresses. */
|
||||
net->want_addr_dump_ = true;
|
||||
net->askForStateDump();
|
||||
net->thread_timer_.wake_up();
|
||||
} else if (is_del_event && temp_idx == net->ifid_) {
|
||||
spdlog::debug("network: default route deleted {}/if{}",
|
||||
net->ifname_, temp_idx);
|
||||
|
||||
net->ifname_.clear();
|
||||
net->clearIface();
|
||||
net->dp.emit();
|
||||
/* Ask for a dump of all routes in case another one is already
|
||||
* setup. If there's none, there'll be an event with new one
|
||||
* later. */
|
||||
net->want_route_dump_ = true;
|
||||
net->askForStateDump();
|
||||
/* Ask ifname associated with temp_idx as well as carrier status */
|
||||
struct ifinfomsg ifinfo_hdr = {
|
||||
.ifi_family = AF_UNSPEC,
|
||||
.ifi_index = temp_idx,
|
||||
};
|
||||
int err;
|
||||
err = nl_send_simple(net->ev_sock_, RTM_GETLINK, NLM_F_REQUEST,
|
||||
&ifinfo_hdr, sizeof (ifinfo_hdr));
|
||||
if (err < 0) {
|
||||
spdlog::error("network: failed to ask link info: {}", err);
|
||||
/* Ask for a dump of all links instead */
|
||||
net->want_link_dump_ = true;
|
||||
}
|
||||
|
||||
/* Also ask for the address. Asking for a addresses of a specific
|
||||
* interface doesn't seems to work so ask for a dump of all
|
||||
* addresses. */
|
||||
net->want_addr_dump_ = true;
|
||||
net->askForStateDump();
|
||||
net->thread_timer_.wake_up();
|
||||
} else if (is_del_event && temp_idx == net->ifid_
|
||||
&& net->route_priority == priority) {
|
||||
spdlog::debug("network: default route deleted {}/if{} metric {}",
|
||||
net->ifname_, temp_idx, priority);
|
||||
|
||||
net->clearIface();
|
||||
net->dp.emit();
|
||||
/* Ask for a dump of all routes in case another one is already
|
||||
* setup. If there's none, there'll be an event with new one
|
||||
* later. */
|
||||
net->want_route_dump_ = true;
|
||||
net->askForStateDump();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@@ -151,8 +151,24 @@ void waybar::modules::Pulseaudio::sourceInfoCb(pa_context * /*context*/, const p
|
||||
*/
|
||||
void waybar::modules::Pulseaudio::sinkInfoCb(pa_context * /*context*/, const pa_sink_info *i,
|
||||
int /*eol*/, void *data) {
|
||||
if (i == nullptr)
|
||||
return;
|
||||
|
||||
auto pa = static_cast<waybar::modules::Pulseaudio *>(data);
|
||||
if (i != nullptr && pa->default_sink_name_ == i->name) {
|
||||
if (pa->current_sink_name_ == i->name) {
|
||||
if (i->state != PA_SINK_RUNNING) {
|
||||
pa->current_sink_running_ = false;
|
||||
} else {
|
||||
pa->current_sink_running_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!pa->current_sink_running_ && i->state == PA_SINK_RUNNING) {
|
||||
pa->current_sink_name_ = i->name;
|
||||
pa->current_sink_running_ = true;
|
||||
}
|
||||
|
||||
if (pa->current_sink_name_ == i->name) {
|
||||
pa->pa_volume_ = i->volume;
|
||||
float volume = static_cast<float>(pa_cvolume_avg(&(pa->pa_volume_))) / float{PA_VOLUME_NORM};
|
||||
pa->sink_idx_ = i->index;
|
||||
@@ -175,11 +191,11 @@ void waybar::modules::Pulseaudio::sinkInfoCb(pa_context * /*context*/, const pa_
|
||||
void waybar::modules::Pulseaudio::serverInfoCb(pa_context *context, const pa_server_info *i,
|
||||
void *data) {
|
||||
auto pa = static_cast<waybar::modules::Pulseaudio *>(data);
|
||||
pa->default_sink_name_ = i->default_sink_name;
|
||||
pa->current_sink_name_ = i->default_sink_name;
|
||||
pa->default_source_name_ = i->default_source_name;
|
||||
|
||||
pa_context_get_sink_info_by_name(context, i->default_sink_name, sinkInfoCb, data);
|
||||
pa_context_get_source_info_by_name(context, i->default_source_name, sourceInfoCb, data);
|
||||
pa_context_get_sink_info_list(context, sinkInfoCb, data);
|
||||
pa_context_get_source_info_list(context, sourceInfoCb, data);
|
||||
}
|
||||
|
||||
static const std::array<std::string, 9> ports = {
|
||||
@@ -194,15 +210,17 @@ static const std::array<std::string, 9> ports = {
|
||||
"phone",
|
||||
};
|
||||
|
||||
const std::string waybar::modules::Pulseaudio::getPortIcon() const {
|
||||
const std::vector<std::string> waybar::modules::Pulseaudio::getPulseIcon() const {
|
||||
std::vector<std::string> res = {default_source_name_};
|
||||
std::string nameLC = port_name_ + form_factor_;
|
||||
std::transform(nameLC.begin(), nameLC.end(), nameLC.begin(), ::tolower);
|
||||
for (auto const &port : ports) {
|
||||
if (nameLC.find(port) != std::string::npos) {
|
||||
return port;
|
||||
res.push_back(port);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
return port_name_;
|
||||
return res;
|
||||
}
|
||||
|
||||
auto waybar::modules::Pulseaudio::update() -> void {
|
||||
@@ -252,7 +270,7 @@ auto waybar::modules::Pulseaudio::update() -> void {
|
||||
fmt::arg("format_source", format_source),
|
||||
fmt::arg("source_volume", source_volume_),
|
||||
fmt::arg("source_desc", source_desc_),
|
||||
fmt::arg("icon", getIcon(volume_, getPortIcon()))));
|
||||
fmt::arg("icon", getIcon(volume_, getPulseIcon()))));
|
||||
getState(volume_);
|
||||
|
||||
if (tooltipEnabled()) {
|
||||
@@ -267,7 +285,7 @@ auto waybar::modules::Pulseaudio::update() -> void {
|
||||
fmt::arg("format_source", format_source),
|
||||
fmt::arg("source_volume", source_volume_),
|
||||
fmt::arg("source_desc", source_desc_),
|
||||
fmt::arg("icon", getIcon(volume_, getPortIcon()))));
|
||||
fmt::arg("icon", getIcon(volume_, getPulseIcon()))));
|
||||
} else {
|
||||
label_.set_tooltip_text(desc_);
|
||||
}
|
||||
|
@@ -1,7 +1,12 @@
|
||||
#include "modules/sni/item.hpp"
|
||||
|
||||
#include <gdkmm/general.h>
|
||||
#include <glibmm/main.h>
|
||||
#include <gtkmm/tooltip.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<Glib::ustring> : formatter<std::string> {
|
||||
@@ -39,14 +44,22 @@ Item::Item(const std::string& bn, const std::string& op, const Json::Value& conf
|
||||
object_path(op),
|
||||
icon_size(16),
|
||||
effective_icon_size(0),
|
||||
icon_theme(Gtk::IconTheme::create()),
|
||||
update_pending_(false) {
|
||||
icon_theme(Gtk::IconTheme::create()) {
|
||||
if (config["icon-size"].isUInt()) {
|
||||
icon_size = config["icon-size"].asUInt();
|
||||
}
|
||||
if (config["smooth-scrolling-threshold"].isNumeric()) {
|
||||
scroll_threshold_ = config["smooth-scrolling-threshold"].asDouble();
|
||||
}
|
||||
if (config["show-passive-items"].isBool()) {
|
||||
show_passive_ = config["show-passive-items"].asBool();
|
||||
}
|
||||
event_box.add(image);
|
||||
event_box.add_events(Gdk::BUTTON_PRESS_MASK);
|
||||
event_box.add_events(Gdk::BUTTON_PRESS_MASK | Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK);
|
||||
event_box.signal_button_press_event().connect(sigc::mem_fun(*this, &Item::handleClick));
|
||||
event_box.signal_scroll_event().connect(sigc::mem_fun(*this, &Item::handleScroll));
|
||||
// initial visibility
|
||||
event_box.set_visible(show_passive_);
|
||||
|
||||
cancellable_ = Gio::Cancellable::create();
|
||||
|
||||
@@ -73,12 +86,11 @@ void Item::proxyReady(Glib::RefPtr<Gio::AsyncResult>& result) {
|
||||
|
||||
this->proxy_->signal_signal().connect(sigc::mem_fun(*this, &Item::onSignal));
|
||||
|
||||
if (this->id.empty() || this->category.empty() || this->status.empty()) {
|
||||
if (this->id.empty() || this->category.empty()) {
|
||||
spdlog::error("Invalid Status Notifier Item: {}, {}", bus_name, object_path);
|
||||
return;
|
||||
}
|
||||
this->updateImage();
|
||||
// this->event_box.set_tooltip_text(this->title);
|
||||
|
||||
} catch (const Glib::Error& err) {
|
||||
spdlog::error("Failed to create DBus Proxy for {} {}: {}", bus_name, object_path, err.what());
|
||||
@@ -88,10 +100,24 @@ void Item::proxyReady(Glib::RefPtr<Gio::AsyncResult>& result) {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T get_variant(Glib::VariantBase& value) {
|
||||
T get_variant(const Glib::VariantBase& value) {
|
||||
return Glib::VariantBase::cast_dynamic<Glib::Variant<T>>(value).get();
|
||||
}
|
||||
|
||||
template <>
|
||||
ToolTip get_variant<ToolTip>(const Glib::VariantBase& value) {
|
||||
ToolTip result;
|
||||
// Unwrap (sa(iiay)ss)
|
||||
auto container = value.cast_dynamic<Glib::VariantContainerBase>(value);
|
||||
result.icon_name = get_variant<Glib::ustring>(container.get_child(0));
|
||||
result.text = get_variant<Glib::ustring>(container.get_child(2));
|
||||
auto description = get_variant<Glib::ustring>(container.get_child(3));
|
||||
if (!description.empty()) {
|
||||
result.text = fmt::format("<b>{}</b>\n{}", result.text, description);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void Item::setProperty(const Glib::ustring& name, Glib::VariantBase& value) {
|
||||
try {
|
||||
spdlog::trace("Set tray item property: {}.{} = {}", id.empty() ? bus_name : id, name, value);
|
||||
@@ -102,10 +128,11 @@ void Item::setProperty(const Glib::ustring& name, Glib::VariantBase& value) {
|
||||
id = get_variant<std::string>(value);
|
||||
} else if (name == "Title") {
|
||||
title = get_variant<std::string>(value);
|
||||
if (tooltip.text.empty()) {
|
||||
event_box.set_tooltip_markup(title);
|
||||
}
|
||||
} else if (name == "Status") {
|
||||
status = get_variant<std::string>(value);
|
||||
} else if (name == "WindowId") {
|
||||
window_id = get_variant<int32_t>(value);
|
||||
setStatus(get_variant<Glib::ustring>(value));
|
||||
} else if (name == "IconName") {
|
||||
icon_name = get_variant<std::string>(value);
|
||||
} else if (name == "IconPixmap") {
|
||||
@@ -121,7 +148,10 @@ void Item::setProperty(const Glib::ustring& name, Glib::VariantBase& value) {
|
||||
} else if (name == "AttentionMovieName") {
|
||||
attention_movie_name = get_variant<std::string>(value);
|
||||
} else if (name == "ToolTip") {
|
||||
// TODO: tooltip
|
||||
tooltip = get_variant<ToolTip>(value);
|
||||
if (!tooltip.text.empty()) {
|
||||
event_box.set_tooltip_markup(tooltip.text);
|
||||
}
|
||||
} else if (name == "IconThemePath") {
|
||||
icon_theme_path = get_variant<std::string>(value);
|
||||
if (!icon_theme_path.empty()) {
|
||||
@@ -148,9 +178,22 @@ void Item::setProperty(const Glib::ustring& name, Glib::VariantBase& value) {
|
||||
}
|
||||
}
|
||||
|
||||
void Item::getUpdatedProperties() {
|
||||
update_pending_ = false;
|
||||
void Item::setStatus(const Glib::ustring& value) {
|
||||
Glib::ustring lower = value.lowercase();
|
||||
event_box.set_visible(show_passive_ || lower.compare("passive") != 0);
|
||||
|
||||
auto style = event_box.get_style_context();
|
||||
for (const auto& class_name : style->list_classes()) {
|
||||
style->remove_class(class_name);
|
||||
}
|
||||
if (lower.compare("needsattention") == 0) {
|
||||
// convert status to dash-case for CSS
|
||||
lower = "needs-attention";
|
||||
}
|
||||
style->add_class(lower);
|
||||
}
|
||||
|
||||
void Item::getUpdatedProperties() {
|
||||
auto params = Glib::VariantContainerBase::create_tuple(
|
||||
{Glib::Variant<Glib::ustring>::create(SNI_INTERFACE_NAME)});
|
||||
proxy_->call("org.freedesktop.DBus.Properties.GetAll",
|
||||
@@ -167,33 +210,48 @@ void Item::processUpdatedProperties(Glib::RefPtr<Gio::AsyncResult>& _result) {
|
||||
auto properties = properties_variant.get();
|
||||
|
||||
for (const auto& [name, value] : properties) {
|
||||
Glib::VariantBase old_value;
|
||||
proxy_->get_cached_property(old_value, name);
|
||||
if (!old_value || !value.equal(old_value)) {
|
||||
proxy_->set_cached_property(name, value);
|
||||
if (update_pending_.count(name.raw())) {
|
||||
setProperty(name, const_cast<Glib::VariantBase&>(value));
|
||||
}
|
||||
}
|
||||
|
||||
this->updateImage();
|
||||
// this->event_box.set_tooltip_text(this->title);
|
||||
} catch (const Glib::Error& err) {
|
||||
spdlog::warn("Failed to update properties: {}", err.what());
|
||||
} catch (const std::exception& err) {
|
||||
spdlog::warn("Failed to update properties: {}", err.what());
|
||||
}
|
||||
update_pending_.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mapping from a signal name to a set of possibly changed properties.
|
||||
* Commented signals are not handled by the tray module at the moment.
|
||||
*/
|
||||
static const std::map<std::string_view, std::set<std::string_view>> signal2props = {
|
||||
{"NewTitle", {"Title"}},
|
||||
{"NewIcon", {"IconName", "IconPixmap"}},
|
||||
// {"NewAttentionIcon", {"AttentionIconName", "AttentionIconPixmap", "AttentionMovieName"}},
|
||||
// {"NewOverlayIcon", {"OverlayIconName", "OverlayIconPixmap"}},
|
||||
{"NewIconThemePath", {"IconThemePath"}},
|
||||
{"NewToolTip", {"ToolTip"}},
|
||||
{"NewStatus", {"Status"}},
|
||||
// {"XAyatanaNewLabel", {"XAyatanaLabel"}},
|
||||
};
|
||||
|
||||
void Item::onSignal(const Glib::ustring& sender_name, const Glib::ustring& signal_name,
|
||||
const Glib::VariantContainerBase& arguments) {
|
||||
spdlog::trace("Tray item '{}' got signal {}", id, signal_name);
|
||||
if (!update_pending_ && signal_name.compare(0, 3, "New") == 0) {
|
||||
/* Debounce signals and schedule update of all properties.
|
||||
* Based on behavior of Plasma dataengine for StatusNotifierItem.
|
||||
*/
|
||||
update_pending_ = true;
|
||||
Glib::signal_timeout().connect_once(sigc::mem_fun(*this, &Item::getUpdatedProperties),
|
||||
UPDATE_DEBOUNCE_TIME);
|
||||
auto changed = signal2props.find(signal_name.raw());
|
||||
if (changed != signal2props.end()) {
|
||||
if (update_pending_.empty()) {
|
||||
/* Debounce signals and schedule update of all properties.
|
||||
* Based on behavior of Plasma dataengine for StatusNotifierItem.
|
||||
*/
|
||||
Glib::signal_timeout().connect_once(sigc::mem_fun(*this, &Item::getUpdatedProperties),
|
||||
UPDATE_DEBOUNCE_TIME);
|
||||
}
|
||||
update_pending_.insert(changed->second.begin(), changed->second.end());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,33 +310,42 @@ Glib::RefPtr<Gdk::Pixbuf> Item::extractPixBuf(GVariant* variant) {
|
||||
}
|
||||
|
||||
void Item::updateImage() {
|
||||
auto scale_factor = image.get_scale_factor();
|
||||
auto scaled_icon_size = icon_size * scale_factor;
|
||||
|
||||
image.set_from_icon_name("image-missing", Gtk::ICON_SIZE_MENU);
|
||||
image.set_pixel_size(icon_size);
|
||||
image.set_pixel_size(scaled_icon_size);
|
||||
if (!icon_name.empty()) {
|
||||
try {
|
||||
// Try to find icons specified by path and filename
|
||||
std::ifstream temp(icon_name);
|
||||
if (temp.is_open()) {
|
||||
auto pixbuf = Gdk::Pixbuf::create_from_file(icon_name);
|
||||
|
||||
if (pixbuf->gobj() != nullptr) {
|
||||
// An icon specified by path and filename may be the wrong size for
|
||||
// the tray
|
||||
// Keep the aspect ratio and scale to make the height equal to icon_size
|
||||
// Keep the aspect ratio and scale to make the height equal to scaled_icon_size
|
||||
// If people have non square icons, assume they want it to grow in width not height
|
||||
int width = icon_size * pixbuf->get_width() / pixbuf->get_height();
|
||||
int width = scaled_icon_size * pixbuf->get_width() / pixbuf->get_height();
|
||||
|
||||
pixbuf = pixbuf->scale_simple(width, icon_size, Gdk::InterpType::INTERP_BILINEAR);
|
||||
image.set(pixbuf);
|
||||
pixbuf = pixbuf->scale_simple(width, scaled_icon_size, Gdk::InterpType::INTERP_BILINEAR);
|
||||
|
||||
auto surface = Gdk::Cairo::create_surface_from_pixbuf(pixbuf, 0, image.get_window());
|
||||
image.set(surface);
|
||||
}
|
||||
} else {
|
||||
image.set(getIconByName(icon_name, icon_size));
|
||||
auto icon_by_name = getIconByName(icon_name, scaled_icon_size);
|
||||
auto surface = Gdk::Cairo::create_surface_from_pixbuf(icon_by_name, 0, image.get_window());
|
||||
image.set(surface);
|
||||
}
|
||||
} catch (Glib::Error& e) {
|
||||
spdlog::error("Item '{}': {}", id, static_cast<std::string>(e.what()));
|
||||
}
|
||||
} else if (icon_pixmap) {
|
||||
// An icon extracted may be the wrong size for the tray
|
||||
icon_pixmap = icon_pixmap->scale_simple(icon_size, icon_size, Gdk::InterpType::INTERP_BILINEAR);
|
||||
icon_pixmap = icon_pixmap->scale_simple(icon_size, scaled_icon_size, Gdk::InterpType::INTERP_BILINEAR);
|
||||
auto surface = Gdk::Cairo::create_surface_from_pixbuf(icon_pixmap, 0, image.get_window());
|
||||
image.set(icon_pixmap);
|
||||
}
|
||||
}
|
||||
@@ -360,4 +427,52 @@ bool Item::handleClick(GdkEventButton* const& ev) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Item::handleScroll(GdkEventScroll* const& ev) {
|
||||
int dx = 0, dy = 0;
|
||||
switch (ev->direction) {
|
||||
case GDK_SCROLL_UP:
|
||||
dy = -1;
|
||||
break;
|
||||
case GDK_SCROLL_DOWN:
|
||||
dy = 1;
|
||||
break;
|
||||
case GDK_SCROLL_LEFT:
|
||||
dx = -1;
|
||||
break;
|
||||
case GDK_SCROLL_RIGHT:
|
||||
dx = 1;
|
||||
break;
|
||||
case GDK_SCROLL_SMOOTH:
|
||||
distance_scrolled_x_ += ev->delta_x;
|
||||
distance_scrolled_y_ += ev->delta_y;
|
||||
// check against the configured threshold and ensure that the absolute value >= 1
|
||||
if (distance_scrolled_x_ > scroll_threshold_) {
|
||||
dx = (int)lround(std::max(distance_scrolled_x_, 1.0));
|
||||
distance_scrolled_x_ = 0;
|
||||
} else if (distance_scrolled_x_ < -scroll_threshold_) {
|
||||
dx = (int)lround(std::min(distance_scrolled_x_, -1.0));
|
||||
distance_scrolled_x_ = 0;
|
||||
}
|
||||
if (distance_scrolled_y_ > scroll_threshold_) {
|
||||
dy = (int)lround(std::max(distance_scrolled_y_, 1.0));
|
||||
distance_scrolled_y_ = 0;
|
||||
} else if (distance_scrolled_y_ < -scroll_threshold_) {
|
||||
dy = (int)lround(std::min(distance_scrolled_y_, -1.0));
|
||||
distance_scrolled_y_ = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (dx != 0) {
|
||||
auto parameters = Glib::VariantContainerBase::create_tuple(
|
||||
{Glib::Variant<int>::create(dx), Glib::Variant<Glib::ustring>::create("horizontal")});
|
||||
proxy_->call("Scroll", parameters);
|
||||
}
|
||||
if (dy != 0) {
|
||||
auto parameters = Glib::VariantContainerBase::create_tuple(
|
||||
{Glib::Variant<int>::create(dy), Glib::Variant<Glib::ustring>::create("vertical")});
|
||||
proxy_->call("Scroll", parameters);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace waybar::modules::SNI
|
||||
|
@@ -1,10 +1,28 @@
|
||||
#include "modules/sway/language.hpp"
|
||||
|
||||
#include <fmt/core.h>
|
||||
#include <json/json.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <xkbcommon/xkbregistry.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "modules/sway/ipc/ipc.hpp"
|
||||
#include "util/string.hpp"
|
||||
|
||||
namespace waybar::modules::sway {
|
||||
|
||||
const std::string Language::XKB_LAYOUT_NAMES_KEY = "xkb_layout_names";
|
||||
const std::string Language::XKB_ACTIVE_LAYOUT_NAME_KEY = "xkb_active_layout_name";
|
||||
|
||||
Language::Language(const std::string& id, const Json::Value& config)
|
||||
: ALabel(config, "language", id, "{}", 0, true) {
|
||||
is_variant_displayed = format_.find("{variant}") != std::string::npos;
|
||||
if (config.isMember("tooltip-format")) {
|
||||
tooltip_format_ = config["tooltip-format"].asString();
|
||||
}
|
||||
ipc_.subscribe(R"(["input"])");
|
||||
ipc_.signal_event.connect(sigc::mem_fun(*this, &Language::onEvent));
|
||||
ipc_.signal_cmd.connect(sigc::mem_fun(*this, &Language::onCmd));
|
||||
@@ -21,18 +39,31 @@ Language::Language(const std::string& id, const Json::Value& config)
|
||||
}
|
||||
|
||||
void Language::onCmd(const struct Ipc::ipc_response& res) {
|
||||
if (res.type != static_cast<uint32_t>(IPC_GET_INPUTS)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
auto payload = parser_.parse(res.payload);
|
||||
//Display current layout of a device with a maximum count of layouts, expecting that all will be OK
|
||||
Json::Value::ArrayIndex maxId = 0, max = 0;
|
||||
for(Json::Value::ArrayIndex i = 0; i < payload.size(); i++) {
|
||||
if(payload[i]["xkb_layout_names"].size() > max) {
|
||||
max = payload[i]["xkb_layout_names"].size();
|
||||
maxId = i;
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
auto payload = parser_.parse(res.payload);
|
||||
std::vector<std::string> used_layouts;
|
||||
// Display current layout of a device with a maximum count of layouts, expecting that all will
|
||||
// be OK
|
||||
Json::ArrayIndex max_id = 0, max = 0;
|
||||
for (Json::ArrayIndex i = 0; i < payload.size(); i++) {
|
||||
auto size = payload[i][XKB_LAYOUT_NAMES_KEY].size();
|
||||
if (size > max) {
|
||||
max = size;
|
||||
max_id = i;
|
||||
}
|
||||
}
|
||||
auto layout_name = payload[maxId]["xkb_active_layout_name"].asString().substr(0,2);
|
||||
lang_ = Glib::Markup::escape_text(layout_name);
|
||||
|
||||
for (const auto& layout : payload[max_id][XKB_LAYOUT_NAMES_KEY]) {
|
||||
used_layouts.push_back(layout.asString());
|
||||
}
|
||||
|
||||
init_layouts_map(used_layouts);
|
||||
set_current_layout(payload[max_id][XKB_ACTIVE_LAYOUT_NAME_KEY].asString());
|
||||
dp.emit();
|
||||
} catch (const std::exception& e) {
|
||||
spdlog::error("Language: {}", e.what());
|
||||
@@ -40,12 +71,15 @@ void Language::onCmd(const struct Ipc::ipc_response& res) {
|
||||
}
|
||||
|
||||
void Language::onEvent(const struct Ipc::ipc_response& res) {
|
||||
if (res.type != static_cast<uint32_t>(IPC_EVENT_INPUT)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
auto payload = parser_.parse(res.payload)["input"];
|
||||
auto payload = parser_.parse(res.payload)["input"];
|
||||
if (payload["type"].asString() == "keyboard") {
|
||||
auto layout_name = payload["xkb_active_layout_name"].asString().substr(0,2);
|
||||
lang_ = Glib::Markup::escape_text(layout_name);
|
||||
set_current_layout(payload[XKB_ACTIVE_LAYOUT_NAME_KEY].asString());
|
||||
}
|
||||
dp.emit();
|
||||
} catch (const std::exception& e) {
|
||||
@@ -54,17 +88,102 @@ void Language::onEvent(const struct Ipc::ipc_response& res) {
|
||||
}
|
||||
|
||||
auto Language::update() -> void {
|
||||
if (lang_.empty()) {
|
||||
event_box_.hide();
|
||||
} else {
|
||||
label_.set_markup(fmt::format(format_, lang_));
|
||||
if (tooltipEnabled()) {
|
||||
label_.set_tooltip_text(lang_);
|
||||
}
|
||||
event_box_.show();
|
||||
auto display_layout = trim(fmt::format(format_,
|
||||
fmt::arg("short", layout_.short_name),
|
||||
fmt::arg("long", layout_.full_name),
|
||||
fmt::arg("variant", layout_.variant)));
|
||||
label_.set_markup(display_layout);
|
||||
if (tooltipEnabled()) {
|
||||
if (tooltip_format_ != "") {
|
||||
auto tooltip_display_layout = trim(fmt::format(tooltip_format_,
|
||||
fmt::arg("short", layout_.short_name),
|
||||
fmt::arg("long", layout_.full_name),
|
||||
fmt::arg("variant", layout_.variant)));
|
||||
label_.set_tooltip_markup(tooltip_display_layout);
|
||||
|
||||
} else {
|
||||
label_.set_tooltip_markup(display_layout);
|
||||
}
|
||||
}
|
||||
|
||||
event_box_.show();
|
||||
|
||||
// Call parent update
|
||||
ALabel::update();
|
||||
}
|
||||
|
||||
auto Language::set_current_layout(std::string current_layout) -> void {
|
||||
layout_ = layouts_map_[current_layout];
|
||||
}
|
||||
|
||||
auto Language::init_layouts_map(const std::vector<std::string>& used_layouts) -> void {
|
||||
std::map<std::string, std::vector<Layout*>> found_by_short_names;
|
||||
auto layout = xkb_context_.next_layout();
|
||||
for (; layout != nullptr; layout = xkb_context_.next_layout()) {
|
||||
if (std::find(used_layouts.begin(), used_layouts.end(), layout->full_name) ==
|
||||
used_layouts.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!is_variant_displayed) {
|
||||
auto short_name = layout->short_name;
|
||||
if (found_by_short_names.count(short_name) > 0) {
|
||||
found_by_short_names[short_name].push_back(layout);
|
||||
} else {
|
||||
found_by_short_names[short_name] = {layout};
|
||||
}
|
||||
}
|
||||
|
||||
layouts_map_.emplace(layout->full_name, *layout);
|
||||
}
|
||||
|
||||
if (is_variant_displayed || found_by_short_names.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
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 layouts_with_same_name_list = found_by_short_names[used_layout->short_name];
|
||||
spdlog::info("SIZE: " + std::to_string(layouts_with_same_name_list.size()));
|
||||
if (layouts_with_same_name_list.size() < 2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (short_name_to_number_map.count(used_layout->short_name) == 0) {
|
||||
short_name_to_number_map[used_layout->short_name] = 1;
|
||||
}
|
||||
|
||||
used_layout->short_name =
|
||||
used_layout->short_name + std::to_string(short_name_to_number_map[used_layout->short_name]++);
|
||||
}
|
||||
}
|
||||
|
||||
Language::XKBContext::XKBContext() {
|
||||
context_ = rxkb_context_new(RXKB_CONTEXT_NO_DEFAULT_INCLUDES);
|
||||
rxkb_context_include_path_append_default(context_);
|
||||
rxkb_context_parse_default_ruleset(context_);
|
||||
}
|
||||
|
||||
auto Language::XKBContext::next_layout() -> Layout* {
|
||||
if (xkb_layout_ == nullptr) {
|
||||
xkb_layout_ = rxkb_layout_first(context_);
|
||||
} else {
|
||||
xkb_layout_ = rxkb_layout_next(xkb_layout_);
|
||||
}
|
||||
|
||||
if (xkb_layout_ == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto description = std::string(rxkb_layout_get_description(xkb_layout_));
|
||||
auto name = std::string(rxkb_layout_get_name(xkb_layout_));
|
||||
auto variant_ = rxkb_layout_get_variant(xkb_layout_);
|
||||
std::string variant = variant_ == nullptr ? "" : std::string(variant_);
|
||||
|
||||
layout_ = new Layout{description, name, variant};
|
||||
return layout_;
|
||||
}
|
||||
|
||||
Language::XKBContext::~XKBContext() { rxkb_context_unref(context_); }
|
||||
} // namespace waybar::modules::sway
|
||||
|
Reference in New Issue
Block a user