mirror of
				https://github.com/rad4day/Waybar.git
				synced 2025-11-04 09:42:42 +01:00 
			
		
		
		
	Merge branch 'master' of https://github.com/Alexays/Waybar into pr/anakael/add-name-to-taskbar
This commit is contained in:
		@@ -173,7 +173,7 @@ auto waybar::modules::Backlight::update() -> void {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const auto percent = best->get_max() == 0 ? 100 : best->get_actual() * 100 / best->get_max();
 | 
			
		||||
    const uint8_t percent = best->get_max() == 0 ? 100 : round(best->get_actual() * 100.0f / best->get_max());
 | 
			
		||||
    label_.set_markup(fmt::format(
 | 
			
		||||
        format_, fmt::arg("percent", std::to_string(percent)), fmt::arg("icon", getIcon(percent))));
 | 
			
		||||
    getState(percent);
 | 
			
		||||
 
 | 
			
		||||
@@ -15,10 +15,14 @@ using waybar::modules::waybar_time;
 | 
			
		||||
 | 
			
		||||
waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
 | 
			
		||||
    : ALabel(config, "clock", id, "{:%H:%M}", 60, false, false, true), fixed_time_zone_(false) {
 | 
			
		||||
  if (config_["timezone"].isString()) {
 | 
			
		||||
    spdlog::warn("As using a timezone, some format args may be missing as the date library havn't got a release since 2018.");
 | 
			
		||||
    time_zone_ = date::locate_zone(config_["timezone"].asString());
 | 
			
		||||
    fixed_time_zone_ = true;
 | 
			
		||||
  if (config_["timezones"].isArray() && !config_["timezones"].empty()) {
 | 
			
		||||
    time_zone_idx_ = 0;
 | 
			
		||||
    setTimeZone(config_["timezones"][time_zone_idx_]);
 | 
			
		||||
  } else {
 | 
			
		||||
    setTimeZone(config_["timezone"]);
 | 
			
		||||
  }
 | 
			
		||||
  if (fixed_time_zone_) {
 | 
			
		||||
    spdlog::warn("As using a timezone, some format args may be missing as the date library haven't got a release since 2018.");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (config_["locale"].isString()) {
 | 
			
		||||
@@ -72,6 +76,17 @@ auto waybar::modules::Clock::update() -> void {
 | 
			
		||||
  ALabel::update();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool waybar::modules::Clock::setTimeZone(Json::Value zone_name) {
 | 
			
		||||
  if (!zone_name.isString() || zone_name.asString().empty()) {
 | 
			
		||||
      fixed_time_zone_ = false;
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  time_zone_ = date::locate_zone(zone_name.asString());
 | 
			
		||||
  fixed_time_zone_ = true;
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool waybar::modules::Clock::handleScroll(GdkEventScroll *e) {
 | 
			
		||||
  // defer to user commands if set
 | 
			
		||||
  if (config_["on-scroll-up"].isString() || config_["on-scroll-down"].isString()) {
 | 
			
		||||
@@ -92,14 +107,7 @@ bool waybar::modules::Clock::handleScroll(GdkEventScroll *e) {
 | 
			
		||||
  } else {
 | 
			
		||||
    time_zone_idx_ = time_zone_idx_ == 0 ? nr_zones - 1 : time_zone_idx_ - 1;
 | 
			
		||||
  }
 | 
			
		||||
  auto zone_name = config_["timezones"][time_zone_idx_];
 | 
			
		||||
  if (!zone_name.isString() || zone_name.empty()) {
 | 
			
		||||
    fixed_time_zone_ = false;
 | 
			
		||||
  } else {
 | 
			
		||||
    time_zone_ = date::locate_zone(zone_name.asString());
 | 
			
		||||
    fixed_time_zone_ = true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  setTimeZone(config_["timezones"][time_zone_idx_]);
 | 
			
		||||
  update();
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,14 @@
 | 
			
		||||
#include "modules/cpu.hpp"
 | 
			
		||||
 | 
			
		||||
// In the 80000 version of fmt library authors decided to optimize imports
 | 
			
		||||
// and moved declarations required for fmt::dynamic_format_arg_store in new
 | 
			
		||||
// header fmt/args.h
 | 
			
		||||
#if (FMT_VERSION >= 80000)
 | 
			
		||||
#include <fmt/args.h>
 | 
			
		||||
#else
 | 
			
		||||
#include <fmt/core.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
waybar::modules::Cpu::Cpu(const std::string& id, const Json::Value& config)
 | 
			
		||||
    : ALabel(config, "cpu", id, "{usage}%", 10) {
 | 
			
		||||
  thread_ = [this] {
 | 
			
		||||
@@ -17,7 +26,8 @@ auto waybar::modules::Cpu::update() -> void {
 | 
			
		||||
    label_.set_tooltip_text(tooltip);
 | 
			
		||||
  }
 | 
			
		||||
  auto format = format_;
 | 
			
		||||
  auto state = getState(cpu_usage);
 | 
			
		||||
  auto total_usage = cpu_usage.empty() ? 0 : cpu_usage[0];
 | 
			
		||||
  auto state = getState(total_usage);
 | 
			
		||||
  if (!state.empty() && config_["format-" + state].isString()) {
 | 
			
		||||
    format = config_["format-" + state].asString();
 | 
			
		||||
  }
 | 
			
		||||
@@ -27,13 +37,22 @@ auto waybar::modules::Cpu::update() -> void {
 | 
			
		||||
  } else {
 | 
			
		||||
    event_box_.show();
 | 
			
		||||
    auto icons = std::vector<std::string>{state};
 | 
			
		||||
    label_.set_markup(fmt::format(format,
 | 
			
		||||
                                  fmt::arg("load", cpu_load),
 | 
			
		||||
                                  fmt::arg("usage", cpu_usage),
 | 
			
		||||
                                  fmt::arg("icon", getIcon(cpu_usage, icons)),
 | 
			
		||||
                                  fmt::arg("max_frequency", max_frequency),
 | 
			
		||||
                                  fmt::arg("min_frequency", min_frequency),
 | 
			
		||||
                                  fmt::arg("avg_frequency", avg_frequency)));
 | 
			
		||||
    fmt::dynamic_format_arg_store<fmt::format_context> store;
 | 
			
		||||
    store.push_back(fmt::arg("load", cpu_load));
 | 
			
		||||
    store.push_back(fmt::arg("load", cpu_load));
 | 
			
		||||
    store.push_back(fmt::arg("usage", total_usage));
 | 
			
		||||
    store.push_back(fmt::arg("icon", getIcon(total_usage, icons)));
 | 
			
		||||
    store.push_back(fmt::arg("max_frequency", max_frequency));
 | 
			
		||||
    store.push_back(fmt::arg("min_frequency", min_frequency));
 | 
			
		||||
    store.push_back(fmt::arg("avg_frequency", avg_frequency));
 | 
			
		||||
    for (size_t i = 1; i < cpu_usage.size(); ++i) {
 | 
			
		||||
	    auto core_i = i - 1;
 | 
			
		||||
	    auto core_format = fmt::format("usage{}", core_i);
 | 
			
		||||
	    store.push_back(fmt::arg(core_format.c_str(), cpu_usage[i]));
 | 
			
		||||
	    auto icon_format = fmt::format("icon{}", core_i);
 | 
			
		||||
	    store.push_back(fmt::arg(icon_format.c_str(), getIcon(cpu_usage[i], icons)));
 | 
			
		||||
    }
 | 
			
		||||
    label_.set_markup(fmt::vformat(format, store));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Call parent update
 | 
			
		||||
@@ -48,14 +67,14 @@ double waybar::modules::Cpu::getCpuLoad() {
 | 
			
		||||
  throw std::runtime_error("Can't get Cpu load");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::tuple<uint16_t, std::string> waybar::modules::Cpu::getCpuUsage() {
 | 
			
		||||
std::tuple<std::vector<uint16_t>, std::string> waybar::modules::Cpu::getCpuUsage() {
 | 
			
		||||
  if (prev_times_.empty()) {
 | 
			
		||||
    prev_times_ = parseCpuinfo();
 | 
			
		||||
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
 | 
			
		||||
  }
 | 
			
		||||
  std::vector<std::tuple<size_t, size_t>> curr_times = parseCpuinfo();
 | 
			
		||||
  std::string                             tooltip;
 | 
			
		||||
  uint16_t                                usage = 0;
 | 
			
		||||
  std::vector<uint16_t>                   usage;
 | 
			
		||||
  for (size_t i = 0; i < curr_times.size(); ++i) {
 | 
			
		||||
    auto [curr_idle, curr_total] = curr_times[i];
 | 
			
		||||
    auto [prev_idle, prev_total] = prev_times_[i];
 | 
			
		||||
@@ -63,11 +82,11 @@ std::tuple<uint16_t, std::string> waybar::modules::Cpu::getCpuUsage() {
 | 
			
		||||
    const float delta_total = curr_total - prev_total;
 | 
			
		||||
    uint16_t    tmp = 100 * (1 - delta_idle / delta_total);
 | 
			
		||||
    if (i == 0) {
 | 
			
		||||
      usage = tmp;
 | 
			
		||||
      tooltip = fmt::format("Total: {}%", tmp);
 | 
			
		||||
    } else {
 | 
			
		||||
      tooltip = tooltip + fmt::format("\nCore{}: {}%", i - 1, tmp);
 | 
			
		||||
    }
 | 
			
		||||
    usage.push_back(tmp);
 | 
			
		||||
  }
 | 
			
		||||
  prev_times_ = curr_times;
 | 
			
		||||
  return {usage, tooltip};
 | 
			
		||||
 
 | 
			
		||||
@@ -45,9 +45,9 @@ auto waybar::modules::Disk::update() -> void {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  auto free = pow_format(stats.f_bavail * stats.f_frsize, "B", true);
 | 
			
		||||
  auto used = pow_format((stats.f_blocks - stats.f_bavail) * stats.f_frsize, "B", true);
 | 
			
		||||
  auto used = pow_format((stats.f_blocks - stats.f_bfree) * stats.f_frsize, "B", true);
 | 
			
		||||
  auto total = pow_format(stats.f_blocks * stats.f_frsize, "B", true);
 | 
			
		||||
  auto percentage_used = (stats.f_blocks - stats.f_bavail) * 100 / stats.f_blocks;
 | 
			
		||||
  auto percentage_used = (stats.f_blocks - stats.f_bfree) * 100 / stats.f_blocks;
 | 
			
		||||
 | 
			
		||||
  auto format = format_;
 | 
			
		||||
  auto state = getState(percentage_used);
 | 
			
		||||
 
 | 
			
		||||
@@ -15,11 +15,11 @@ auto waybar::modules::Memory::update() -> void {
 | 
			
		||||
  unsigned long memfree;
 | 
			
		||||
  if (meminfo_.count("MemAvailable")) {
 | 
			
		||||
    // New kernels (3.4+) have an accurate available memory field.
 | 
			
		||||
    memfree = meminfo_["MemAvailable"];
 | 
			
		||||
    memfree = meminfo_["MemAvailable"] + meminfo_["zfs_size"];
 | 
			
		||||
  } else {
 | 
			
		||||
    // Old kernel; give a best-effort approximation of available memory.
 | 
			
		||||
    memfree = meminfo_["MemFree"] + meminfo_["Buffers"] + meminfo_["Cached"] +
 | 
			
		||||
              meminfo_["SReclaimable"] - meminfo_["Shmem"];
 | 
			
		||||
              meminfo_["SReclaimable"] - meminfo_["Shmem"] + meminfo_["zfs_size"];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (memtotal > 0 && memfree >= 0) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,29 @@
 | 
			
		||||
#include "modules/memory.hpp"
 | 
			
		||||
 | 
			
		||||
static unsigned zfsArcSize() {
 | 
			
		||||
  std::ifstream zfs_arc_stats{"/proc/spl/kstat/zfs/arcstats"};
 | 
			
		||||
 | 
			
		||||
  if (zfs_arc_stats.is_open()) {
 | 
			
		||||
    std::string   name;
 | 
			
		||||
    std::string   type;
 | 
			
		||||
    unsigned long data{0};
 | 
			
		||||
 | 
			
		||||
    std::string line;
 | 
			
		||||
    while (std::getline(zfs_arc_stats, line)) {
 | 
			
		||||
      std::stringstream(line) >> name >> type >> data;
 | 
			
		||||
 | 
			
		||||
      if (name == "size") {
 | 
			
		||||
        return data / 1024;  // convert to kB
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void waybar::modules::Memory::parseMeminfo() {
 | 
			
		||||
  const std::string data_dir_ = "/proc/meminfo";
 | 
			
		||||
  std::ifstream info(data_dir_);
 | 
			
		||||
  std::ifstream     info(data_dir_);
 | 
			
		||||
  if (!info.is_open()) {
 | 
			
		||||
    throw std::runtime_error("Can't open " + data_dir_);
 | 
			
		||||
  }
 | 
			
		||||
@@ -17,4 +38,6 @@ void waybar::modules::Memory::parseMeminfo() {
 | 
			
		||||
    int64_t     value = std::stol(line.substr(posDelim + 1));
 | 
			
		||||
    meminfo_[name] = value;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  meminfo_["zfs_size"] = zfsArcSize();
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -131,6 +131,9 @@ void waybar::modules::MPD::setLabel() {
 | 
			
		||||
    date = getTag(MPD_TAG_DATE);
 | 
			
		||||
    song_pos = mpd_status_get_song_pos(status_.get());
 | 
			
		||||
    volume = mpd_status_get_volume(status_.get());
 | 
			
		||||
    if (volume < 0) {
 | 
			
		||||
      volume = 0;
 | 
			
		||||
    }
 | 
			
		||||
    queue_length = mpd_status_get_queue_length(status_.get());
 | 
			
		||||
    elapsedTime = std::chrono::seconds(mpd_status_get_elapsed_time(status_.get()));
 | 
			
		||||
    totalTime = std::chrono::seconds(mpd_status_get_total_time(status_.get()));
 | 
			
		||||
 
 | 
			
		||||
@@ -1,87 +1,77 @@
 | 
			
		||||
#include "modules/network.hpp"
 | 
			
		||||
#include <linux/if.h>
 | 
			
		||||
#include <spdlog/spdlog.h>
 | 
			
		||||
#include <sys/eventfd.h>
 | 
			
		||||
#include <linux/if.h>
 | 
			
		||||
#include <fstream>
 | 
			
		||||
 | 
			
		||||
#include <cassert>
 | 
			
		||||
#include <fstream>
 | 
			
		||||
#include <sstream>
 | 
			
		||||
#include <optional>
 | 
			
		||||
 | 
			
		||||
#include "modules/network.hpp"
 | 
			
		||||
#include "util/format.hpp"
 | 
			
		||||
#ifdef WANT_RFKILL
 | 
			
		||||
#include "util/rfkill.hpp"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
 | 
			
		||||
using namespace waybar::util;
 | 
			
		||||
 | 
			
		||||
constexpr const char *NETSTAT_FILE =
 | 
			
		||||
    "/proc/net/netstat";  // std::ifstream does not take std::string_view as param
 | 
			
		||||
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) {
 | 
			
		||||
  if (!netstat) {
 | 
			
		||||
    spdlog::warn("Failed to open netstat file {}", NETSTAT_FILE);
 | 
			
		||||
    return {};
 | 
			
		||||
  }
 | 
			
		||||
  netstat.seekg(std::ios_base::beg);
 | 
			
		||||
 | 
			
		||||
  // finding corresponding line (category)
 | 
			
		||||
  // looks into the file for the first line starting by the 'category' string
 | 
			
		||||
  auto starts_with = [](const std::string &str, std::string_view start) {
 | 
			
		||||
    return start == std::string_view{str.data(), std::min(str.size(), start.size())};
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  std::string read;
 | 
			
		||||
  while (std::getline(netstat, read) && !starts_with(read, category))
 | 
			
		||||
    ;
 | 
			
		||||
  if (!starts_with(read, category)) {
 | 
			
		||||
    spdlog::warn("Category '{}' not found in netstat file {}", category, NETSTAT_FILE);
 | 
			
		||||
    return {};
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // finding corresponding column (key)
 | 
			
		||||
  // looks into the fetched line for the first word (space separated) equal to 'key'
 | 
			
		||||
  int  index = 0;
 | 
			
		||||
  auto r_it = read.begin();
 | 
			
		||||
  auto k_it = key.begin();
 | 
			
		||||
  while (k_it != key.end() && r_it != read.end()) {
 | 
			
		||||
    if (*r_it != *k_it) {
 | 
			
		||||
      r_it = std::find(r_it, read.end(), ' ');
 | 
			
		||||
      if (r_it != read.end()) {
 | 
			
		||||
        ++r_it;
 | 
			
		||||
      }
 | 
			
		||||
      k_it = key.begin();
 | 
			
		||||
      ++index;
 | 
			
		||||
    } else {
 | 
			
		||||
      ++r_it;
 | 
			
		||||
      ++k_it;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (r_it == read.end() && k_it != key.end()) {
 | 
			
		||||
    spdlog::warn(
 | 
			
		||||
        "Key '{}' not found in category '{}' of netstat file {}", key, category, NETSTAT_FILE);
 | 
			
		||||
    return {};
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // finally accessing value
 | 
			
		||||
  // accesses the line right under the fetched one
 | 
			
		||||
  std::getline(netstat, read);
 | 
			
		||||
  assert(starts_with(read, category));
 | 
			
		||||
  std::istringstream iss(read);
 | 
			
		||||
  while (index--) {
 | 
			
		||||
    std::getline(iss, read, ' ');
 | 
			
		||||
  }
 | 
			
		||||
  unsigned long long value;
 | 
			
		||||
  iss >> value;
 | 
			
		||||
  return value;
 | 
			
		||||
}
 | 
			
		||||
}  // namespace
 | 
			
		||||
 | 
			
		||||
constexpr const char *NETDEV_FILE =
 | 
			
		||||
    "/proc/net/dev";  // std::ifstream does not take std::string_view as param
 | 
			
		||||
std::optional<std::pair<unsigned long long, unsigned long long>>
 | 
			
		||||
waybar::modules::Network::readBandwidthUsage() {
 | 
			
		||||
  std::ifstream netdev(NETDEV_FILE);
 | 
			
		||||
  if (!netdev) {
 | 
			
		||||
    spdlog::warn("Failed to open netdev file {}", NETDEV_FILE);
 | 
			
		||||
    return {};
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  std::string line;
 | 
			
		||||
  // skip the headers (first two lines)
 | 
			
		||||
  std::getline(netdev, line);
 | 
			
		||||
  std::getline(netdev, line);
 | 
			
		||||
 | 
			
		||||
  unsigned long long receivedBytes = 0ull;
 | 
			
		||||
  unsigned long long transmittedBytes = 0ull;
 | 
			
		||||
  while (std::getline(netdev, line)) {
 | 
			
		||||
    std::istringstream iss(line);
 | 
			
		||||
 | 
			
		||||
    std::string ifacename;
 | 
			
		||||
    iss >> ifacename;  // ifacename contains "eth0:"
 | 
			
		||||
    ifacename.pop_back();  // remove trailing ':'
 | 
			
		||||
    if (!checkInterface(ifacename)) {
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // The rest of the line consists of whitespace separated counts divided
 | 
			
		||||
    // into two groups (receive and transmit). Each group has the following
 | 
			
		||||
    // columns: bytes, packets, errs, drop, fifo, frame, compressed, multicast
 | 
			
		||||
    //
 | 
			
		||||
    // We only care about the bytes count, so we'll just ignore the 7 other
 | 
			
		||||
    // columns.
 | 
			
		||||
    unsigned long long r = 0ull;
 | 
			
		||||
    unsigned long long t = 0ull;
 | 
			
		||||
    // Read received bytes
 | 
			
		||||
    iss >> r;
 | 
			
		||||
    // Skip all the other columns in the received group
 | 
			
		||||
    for (int colsToSkip = 7; colsToSkip > 0; colsToSkip--) {
 | 
			
		||||
      // skip whitespace between columns
 | 
			
		||||
      while (iss.peek() == ' ') { iss.ignore(); }
 | 
			
		||||
      // skip the irrelevant column
 | 
			
		||||
      while (iss.peek() != ' ') { iss.ignore(); }
 | 
			
		||||
    }
 | 
			
		||||
    // Read transmit bytes
 | 
			
		||||
    iss >> t;
 | 
			
		||||
 | 
			
		||||
    receivedBytes += r;
 | 
			
		||||
    transmittedBytes += t;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return {{receivedBytes, transmittedBytes}};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
waybar::modules::Network::Network(const std::string &id, const Json::Value &config)
 | 
			
		||||
    : ALabel(config, "network", id, DEFAULT_FORMAT, 60),
 | 
			
		||||
      ifid_(-1),
 | 
			
		||||
@@ -106,17 +96,12 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf
 | 
			
		||||
  // the module start with no text, but the the event_box_ is shown.
 | 
			
		||||
  label_.set_markup("<s></s>");
 | 
			
		||||
 | 
			
		||||
  auto down_octets = read_netstat(BANDWIDTH_CATEGORY, BANDWIDTH_DOWN_TOTAL_KEY);
 | 
			
		||||
  auto up_octets = read_netstat(BANDWIDTH_CATEGORY, BANDWIDTH_UP_TOTAL_KEY);
 | 
			
		||||
  if (down_octets) {
 | 
			
		||||
    bandwidth_down_total_ = *down_octets;
 | 
			
		||||
  auto bandwidth = readBandwidthUsage();
 | 
			
		||||
  if (bandwidth.has_value()) {
 | 
			
		||||
    bandwidth_down_total_ = (*bandwidth).first;
 | 
			
		||||
    bandwidth_up_total_ = (*bandwidth).second;
 | 
			
		||||
  } else {
 | 
			
		||||
    bandwidth_down_total_ = 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (up_octets) {
 | 
			
		||||
    bandwidth_up_total_ = *up_octets;
 | 
			
		||||
  } else {
 | 
			
		||||
    bandwidth_up_total_ = 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -303,20 +288,21 @@ const std::string waybar::modules::Network::getNetworkState() const {
 | 
			
		||||
auto waybar::modules::Network::update() -> void {
 | 
			
		||||
  std::lock_guard<std::mutex> lock(mutex_);
 | 
			
		||||
  std::string                 tooltip_format;
 | 
			
		||||
  auto down_octets = read_netstat(BANDWIDTH_CATEGORY, BANDWIDTH_DOWN_TOTAL_KEY);
 | 
			
		||||
  auto up_octets = read_netstat(BANDWIDTH_CATEGORY, BANDWIDTH_UP_TOTAL_KEY);
 | 
			
		||||
 | 
			
		||||
  unsigned long long bandwidth_down = 0;
 | 
			
		||||
  if (down_octets) {
 | 
			
		||||
    bandwidth_down = *down_octets - bandwidth_down_total_;
 | 
			
		||||
    bandwidth_down_total_ = *down_octets;
 | 
			
		||||
  auto bandwidth = readBandwidthUsage();
 | 
			
		||||
  auto bandwidth_down = 0ull;
 | 
			
		||||
  auto bandwidth_up = 0ull;
 | 
			
		||||
  if (bandwidth.has_value()) {
 | 
			
		||||
    auto down_octets = (*bandwidth).first;
 | 
			
		||||
    auto up_octets = (*bandwidth).second;
 | 
			
		||||
 | 
			
		||||
    bandwidth_down = down_octets - bandwidth_down_total_;
 | 
			
		||||
    bandwidth_down_total_ = down_octets;
 | 
			
		||||
 | 
			
		||||
    bandwidth_up = up_octets - bandwidth_up_total_;
 | 
			
		||||
    bandwidth_up_total_ = up_octets;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  unsigned long long bandwidth_up = 0;
 | 
			
		||||
  if (up_octets) {
 | 
			
		||||
    bandwidth_up = *up_octets - bandwidth_up_total_;
 | 
			
		||||
    bandwidth_up_total_ = *up_octets;
 | 
			
		||||
  }
 | 
			
		||||
  if (!alt_) {
 | 
			
		||||
    auto state = getNetworkState();
 | 
			
		||||
    if (!state_.empty() && label_.get_style_context()->has_class(state_)) {
 | 
			
		||||
@@ -348,6 +334,7 @@ auto waybar::modules::Network::update() -> void {
 | 
			
		||||
      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("icon", getIcon(signal_strength_, state_)),
 | 
			
		||||
@@ -376,6 +363,7 @@ auto waybar::modules::Network::update() -> void {
 | 
			
		||||
          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("icon", getIcon(signal_strength_, state_)),
 | 
			
		||||
@@ -409,6 +397,7 @@ void waybar::modules::Network::clearIface() {
 | 
			
		||||
  ifname_.clear();
 | 
			
		||||
  essid_.clear();
 | 
			
		||||
  ipaddr_.clear();
 | 
			
		||||
  gwaddr_.clear();
 | 
			
		||||
  netmask_.clear();
 | 
			
		||||
  carrier_ = false;
 | 
			
		||||
  cidr_ = 0;
 | 
			
		||||
@@ -466,7 +455,7 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!is_del_event && ifi->ifi_index == net->ifid_) {
 | 
			
		||||
      // Update inferface information
 | 
			
		||||
      // Update interface information
 | 
			
		||||
      if (net->ifname_.empty() && ifname != NULL) {
 | 
			
		||||
        std::string new_ifname (ifname, ifname_len);
 | 
			
		||||
        net->ifname_ = new_ifname;
 | 
			
		||||
@@ -581,6 +570,7 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) {
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
    char	temp_gw_addr[INET6_ADDRSTRLEN];
 | 
			
		||||
  case RTM_DELROUTE:
 | 
			
		||||
    is_del_event = true;
 | 
			
		||||
  case RTM_NEWROUTE: {
 | 
			
		||||
@@ -595,6 +585,7 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) {
 | 
			
		||||
    int            temp_idx = -1;
 | 
			
		||||
    uint32_t       priority = 0;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /* Find the message(s) concerting the main routing table, each message
 | 
			
		||||
     * corresponds to a single routing table entry.
 | 
			
		||||
     */
 | 
			
		||||
@@ -612,9 +603,10 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) {
 | 
			
		||||
      case RTA_GATEWAY:
 | 
			
		||||
        /* The gateway of the route.
 | 
			
		||||
         *
 | 
			
		||||
         * If someone every needs to figure out the gateway address as well,
 | 
			
		||||
         * If someone ever needs to figure out the gateway address as well,
 | 
			
		||||
         * it's here as the attribute payload.
 | 
			
		||||
         */
 | 
			
		||||
	inet_ntop(net->family_, RTA_DATA(attr), temp_gw_addr, sizeof(temp_gw_addr));
 | 
			
		||||
        has_gateway = true;
 | 
			
		||||
        break;
 | 
			
		||||
      case RTA_DST: {
 | 
			
		||||
@@ -655,8 +647,8 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) {
 | 
			
		||||
        net->clearIface();
 | 
			
		||||
        net->ifid_ = temp_idx;
 | 
			
		||||
        net->route_priority = priority;
 | 
			
		||||
 | 
			
		||||
        spdlog::debug("network: new default route via if{} metric {}", temp_idx, priority);
 | 
			
		||||
        net->gwaddr_ = temp_gw_addr;
 | 
			
		||||
        spdlog::debug("network: new default route via {} on if{} metric {}", temp_gw_addr, temp_idx, priority);
 | 
			
		||||
 | 
			
		||||
        /* Ask ifname associated with temp_idx as well as carrier status */
 | 
			
		||||
        struct ifinfomsg ifinfo_hdr = {
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,6 @@
 | 
			
		||||
 | 
			
		||||
#include "client.hpp"
 | 
			
		||||
#include "modules/river/tags.hpp"
 | 
			
		||||
#include "river-status-unstable-v1-client-protocol.h"
 | 
			
		||||
#include "xdg-output-unstable-v1-client-protocol.h"
 | 
			
		||||
 | 
			
		||||
namespace waybar::modules::river {
 | 
			
		||||
@@ -33,6 +32,23 @@ static const zriver_output_status_v1_listener output_status_listener_impl{
 | 
			
		||||
    .urgent_tags = listen_urgent_tags,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void listen_command_success(void *data,
 | 
			
		||||
                                   struct zriver_command_callback_v1 *zriver_command_callback_v1,
 | 
			
		||||
                                   const char *output) {
 | 
			
		||||
  // Do nothing but keep listener to avoid crashing when command was successful
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void listen_command_failure(void *data,
 | 
			
		||||
                                   struct zriver_command_callback_v1 *zriver_command_callback_v1,
 | 
			
		||||
                                   const char *output) {
 | 
			
		||||
  spdlog::error("failure when selecting/toggling tags {}", output);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const zriver_command_callback_v1_listener command_callback_listener_impl {
 | 
			
		||||
    .success = listen_command_success,
 | 
			
		||||
    .failure = listen_command_failure,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void handle_global(void *data, struct wl_registry *registry, uint32_t name,
 | 
			
		||||
                          const char *interface, uint32_t version) {
 | 
			
		||||
  if (std::strcmp(interface, zriver_status_manager_v1_interface.name) == 0) {
 | 
			
		||||
@@ -43,18 +59,33 @@ static void handle_global(void *data, struct wl_registry *registry, uint32_t nam
 | 
			
		||||
    static_cast<Tags *>(data)->status_manager_ = static_cast<struct zriver_status_manager_v1 *>(
 | 
			
		||||
        wl_registry_bind(registry, name, &zriver_status_manager_v1_interface, version));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (std::strcmp(interface, zriver_control_v1_interface.name) == 0) {
 | 
			
		||||
    version = std::min<uint32_t>(version, 1);
 | 
			
		||||
    static_cast<Tags *>(data)->control_ = static_cast<struct zriver_control_v1 *>(
 | 
			
		||||
        wl_registry_bind(registry, name, &zriver_control_v1_interface, version));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (std::strcmp(interface, wl_seat_interface.name) == 0) {
 | 
			
		||||
    version = std::min<uint32_t>(version, 1);
 | 
			
		||||
    static_cast<Tags *>(data)->seat_ = static_cast<struct wl_seat *>(
 | 
			
		||||
        wl_registry_bind(registry, name, &wl_seat_interface, version));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) {
 | 
			
		||||
  /* Ignore event */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static const wl_registry_listener registry_listener_impl = {.global = handle_global,
 | 
			
		||||
                                                            .global_remove = handle_global_remove};
 | 
			
		||||
 | 
			
		||||
Tags::Tags(const std::string &id, const waybar::Bar &bar, const Json::Value &config)
 | 
			
		||||
    : waybar::AModule(config, "tags", id, false, false),
 | 
			
		||||
      status_manager_{nullptr},
 | 
			
		||||
      control_{nullptr},
 | 
			
		||||
      seat_{nullptr},
 | 
			
		||||
      bar_(bar),
 | 
			
		||||
      box_{bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0},
 | 
			
		||||
      output_status_{nullptr} {
 | 
			
		||||
@@ -68,6 +99,14 @@ Tags::Tags(const std::string &id, const waybar::Bar &bar, const Json::Value &con
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!control_) {
 | 
			
		||||
    spdlog::error("river_control_v1 not advertised");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!seat_) {
 | 
			
		||||
    spdlog::error("wl_seat not advertised");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  box_.set_name("tags");
 | 
			
		||||
  if (!id.empty()) {
 | 
			
		||||
    box_.get_style_context()->add_class(id);
 | 
			
		||||
@@ -89,11 +128,17 @@ Tags::Tags(const std::string &id, const waybar::Bar &bar, const Json::Value &con
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  uint32_t i = 1;
 | 
			
		||||
  for (const auto &tag_label : tag_labels) {
 | 
			
		||||
    Gtk::Button &button = buttons_.emplace_back(tag_label);
 | 
			
		||||
    button.set_relief(Gtk::RELIEF_NONE);
 | 
			
		||||
    box_.pack_start(button, false, false, 0);
 | 
			
		||||
    if (!config_["disable-click"].asBool()) {
 | 
			
		||||
      button.signal_clicked().connect(sigc::bind(sigc::mem_fun(*this, &Tags::handle_primary_clicked), i));
 | 
			
		||||
      button.signal_button_press_event().connect(sigc::bind(sigc::mem_fun(*this, &Tags::handle_button_press), i));
 | 
			
		||||
    }
 | 
			
		||||
    button.show();
 | 
			
		||||
    i <<= 1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  struct wl_output *output = gdk_wayland_monitor_get_wl_output(bar_.output->monitor->gobj());
 | 
			
		||||
@@ -107,6 +152,31 @@ Tags::~Tags() {
 | 
			
		||||
  if (output_status_) {
 | 
			
		||||
    zriver_output_status_v1_destroy(output_status_);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (control_) {
 | 
			
		||||
    zriver_control_v1_destroy(control_);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Tags::handle_primary_clicked(uint32_t tag) {
 | 
			
		||||
  // Send river command to select tag on left mouse click
 | 
			
		||||
  zriver_command_callback_v1 *callback;
 | 
			
		||||
  zriver_control_v1_add_argument(control_, "set-focused-tags");
 | 
			
		||||
  zriver_control_v1_add_argument(control_, std::to_string(tag).c_str());
 | 
			
		||||
  callback = zriver_control_v1_run_command(control_, seat_);
 | 
			
		||||
  zriver_command_callback_v1_add_listener(callback, &command_callback_listener_impl, nullptr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Tags::handle_button_press(GdkEventButton *event_button, uint32_t tag) {
 | 
			
		||||
  if (event_button->type == GDK_BUTTON_PRESS && event_button->button == 3) {
 | 
			
		||||
    // Send river command to toggle tag on right mouse click
 | 
			
		||||
    zriver_command_callback_v1 *callback;
 | 
			
		||||
    zriver_control_v1_add_argument(control_, "toggle-focused-tags");
 | 
			
		||||
    zriver_control_v1_add_argument(control_, std::to_string(tag).c_str());
 | 
			
		||||
    callback = zriver_control_v1_run_command(control_, seat_);
 | 
			
		||||
    zriver_command_callback_v1_add_listener(callback, &command_callback_listener_impl, nullptr);
 | 
			
		||||
  }
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Tags::handle_focused_tags(uint32_t tags) {
 | 
			
		||||
 
 | 
			
		||||
@@ -62,6 +62,7 @@ Item::Item(const std::string& bn, const std::string& op, const Json::Value& conf
 | 
			
		||||
  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.show_all();
 | 
			
		||||
  event_box.set_visible(show_passive_);
 | 
			
		||||
 | 
			
		||||
  cancellable_ = Gio::Cancellable::create();
 | 
			
		||||
@@ -287,7 +288,11 @@ Glib::RefPtr<Gdk::Pixbuf> Item::extractPixBuf(GVariant* variant) {
 | 
			
		||||
          if (array != nullptr) {
 | 
			
		||||
            g_free(array);
 | 
			
		||||
          }
 | 
			
		||||
#if GLIB_MAJOR_VERSION >= 2 && GLIB_MINOR_VERSION >= 68
 | 
			
		||||
          array = static_cast<guchar*>(g_memdup2(data, size));
 | 
			
		||||
#else
 | 
			
		||||
          array = static_cast<guchar*>(g_memdup(data, size));
 | 
			
		||||
#endif
 | 
			
		||||
          lwidth = width;
 | 
			
		||||
          lheight = height;
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -35,11 +35,8 @@ void Tray::onRemove(std::unique_ptr<Item>& item) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto Tray::update() -> void {
 | 
			
		||||
  if (box_.get_children().empty()) {
 | 
			
		||||
    box_.hide();
 | 
			
		||||
  } else {
 | 
			
		||||
    box_.show_all();
 | 
			
		||||
  }
 | 
			
		||||
  // Show tray only when items are available
 | 
			
		||||
  box_.set_visible(!box_.get_children().empty());
 | 
			
		||||
  // Call parent update
 | 
			
		||||
  AModule::update();
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -20,15 +20,15 @@ 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 (format_.find("{}") != std::string::npos || format_.find("{short}") != std::string::npos) {
 | 
			
		||||
		displayed_short_flag |= static_cast<std::byte>(DispayedShortFlag::ShortName);
 | 
			
		||||
	}
 | 
			
		||||
	if (format_.find("{shortDescription}") != std::string::npos) {
 | 
			
		||||
		displayed_short_flag |= static_cast<std::byte>(DispayedShortFlag::ShortDescription);
 | 
			
		||||
	}
 | 
			
		||||
	if (config.isMember("tooltip-format")) {
 | 
			
		||||
		tooltip_format_ = config["tooltip-format"].asString();
 | 
			
		||||
	}
 | 
			
		||||
  if (format_.find("{}") != std::string::npos || format_.find("{short}") != std::string::npos) {
 | 
			
		||||
    displayed_short_flag |= static_cast<std::byte>(DispayedShortFlag::ShortName);
 | 
			
		||||
  }
 | 
			
		||||
  if (format_.find("{shortDescription}") != std::string::npos) {
 | 
			
		||||
    displayed_short_flag |= static_cast<std::byte>(DispayedShortFlag::ShortDescription);
 | 
			
		||||
  }
 | 
			
		||||
  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));
 | 
			
		||||
@@ -102,16 +102,16 @@ auto Language::update() -> void {
 | 
			
		||||
                                         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("shortDescription", layout_.short_description),
 | 
			
		||||
																						 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);
 | 
			
		||||
		}
 | 
			
		||||
    if (tooltip_format_ != "") {
 | 
			
		||||
      auto tooltip_display_layout = trim(fmt::format(tooltip_format_,
 | 
			
		||||
                                                     fmt::arg("short", layout_.short_name),
 | 
			
		||||
                                                     fmt::arg("shortDescription", layout_.short_description),
 | 
			
		||||
                                                     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();
 | 
			
		||||
@@ -126,7 +126,7 @@ auto Language::set_current_layout(std::string current_layout) -> void {
 | 
			
		||||
 | 
			
		||||
auto Language::init_layouts_map(const std::vector<std::string>& used_layouts) -> void {
 | 
			
		||||
  std::map<std::string, std::vector<Layout*>> found_by_short_names;
 | 
			
		||||
	XKBContext xkb_context;
 | 
			
		||||
  XKBContext xkb_context;
 | 
			
		||||
  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) ==
 | 
			
		||||
@@ -161,15 +161,15 @@ auto Language::init_layouts_map(const std::vector<std::string>& used_layouts) ->
 | 
			
		||||
    if (short_name_to_number_map.count(used_layout->short_name) == 0) {
 | 
			
		||||
      short_name_to_number_map[used_layout->short_name] = 1;
 | 
			
		||||
    }
 | 
			
		||||
		
 | 
			
		||||
		if (displayed_short_flag != static_cast<std::byte>(0)) {
 | 
			
		||||
			int& number = short_name_to_number_map[used_layout->short_name];
 | 
			
		||||
			used_layout->short_name =
 | 
			
		||||
					used_layout->short_name + std::to_string(number);
 | 
			
		||||
			used_layout->short_description =
 | 
			
		||||
					used_layout->short_description + std::to_string(number);
 | 
			
		||||
			++number;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
    if (displayed_short_flag != static_cast<std::byte>(0)) {
 | 
			
		||||
      int& number = short_name_to_number_map[used_layout->short_name];
 | 
			
		||||
      used_layout->short_name =
 | 
			
		||||
          used_layout->short_name + std::to_string(number);
 | 
			
		||||
      used_layout->short_description =
 | 
			
		||||
          used_layout->short_description + std::to_string(number);
 | 
			
		||||
      ++number;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -195,14 +195,14 @@ auto Language::XKBContext::next_layout() -> Layout* {
 | 
			
		||||
  auto        variant_ = rxkb_layout_get_variant(xkb_layout_);
 | 
			
		||||
  std::string variant = variant_ == nullptr ? "" : std::string(variant_);
 | 
			
		||||
  auto        short_description_ = rxkb_layout_get_brief(xkb_layout_);
 | 
			
		||||
	std::string short_description;
 | 
			
		||||
	if (short_description_ != nullptr) {
 | 
			
		||||
			short_description = std::string(short_description_);
 | 
			
		||||
			base_layouts_by_name_.emplace(name, xkb_layout_);
 | 
			
		||||
	} else {
 | 
			
		||||
			auto base_layout = base_layouts_by_name_[name];
 | 
			
		||||
			short_description = base_layout == nullptr ? "" : std::string(rxkb_layout_get_brief(base_layout));
 | 
			
		||||
	}
 | 
			
		||||
  std::string short_description;
 | 
			
		||||
  if (short_description_ != nullptr) {
 | 
			
		||||
    short_description = std::string(short_description_);
 | 
			
		||||
    base_layouts_by_name_.emplace(name, xkb_layout_);
 | 
			
		||||
  } else {
 | 
			
		||||
    auto base_layout = base_layouts_by_name_[name];
 | 
			
		||||
    short_description = base_layout == nullptr ? "" : std::string(rxkb_layout_get_brief(base_layout));
 | 
			
		||||
  }
 | 
			
		||||
  delete layout_;
 | 
			
		||||
  layout_ = new Layout{description, name, variant, short_description};
 | 
			
		||||
  return layout_;
 | 
			
		||||
 
 | 
			
		||||
@@ -68,15 +68,22 @@ auto Window::update() -> void {
 | 
			
		||||
 | 
			
		||||
int leafNodesInWorkspace(const Json::Value& node) {
 | 
			
		||||
  auto const& nodes = node["nodes"];
 | 
			
		||||
  if(nodes.empty()) {
 | 
			
		||||
  auto const& floating_nodes = node["floating_nodes"];
 | 
			
		||||
  if(nodes.empty() && floating_nodes.empty()) {
 | 
			
		||||
    if(node["type"] == "workspace")
 | 
			
		||||
      return 0;
 | 
			
		||||
    else
 | 
			
		||||
      return 1;
 | 
			
		||||
  }
 | 
			
		||||
  int sum = 0;
 | 
			
		||||
  for(auto const& node : nodes)
 | 
			
		||||
    sum += leafNodesInWorkspace(node);
 | 
			
		||||
  if (!nodes.empty()) {
 | 
			
		||||
    for(auto const& node : nodes)
 | 
			
		||||
      sum += leafNodesInWorkspace(node);
 | 
			
		||||
  }
 | 
			
		||||
  if (!floating_nodes.empty()) {
 | 
			
		||||
    for(auto const& node : floating_nodes)
 | 
			
		||||
      sum += leafNodesInWorkspace(node);
 | 
			
		||||
  }
 | 
			
		||||
  return sum;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -292,8 +292,7 @@ Task::Task(const waybar::Bar &bar, const Json::Value &config, Taskbar *tbar,
 | 
			
		||||
        struct zwlr_foreign_toplevel_handle_v1 *tl_handle, struct wl_seat *seat) :
 | 
			
		||||
    bar_{bar}, config_{config}, tbar_{tbar}, handle_{tl_handle}, seat_{seat},
 | 
			
		||||
    id_{global_id++},
 | 
			
		||||
    content_{bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0},
 | 
			
		||||
    button_visible_{false}, ignored_{false}
 | 
			
		||||
    content_{bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0}
 | 
			
		||||
{
 | 
			
		||||
    zwlr_foreign_toplevel_handle_v1_add_listener(handle_, &toplevel_handle_impl, this);
 | 
			
		||||
 | 
			
		||||
@@ -395,13 +394,12 @@ std::string Task::state_string(bool shortened) const
 | 
			
		||||
void Task::handle_title(const char *title)
 | 
			
		||||
{
 | 
			
		||||
    title_ = title;
 | 
			
		||||
    hide_if_ignored();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Task::handle_app_id(const char *app_id)
 | 
			
		||||
void Task::hide_if_ignored()
 | 
			
		||||
{
 | 
			
		||||
    app_id_ = app_id;
 | 
			
		||||
 | 
			
		||||
    if (tbar_->ignore_list().count(app_id)) {
 | 
			
		||||
    if (tbar_->ignore_list().count(app_id_) || tbar_->ignore_list().count(title_)) {
 | 
			
		||||
        ignored_ = true;
 | 
			
		||||
        if (button_visible_) {
 | 
			
		||||
          auto output = gdk_wayland_monitor_get_wl_output(bar_.output->monitor->gobj());
 | 
			
		||||
@@ -415,6 +413,12 @@ void Task::handle_app_id(const char *app_id)
 | 
			
		||||
          handle_output_enter(output);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Task::handle_app_id(const char *app_id)
 | 
			
		||||
{
 | 
			
		||||
    app_id_ = app_id;
 | 
			
		||||
    hide_if_ignored();
 | 
			
		||||
 | 
			
		||||
	auto ids_replace_map = tbar_->app_ids_replace_map();
 | 
			
		||||
	if (ids_replace_map.count(app_id_)) {
 | 
			
		||||
@@ -451,13 +455,13 @@ void Task::handle_app_id(const char *app_id)
 | 
			
		||||
 | 
			
		||||
void Task::handle_output_enter(struct wl_output *output)
 | 
			
		||||
{
 | 
			
		||||
    spdlog::debug("{} entered output {}", repr(), (void*)output);
 | 
			
		||||
 | 
			
		||||
    if (ignored_) {
 | 
			
		||||
      spdlog::debug("{} is ignored", repr());
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    spdlog::debug("{} entered output {}", repr(), (void*)output);
 | 
			
		||||
 | 
			
		||||
    if (!button_visible_ && (tbar_->all_outputs() || tbar_->show_output(output))) {
 | 
			
		||||
        /* The task entered the output of the current bar make the button visible */
 | 
			
		||||
        tbar_->add_button(button_);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										472
									
								
								src/modules/wlr/workspace_manager.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										472
									
								
								src/modules/wlr/workspace_manager.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,472 @@
 | 
			
		||||
#include "modules/wlr/workspace_manager.hpp"
 | 
			
		||||
 | 
			
		||||
#include <gdk/gdkwayland.h>
 | 
			
		||||
#include <gtkmm.h>
 | 
			
		||||
#include <spdlog/spdlog.h>
 | 
			
		||||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <iterator>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include "gtkmm/widget.h"
 | 
			
		||||
#include "modules/wlr/workspace_manager_binding.hpp"
 | 
			
		||||
 | 
			
		||||
namespace waybar::modules::wlr {
 | 
			
		||||
 | 
			
		||||
uint32_t                           WorkspaceGroup::workspace_global_id = 0;
 | 
			
		||||
uint32_t                           WorkspaceManager::group_global_id = 0;
 | 
			
		||||
std::map<std::string, std::string> Workspace::icons_map_;
 | 
			
		||||
 | 
			
		||||
WorkspaceManager::WorkspaceManager(const std::string &id, const waybar::Bar &bar,
 | 
			
		||||
                                   const Json::Value &config)
 | 
			
		||||
    : waybar::AModule(config, "workspaces", id, false, false),
 | 
			
		||||
      bar_(bar),
 | 
			
		||||
      box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0) {
 | 
			
		||||
  auto config_sort_by_name = config_["sort-by-name"];
 | 
			
		||||
  if (config_sort_by_name.isBool()) {
 | 
			
		||||
    sort_by_name_ = config_sort_by_name.asBool();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  auto config_sort_by_coordinates = config_["sort-by-coordinates"];
 | 
			
		||||
  if (config_sort_by_coordinates.isBool()) {
 | 
			
		||||
    sort_by_coordinates_ = config_sort_by_coordinates.asBool();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  auto config_all_outputs = config_["all-outputs"];
 | 
			
		||||
  if (config_all_outputs.isBool()) {
 | 
			
		||||
    all_outputs_ = config_all_outputs.asBool();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  auto config_active_only = config_["active-only"];
 | 
			
		||||
  if (config_active_only.isBool()) {
 | 
			
		||||
    active_only_ = config_active_only.asBool();
 | 
			
		||||
    creation_delayed_ = active_only_;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  box_.set_name("workspaces");
 | 
			
		||||
  if (!id.empty()) {
 | 
			
		||||
    box_.get_style_context()->add_class(id);
 | 
			
		||||
  }
 | 
			
		||||
  event_box_.add(box_);
 | 
			
		||||
 | 
			
		||||
  add_registry_listener(this);
 | 
			
		||||
  if (!workspace_manager_) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto WorkspaceManager::workspace_comparator() const
 | 
			
		||||
    -> std::function<bool(std::unique_ptr<Workspace> &, std::unique_ptr<Workspace> &)> {
 | 
			
		||||
  return [=](std::unique_ptr<Workspace> &lhs, std::unique_ptr<Workspace> &rhs) {
 | 
			
		||||
    auto is_name_less = lhs->get_name() < rhs->get_name();
 | 
			
		||||
    auto is_name_eq = lhs->get_name() == rhs->get_name();
 | 
			
		||||
    auto is_coords_less = lhs->get_coords() < rhs->get_coords();
 | 
			
		||||
    if (sort_by_name_) {
 | 
			
		||||
      if (sort_by_coordinates_) {
 | 
			
		||||
        return is_name_eq ? is_coords_less : is_name_less;
 | 
			
		||||
      } else {
 | 
			
		||||
        return is_name_less;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (sort_by_coordinates_) {
 | 
			
		||||
      return is_coords_less;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return lhs->id() < rhs->id();
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto WorkspaceManager::sort_workspaces() -> void {
 | 
			
		||||
  std::vector<std::reference_wrapper<std::unique_ptr<Workspace>>> all_workspaces;
 | 
			
		||||
  for (auto &group : groups_) {
 | 
			
		||||
    auto &group_workspaces = group->workspaces();
 | 
			
		||||
    all_workspaces.reserve(all_workspaces.size() +
 | 
			
		||||
                           std::distance(group_workspaces.begin(), group_workspaces.end()));
 | 
			
		||||
    if (!active_only()) {
 | 
			
		||||
      all_workspaces.insert(all_workspaces.end(), group_workspaces.begin(), group_workspaces.end());
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (auto &workspace : group_workspaces) {
 | 
			
		||||
      if (!workspace->is_active()) {
 | 
			
		||||
        continue;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      all_workspaces.push_back(workspace);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  std::sort(all_workspaces.begin(), all_workspaces.end(), workspace_comparator());
 | 
			
		||||
  for (size_t i = 0; i < all_workspaces.size(); ++i) {
 | 
			
		||||
    box_.reorder_child(all_workspaces[i].get()->get_button_ref(), i);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto WorkspaceManager::register_manager(wl_registry *registry, uint32_t name, uint32_t version)
 | 
			
		||||
    -> void {
 | 
			
		||||
  if (workspace_manager_) {
 | 
			
		||||
    spdlog::warn("Register workspace manager again although already registered!");
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (version != 1) {
 | 
			
		||||
    spdlog::warn("Using different workspace manager protocol version: {}", version);
 | 
			
		||||
  }
 | 
			
		||||
  workspace_manager_ = workspace_manager_bind(registry, name, version, this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto WorkspaceManager::handle_workspace_group_create(
 | 
			
		||||
    zext_workspace_group_handle_v1 *workspace_group_handle) -> void {
 | 
			
		||||
  auto new_id = ++group_global_id;
 | 
			
		||||
  groups_.push_back(
 | 
			
		||||
      std::make_unique<WorkspaceGroup>(bar_, box_, config_, *this, workspace_group_handle, new_id));
 | 
			
		||||
  spdlog::debug("Workspace group {} created", new_id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto WorkspaceManager::handle_finished() -> void {
 | 
			
		||||
  zext_workspace_manager_v1_destroy(workspace_manager_);
 | 
			
		||||
  workspace_manager_ = nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto WorkspaceManager::handle_done() -> void {
 | 
			
		||||
  for (auto &group : groups_) {
 | 
			
		||||
    group->handle_done();
 | 
			
		||||
  }
 | 
			
		||||
  dp.emit();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto WorkspaceManager::update() -> void {
 | 
			
		||||
  for (auto &group : groups_) {
 | 
			
		||||
    group->update();
 | 
			
		||||
  }
 | 
			
		||||
  if (creation_delayed()) {
 | 
			
		||||
    creation_delayed_ = false;
 | 
			
		||||
    sort_workspaces();
 | 
			
		||||
  }
 | 
			
		||||
  AModule::update();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
WorkspaceManager::~WorkspaceManager() {
 | 
			
		||||
  if (!workspace_manager_) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  zext_workspace_manager_v1_destroy(workspace_manager_);
 | 
			
		||||
  workspace_manager_ = nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto WorkspaceManager::remove_workspace_group(uint32_t id) -> void {
 | 
			
		||||
  auto it = std::find_if(groups_.begin(),
 | 
			
		||||
                         groups_.end(),
 | 
			
		||||
                         [id](const std::unique_ptr<WorkspaceGroup> &g) { return g->id() == id; });
 | 
			
		||||
 | 
			
		||||
  if (it == groups_.end()) {
 | 
			
		||||
    spdlog::warn("Can't find group with id {}", id);
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  groups_.erase(it);
 | 
			
		||||
}
 | 
			
		||||
auto WorkspaceManager::commit() -> void { zext_workspace_manager_v1_commit(workspace_manager_); }
 | 
			
		||||
 | 
			
		||||
WorkspaceGroup::WorkspaceGroup(const Bar &bar, Gtk::Box &box, const Json::Value &config,
 | 
			
		||||
                               WorkspaceManager               &manager,
 | 
			
		||||
                               zext_workspace_group_handle_v1 *workspace_group_handle, uint32_t id)
 | 
			
		||||
    : bar_(bar),
 | 
			
		||||
      box_(box),
 | 
			
		||||
      config_(config),
 | 
			
		||||
      workspace_manager_(manager),
 | 
			
		||||
      workspace_group_handle_(workspace_group_handle),
 | 
			
		||||
      id_(id) {
 | 
			
		||||
  add_workspace_group_listener(workspace_group_handle, this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto WorkspaceGroup::active_only() const -> bool { return workspace_manager_.active_only(); }
 | 
			
		||||
auto WorkspaceGroup::creation_delayed() const -> bool {
 | 
			
		||||
  return workspace_manager_.creation_delayed();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto WorkspaceGroup::add_button(Gtk::Button &button) -> void {
 | 
			
		||||
  box_.pack_start(button, false, false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
WorkspaceGroup::~WorkspaceGroup() {
 | 
			
		||||
  if (!workspace_group_handle_) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  zext_workspace_group_handle_v1_destroy(workspace_group_handle_);
 | 
			
		||||
  workspace_group_handle_ = nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto WorkspaceGroup::handle_workspace_create(zext_workspace_handle_v1 *workspace) -> void {
 | 
			
		||||
  auto new_id = ++workspace_global_id;
 | 
			
		||||
  workspaces_.push_back(std::make_unique<Workspace>(bar_, config_, *this, workspace, new_id));
 | 
			
		||||
  spdlog::debug("Workspace {} created", new_id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto WorkspaceGroup::handle_remove() -> void {
 | 
			
		||||
  zext_workspace_group_handle_v1_destroy(workspace_group_handle_);
 | 
			
		||||
  workspace_group_handle_ = nullptr;
 | 
			
		||||
  workspace_manager_.remove_workspace_group(id_);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto WorkspaceGroup::handle_output_enter(wl_output *output) -> void {
 | 
			
		||||
  spdlog::debug("Output {} assigned to {} group", (void *)output, id_);
 | 
			
		||||
  output_ = output;
 | 
			
		||||
 | 
			
		||||
  if (!is_visible() || workspace_manager_.creation_delayed()) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  for (auto &workspace : workspaces_) {
 | 
			
		||||
    add_button(workspace->get_button_ref());
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto WorkspaceGroup::is_visible() const -> bool {
 | 
			
		||||
  return output_ != nullptr &&
 | 
			
		||||
         (workspace_manager_.all_outputs() ||
 | 
			
		||||
          output_ == gdk_wayland_monitor_get_wl_output(bar_.output->monitor->gobj()));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto WorkspaceGroup::handle_output_leave() -> void {
 | 
			
		||||
  spdlog::debug("Output {} remove from {} group", (void *)output_, id_);
 | 
			
		||||
  output_ = nullptr;
 | 
			
		||||
 | 
			
		||||
  if (output_ != gdk_wayland_monitor_get_wl_output(bar_.output->monitor->gobj())) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  for (auto &workspace : workspaces_) {
 | 
			
		||||
    remove_button(workspace->get_button_ref());
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto WorkspaceGroup::update() -> void {
 | 
			
		||||
  for (auto &workspace : workspaces_) {
 | 
			
		||||
    if (workspace_manager_.creation_delayed()) {
 | 
			
		||||
      add_button(workspace->get_button_ref());
 | 
			
		||||
      if (is_visible() && (workspace->is_active() || workspace->is_urgent())) {
 | 
			
		||||
        workspace->show();
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    workspace->update();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto WorkspaceGroup::remove_workspace(uint32_t id) -> void {
 | 
			
		||||
  auto it = std::find_if(workspaces_.begin(),
 | 
			
		||||
                         workspaces_.end(),
 | 
			
		||||
                         [id](const std::unique_ptr<Workspace> &w) { return w->id() == id; });
 | 
			
		||||
 | 
			
		||||
  if (it == workspaces_.end()) {
 | 
			
		||||
    spdlog::warn("Can't find workspace with id {}", id);
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  workspaces_.erase(it);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto WorkspaceGroup::handle_done() -> void {
 | 
			
		||||
  need_to_sort = false;
 | 
			
		||||
  if (!is_visible()) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  for (auto &workspace : workspaces_) {
 | 
			
		||||
    workspace->handle_done();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (creation_delayed()) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!workspace_manager_.all_outputs()) {
 | 
			
		||||
    sort_workspaces();
 | 
			
		||||
  } else {
 | 
			
		||||
    workspace_manager_.sort_workspaces();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto WorkspaceGroup::commit() -> void { workspace_manager_.commit(); }
 | 
			
		||||
 | 
			
		||||
auto WorkspaceGroup::sort_workspaces() -> void {
 | 
			
		||||
  std::sort(workspaces_.begin(), workspaces_.end(), workspace_manager_.workspace_comparator());
 | 
			
		||||
  for (size_t i = 0; i < workspaces_.size(); ++i) {
 | 
			
		||||
    box_.reorder_child(workspaces_[i]->get_button_ref(), i);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto WorkspaceGroup::remove_button(Gtk::Button &button) -> void { box_.remove(button); }
 | 
			
		||||
 | 
			
		||||
Workspace::Workspace(const Bar &bar, const Json::Value &config, WorkspaceGroup &workspace_group,
 | 
			
		||||
                     zext_workspace_handle_v1 *workspace, uint32_t id)
 | 
			
		||||
    : bar_(bar),
 | 
			
		||||
      config_(config),
 | 
			
		||||
      workspace_group_(workspace_group),
 | 
			
		||||
      workspace_handle_(workspace),
 | 
			
		||||
      id_(id) {
 | 
			
		||||
  add_workspace_listener(workspace, this);
 | 
			
		||||
 | 
			
		||||
  auto config_format = config["format"];
 | 
			
		||||
 | 
			
		||||
  format_ = config_format.isString() ? config_format.asString() : "{name}";
 | 
			
		||||
  with_icon_ = format_.find("{icon}") != std::string::npos;
 | 
			
		||||
 | 
			
		||||
  if (with_icon_ && icons_map_.empty()) {
 | 
			
		||||
    auto format_icons = config["format-icons"];
 | 
			
		||||
    for (auto &name : format_icons.getMemberNames()) {
 | 
			
		||||
      icons_map_.emplace(name, format_icons[name].asString());
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* Handle click events if configured */
 | 
			
		||||
  if (config_["on-click"].isString() || config_["on-click-middle"].isString() ||
 | 
			
		||||
      config_["on-click-right"].isString()) {
 | 
			
		||||
    button_.add_events(Gdk::BUTTON_PRESS_MASK);
 | 
			
		||||
    button_.signal_button_press_event().connect(sigc::mem_fun(*this, &Workspace::handle_clicked),
 | 
			
		||||
                                                false);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  button_.set_relief(Gtk::RELIEF_NONE);
 | 
			
		||||
  content_.set_center_widget(label_);
 | 
			
		||||
  button_.add(content_);
 | 
			
		||||
 | 
			
		||||
  if (!workspace_group.is_visible()) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  workspace_group.add_button(button_);
 | 
			
		||||
  button_.show_all();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Workspace::~Workspace() {
 | 
			
		||||
  workspace_group_.remove_button(button_);
 | 
			
		||||
  if (!workspace_handle_) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  zext_workspace_handle_v1_destroy(workspace_handle_);
 | 
			
		||||
  workspace_handle_ = nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto Workspace::update() -> void {
 | 
			
		||||
  label_.set_markup(fmt::format(
 | 
			
		||||
      format_, fmt::arg("name", name_), fmt::arg("icon", with_icon_ ? get_icon() : "")));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto Workspace::handle_state(const std::vector<uint32_t> &state) -> void {
 | 
			
		||||
  state_ = 0;
 | 
			
		||||
  for (auto state_entry : state) {
 | 
			
		||||
    switch (state_entry) {
 | 
			
		||||
      case ZEXT_WORKSPACE_HANDLE_V1_STATE_ACTIVE:
 | 
			
		||||
        state_ |= (uint32_t)State::ACTIVE;
 | 
			
		||||
        break;
 | 
			
		||||
      case ZEXT_WORKSPACE_HANDLE_V1_STATE_URGENT:
 | 
			
		||||
        state_ |= (uint32_t)State::URGENT;
 | 
			
		||||
        break;
 | 
			
		||||
      case ZEXT_WORKSPACE_HANDLE_V1_STATE_HIDDEN:
 | 
			
		||||
        state_ |= (uint32_t)State::HIDDEN;
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto Workspace::handle_remove() -> void {
 | 
			
		||||
  zext_workspace_handle_v1_destroy(workspace_handle_);
 | 
			
		||||
  workspace_handle_ = nullptr;
 | 
			
		||||
  workspace_group_.remove_workspace(id_);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto add_or_remove_class(Glib::RefPtr<Gtk::StyleContext> context, bool condition,
 | 
			
		||||
                         const std::string &class_name) {
 | 
			
		||||
  if (condition) {
 | 
			
		||||
    context->add_class(class_name);
 | 
			
		||||
  } else {
 | 
			
		||||
    context->remove_class(class_name);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto Workspace::handle_done() -> void {
 | 
			
		||||
  spdlog::debug("Workspace {} changed to state {}", id_, state_);
 | 
			
		||||
  auto style_context = button_.get_style_context();
 | 
			
		||||
  add_or_remove_class(style_context, is_active(), "active");
 | 
			
		||||
  add_or_remove_class(style_context, is_urgent(), "urgent");
 | 
			
		||||
  add_or_remove_class(style_context, is_hidden(), "hidden");
 | 
			
		||||
 | 
			
		||||
  if (workspace_group_.creation_delayed()) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (workspace_group_.active_only() && (is_active() || is_urgent())) {
 | 
			
		||||
    button_.show_all();
 | 
			
		||||
  } else if (workspace_group_.active_only() && !(is_active() || is_urgent())) {
 | 
			
		||||
    button_.hide();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto Workspace::get_icon() -> std::string {
 | 
			
		||||
  if (is_active()) {
 | 
			
		||||
    auto active_icon_it = icons_map_.find("active");
 | 
			
		||||
    if (active_icon_it != icons_map_.end()) {
 | 
			
		||||
      return active_icon_it->second;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  auto named_icon_it = icons_map_.find(name_);
 | 
			
		||||
  if (named_icon_it != icons_map_.end()) {
 | 
			
		||||
    return named_icon_it->second;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  auto default_icon_it = icons_map_.find("default");
 | 
			
		||||
  if (default_icon_it != icons_map_.end()) {
 | 
			
		||||
    return default_icon_it->second;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return name_;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto Workspace::handle_clicked(GdkEventButton *bt) -> bool {
 | 
			
		||||
  std::string action;
 | 
			
		||||
  if (config_["on-click"].isString() && bt->button == 1) {
 | 
			
		||||
    action = config_["on-click"].asString();
 | 
			
		||||
  } else if (config_["on-click-middle"].isString() && bt->button == 2) {
 | 
			
		||||
    action = config_["on-click-middle"].asString();
 | 
			
		||||
  } else if (config_["on-click-right"].isString() && bt->button == 3) {
 | 
			
		||||
    action = config_["on-click-right"].asString();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (action.empty())
 | 
			
		||||
    return true;
 | 
			
		||||
  else if (action == "activate") {
 | 
			
		||||
    zext_workspace_handle_v1_activate(workspace_handle_);
 | 
			
		||||
  } else if (action == "close") {
 | 
			
		||||
    zext_workspace_handle_v1_remove(workspace_handle_);
 | 
			
		||||
  } else {
 | 
			
		||||
    spdlog::warn("Unknown action {}", action);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  workspace_group_.commit();
 | 
			
		||||
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto Workspace::show() -> void { button_.show_all(); }
 | 
			
		||||
auto Workspace::hide() -> void { button_.hide(); }
 | 
			
		||||
 | 
			
		||||
auto Workspace::handle_name(const std::string &name) -> void {
 | 
			
		||||
  if (name_ != name) {
 | 
			
		||||
    workspace_group_.set_need_to_sort();
 | 
			
		||||
  }
 | 
			
		||||
  name_ = name;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto Workspace::handle_coordinates(const std::vector<uint32_t> &coordinates) -> void {
 | 
			
		||||
  if (coordinates_ != coordinates) {
 | 
			
		||||
    workspace_group_.set_need_to_sort();
 | 
			
		||||
  }
 | 
			
		||||
  coordinates_ = coordinates;
 | 
			
		||||
}
 | 
			
		||||
}  // namespace waybar::modules::wlr
 | 
			
		||||
							
								
								
									
										135
									
								
								src/modules/wlr/workspace_manager_binding.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										135
									
								
								src/modules/wlr/workspace_manager_binding.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,135 @@
 | 
			
		||||
#include "modules/wlr/workspace_manager_binding.hpp"
 | 
			
		||||
 | 
			
		||||
#include <spdlog/spdlog.h>
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
 | 
			
		||||
#include "client.hpp"
 | 
			
		||||
#include "modules/wlr/workspace_manager.hpp"
 | 
			
		||||
 | 
			
		||||
namespace waybar::modules::wlr {
 | 
			
		||||
 | 
			
		||||
static void handle_global(void *data, wl_registry *registry, uint32_t name, const char *interface,
 | 
			
		||||
                          uint32_t version) {
 | 
			
		||||
  if (std::strcmp(interface, zext_workspace_manager_v1_interface.name) == 0) {
 | 
			
		||||
    static_cast<WorkspaceManager *>(data)->register_manager(registry, name, version);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void handle_global_remove(void *data, wl_registry *registry, uint32_t name) {
 | 
			
		||||
  /* Nothing to do here */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const wl_registry_listener registry_listener_impl = {.global = handle_global,
 | 
			
		||||
                                                            .global_remove = handle_global_remove};
 | 
			
		||||
 | 
			
		||||
void add_registry_listener(void *data) {
 | 
			
		||||
  wl_display * display = Client::inst()->wl_display;
 | 
			
		||||
  wl_registry *registry = wl_display_get_registry(display);
 | 
			
		||||
 | 
			
		||||
  wl_registry_add_listener(registry, ®istry_listener_impl, data);
 | 
			
		||||
  wl_display_roundtrip(display);
 | 
			
		||||
  wl_display_roundtrip(display);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void workspace_manager_handle_workspace_group(
 | 
			
		||||
    void *data, zext_workspace_manager_v1 *_, zext_workspace_group_handle_v1 *workspace_group) {
 | 
			
		||||
  static_cast<WorkspaceManager *>(data)->handle_workspace_group_create(workspace_group);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void workspace_manager_handle_done(void *data, zext_workspace_manager_v1 *_) {
 | 
			
		||||
  static_cast<WorkspaceManager *>(data)->handle_done();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void workspace_manager_handle_finished(void *data, zext_workspace_manager_v1 *_) {
 | 
			
		||||
  static_cast<WorkspaceManager *>(data)->handle_finished();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const zext_workspace_manager_v1_listener workspace_manager_impl = {
 | 
			
		||||
    .workspace_group = workspace_manager_handle_workspace_group,
 | 
			
		||||
    .done = workspace_manager_handle_done,
 | 
			
		||||
    .finished = workspace_manager_handle_finished,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
zext_workspace_manager_v1 *workspace_manager_bind(wl_registry *registry, uint32_t name,
 | 
			
		||||
                                                  uint32_t version, void *data) {
 | 
			
		||||
  auto *workspace_manager = static_cast<zext_workspace_manager_v1 *>(
 | 
			
		||||
      wl_registry_bind(registry, name, &zext_workspace_manager_v1_interface, version));
 | 
			
		||||
 | 
			
		||||
  if (workspace_manager)
 | 
			
		||||
    zext_workspace_manager_v1_add_listener(workspace_manager, &workspace_manager_impl, data);
 | 
			
		||||
  else
 | 
			
		||||
    spdlog::error("Failed to register manager");
 | 
			
		||||
 | 
			
		||||
  return workspace_manager;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void workspace_group_handle_output_enter(void *data, zext_workspace_group_handle_v1 *_,
 | 
			
		||||
                                                wl_output *output) {
 | 
			
		||||
  static_cast<WorkspaceGroup *>(data)->handle_output_enter(output);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void workspace_group_handle_output_leave(void *data, zext_workspace_group_handle_v1 *_,
 | 
			
		||||
                                                wl_output *output) {
 | 
			
		||||
  static_cast<WorkspaceGroup *>(data)->handle_output_leave();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void workspace_group_handle_workspace(void *data, zext_workspace_group_handle_v1 *_,
 | 
			
		||||
                                             zext_workspace_handle_v1 *workspace) {
 | 
			
		||||
  static_cast<WorkspaceGroup *>(data)->handle_workspace_create(workspace);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void workspace_group_handle_remove(void *data, zext_workspace_group_handle_v1 *_) {
 | 
			
		||||
  static_cast<WorkspaceGroup *>(data)->handle_remove();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const zext_workspace_group_handle_v1_listener workspace_group_impl = {
 | 
			
		||||
    .output_enter = workspace_group_handle_output_enter,
 | 
			
		||||
    .output_leave = workspace_group_handle_output_leave,
 | 
			
		||||
    .workspace = workspace_group_handle_workspace,
 | 
			
		||||
    .remove = workspace_group_handle_remove};
 | 
			
		||||
 | 
			
		||||
void add_workspace_group_listener(zext_workspace_group_handle_v1 *workspace_group_handle,
 | 
			
		||||
                                  void *                          data) {
 | 
			
		||||
  zext_workspace_group_handle_v1_add_listener(workspace_group_handle, &workspace_group_impl, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void workspace_handle_name(void *data, struct zext_workspace_handle_v1 *_, const char *name) {
 | 
			
		||||
  static_cast<Workspace *>(data)->handle_name(name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void workspace_handle_coordinates(void *data, struct zext_workspace_handle_v1 *_,
 | 
			
		||||
                                  struct wl_array *coordinates) {
 | 
			
		||||
  std::vector<uint32_t> coords_vec;
 | 
			
		||||
  auto                  coords = static_cast<uint32_t *>(coordinates->data);
 | 
			
		||||
  for (size_t i = 0; i < coordinates->size / sizeof(uint32_t); ++i) {
 | 
			
		||||
    coords_vec.push_back(coords[i]);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static_cast<Workspace *>(data)->handle_coordinates(coords_vec);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void workspace_handle_state(void *data, struct zext_workspace_handle_v1 *workspace_handle,
 | 
			
		||||
                            struct wl_array *state) {
 | 
			
		||||
  std::vector<uint32_t> state_vec;
 | 
			
		||||
  auto                  states = static_cast<uint32_t *>(state->data);
 | 
			
		||||
  for (size_t i = 0; i < state->size / sizeof(uint32_t); ++i) {
 | 
			
		||||
    state_vec.push_back(states[i]);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static_cast<Workspace *>(data)->handle_state(state_vec);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void workspace_handle_remove(void *data, struct zext_workspace_handle_v1 *_) {
 | 
			
		||||
  static_cast<Workspace *>(data)->handle_remove();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const zext_workspace_handle_v1_listener workspace_impl = {
 | 
			
		||||
    .name = workspace_handle_name,
 | 
			
		||||
    .coordinates = workspace_handle_coordinates,
 | 
			
		||||
    .state = workspace_handle_state,
 | 
			
		||||
    .remove = workspace_handle_remove};
 | 
			
		||||
 | 
			
		||||
void add_workspace_listener(zext_workspace_handle_v1 *workspace_handle, void *data) {
 | 
			
		||||
  zext_workspace_handle_v1_add_listener(workspace_handle, &workspace_impl, data);
 | 
			
		||||
}
 | 
			
		||||
}  // namespace waybar::modules::wlr
 | 
			
		||||
		Reference in New Issue
	
	Block a user