Merge branch 'master' into river-tags-monitor-focus
This commit is contained in:
@@ -14,18 +14,17 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Test in FreeBSD VM
|
||||
uses: cross-platform-actions/action@v0.23.0
|
||||
uses: cross-platform-actions/action@v0.25.0
|
||||
timeout-minutes: 180
|
||||
env:
|
||||
CPPFLAGS: '-isystem/usr/local/include'
|
||||
LDFLAGS: '-L/usr/local/lib'
|
||||
with:
|
||||
operating_system: freebsd
|
||||
version: "13.2"
|
||||
version: "14.1"
|
||||
environment_variables: CPPFLAGS LDFLAGS
|
||||
sync_files: runner-to-vm
|
||||
run: |
|
||||
sudo sed -i '' 's/quarterly/latest/' /etc/pkg/FreeBSD.conf
|
||||
sudo pkg install -y git # subprojects/date
|
||||
sudo pkg install -y catch evdev-proto gtk-layer-shell gtkmm30 jsoncpp \
|
||||
libdbusmenu libevdev libfmt libmpdclient libudev-devd meson \
|
||||
|
||||
Generated
+3
-3
@@ -18,11 +18,11 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1719506693,
|
||||
"narHash": "sha256-C8e9S7RzshSdHB7L+v9I51af1gDM5unhJ2xO1ywxNH8=",
|
||||
"lastModified": 1722185531,
|
||||
"narHash": "sha256-veKR07psFoJjINLC8RK4DiLniGGMgF3QMlS4tb74S6k=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "b2852eb9365c6de48ffb0dc2c9562591f652242a",
|
||||
"rev": "52ec9ac3b12395ad677e8b62106f0b98c1f8569d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
+2
-2
@@ -54,7 +54,7 @@ class BarIpcClient;
|
||||
}
|
||||
#endif // HAVE_SWAY
|
||||
|
||||
class Bar {
|
||||
class Bar : public sigc::trackable {
|
||||
public:
|
||||
using bar_mode_map = std::map<std::string, struct bar_mode>;
|
||||
static const bar_mode_map PRESET_MODES;
|
||||
@@ -66,7 +66,7 @@ class Bar {
|
||||
~Bar();
|
||||
|
||||
void setMode(const std::string &mode);
|
||||
void setVisible(bool visible);
|
||||
void setVisible(bool value);
|
||||
void toggle();
|
||||
void handleSignal(int);
|
||||
|
||||
|
||||
@@ -20,6 +20,9 @@ class Config {
|
||||
static std::optional<std::string> findConfigPath(
|
||||
const std::vector<std::string> &names, const std::vector<std::string> &dirs = CONFIG_DIRS);
|
||||
|
||||
static std::optional<std::string> tryExpandPath(const std::string &base,
|
||||
const std::string &filename);
|
||||
|
||||
Config() = default;
|
||||
|
||||
void load(const std::string &config);
|
||||
|
||||
+5
-1
@@ -12,7 +12,7 @@ namespace waybar {
|
||||
class Group : public AModule {
|
||||
public:
|
||||
Group(const std::string &, const std::string &, const Json::Value &, bool);
|
||||
virtual ~Group() = default;
|
||||
~Group() override = default;
|
||||
auto update() -> void override;
|
||||
operator Gtk::Widget &() override;
|
||||
|
||||
@@ -25,9 +25,13 @@ class Group : public AModule {
|
||||
Gtk::Revealer revealer;
|
||||
bool is_first_widget = true;
|
||||
bool is_drawer = false;
|
||||
bool click_to_reveal = false;
|
||||
std::string add_class_to_drawer_children;
|
||||
bool handleMouseEnter(GdkEventCrossing *const &ev) override;
|
||||
bool handleMouseLeave(GdkEventCrossing *const &ev) override;
|
||||
bool handleToggle(GdkEventButton *const &ev) override;
|
||||
void show_group();
|
||||
void hide_group();
|
||||
};
|
||||
|
||||
} // namespace waybar
|
||||
|
||||
@@ -40,6 +40,7 @@ class Network : public ALabel {
|
||||
void parseEssid(struct nlattr**);
|
||||
void parseSignal(struct nlattr**);
|
||||
void parseFreq(struct nlattr**);
|
||||
void parseBssid(struct nlattr**);
|
||||
bool associatedOrJoined(struct nlattr**);
|
||||
bool checkInterface(std::string name);
|
||||
auto getInfo() -> void;
|
||||
@@ -69,6 +70,7 @@ class Network : public ALabel {
|
||||
|
||||
std::string state_;
|
||||
std::string essid_;
|
||||
std::string bssid_;
|
||||
bool carrier_;
|
||||
std::string ifname_;
|
||||
std::string ipaddr_;
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace waybar::modules::sway {
|
||||
class Workspaces : public AModule, public sigc::trackable {
|
||||
public:
|
||||
Workspaces(const std::string&, const waybar::Bar&, const Json::Value&);
|
||||
virtual ~Workspaces() = default;
|
||||
~Workspaces() override = default;
|
||||
auto update() -> void override;
|
||||
|
||||
private:
|
||||
@@ -38,10 +38,10 @@ class Workspaces : public AModule, public sigc::trackable {
|
||||
Gtk::Button& addButton(const Json::Value&);
|
||||
void onButtonReady(const Json::Value&, Gtk::Button&);
|
||||
std::string getIcon(const std::string&, const Json::Value&);
|
||||
const std::string getCycleWorkspace(std::vector<Json::Value>::iterator, bool prev) const;
|
||||
std::string getCycleWorkspace(std::vector<Json::Value>::iterator, bool prev) const;
|
||||
uint16_t getWorkspaceIndex(const std::string& name) const;
|
||||
std::string trimWorkspaceName(std::string);
|
||||
bool handleScroll(GdkEventScroll*) override;
|
||||
static std::string trimWorkspaceName(std::string);
|
||||
bool handleScroll(GdkEventScroll* /*unused*/) override;
|
||||
|
||||
const Bar& bar_;
|
||||
std::vector<Json::Value> workspaces_;
|
||||
|
||||
@@ -24,6 +24,10 @@
|
||||
|
||||
namespace waybar::modules::wlr {
|
||||
|
||||
struct widget_geometry {
|
||||
int x, y, w, h;
|
||||
};
|
||||
|
||||
class Taskbar;
|
||||
|
||||
class Task {
|
||||
@@ -42,6 +46,7 @@ class Task {
|
||||
};
|
||||
// made public so TaskBar can reorder based on configuration.
|
||||
Gtk::Button button;
|
||||
struct widget_geometry minimize_hint;
|
||||
|
||||
private:
|
||||
static uint32_t global_id;
|
||||
@@ -82,6 +87,8 @@ class Task {
|
||||
private:
|
||||
std::string repr() const;
|
||||
std::string state_string(bool = false) const;
|
||||
void set_minimize_hint();
|
||||
void on_button_size_allocated(Gtk::Allocation &alloc);
|
||||
void set_app_info_from_app_id_list(const std::string &app_id_list);
|
||||
bool image_load_icon(Gtk::Image &image, const Glib::RefPtr<Gtk::IconTheme> &icon_theme,
|
||||
Glib::RefPtr<Gio::DesktopAppInfo> app_info, int size);
|
||||
|
||||
@@ -92,7 +92,7 @@ struct formatter<pow_format> {
|
||||
template <>
|
||||
struct formatter<Glib::ustring> : formatter<std::string> {
|
||||
template <typename FormatContext>
|
||||
auto format(const Glib::ustring& value, FormatContext& ctx) {
|
||||
auto format(const Glib::ustring& value, FormatContext& ctx) const {
|
||||
return formatter<std::string>::format(static_cast<std::string>(value), ctx);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -156,6 +156,8 @@ Addressed by *network*
|
||||
|
||||
*{essid}*: Name (SSID) of the wireless network.
|
||||
|
||||
*{bssid}*: MAC address (BSSID) of the wireless access point.
|
||||
|
||||
*{signalStrength}*: Signal strength of the wireless network.
|
||||
|
||||
*{signaldBm}*: Signal strength of the wireless network in dBm.
|
||||
|
||||
@@ -39,6 +39,40 @@ You can apply special styling to any module for when the cursor hovers it.
|
||||
}
|
||||
```
|
||||
|
||||
## Setting cursor style
|
||||
|
||||
Most, if not all, module types support setting the `cursor` option. This is
|
||||
configured in your `config.jsonc`. If set to `false`, when hovering the module a
|
||||
"pointer"(as commonly known from web CSS styling `cursor: pointer`) style cursor
|
||||
will not be shown. Default behavior is to indicate an interaction event is
|
||||
available.
|
||||
|
||||
There are more cursor types to choose from by setting the `cursor` option to
|
||||
a number, see Gdk3 official docs for all possible cursor types:
|
||||
https://docs.gtk.org/gdk3/enum.CursorType.html.
|
||||
However, note that not all cursor options listed may be available on
|
||||
your system. If you attempt to use a cursor which is not available, the
|
||||
application will crash.
|
||||
|
||||
Example of disabling pointer(`Gdk::Hand2`) cursor type on a custom module:
|
||||
|
||||
```
|
||||
"custom/my-custom-module": {
|
||||
...
|
||||
"cursor": false,
|
||||
}
|
||||
```
|
||||
|
||||
Example of setting cursor type to `Gdk::Boat`(according to
|
||||
https://docs.gtk.org/gdk3/enum.CursorType.html#boat):
|
||||
|
||||
```
|
||||
"custom/my-custom-module": {
|
||||
...
|
||||
"cursor": 8,
|
||||
}
|
||||
```
|
||||
|
||||
# SEE ALSO
|
||||
|
||||
- *waybar(5)*
|
||||
|
||||
@@ -87,6 +87,7 @@ warp-on-scroll: ++
|
||||
Regex rules to map window class to an icon or preferred method of representation for a workspace's window.
|
||||
Keys are the rules, while the values are the methods of representation.
|
||||
Rules may specify `class<...>`, `title<...>`, or both in order to fine-tune the matching.
|
||||
You may assign an empty value to a rule to have it ignored from generating any representation in workspaces.
|
||||
|
||||
*window-rewrite-default*:
|
||||
typeof: string ++
|
||||
|
||||
@@ -278,6 +278,11 @@ A group may hide all but one element, showing them only on mouse hover. In order
|
||||
default: "hidden" ++
|
||||
Defines the CSS class to be applied to the hidden elements.
|
||||
|
||||
*click-to-reveal*: ++
|
||||
typeof: bool ++
|
||||
default: false ++
|
||||
Whether left click should reveal the content rather than mouse over. Note that grouped modules may still process their own on-click events.
|
||||
|
||||
*transition-left-to-right*: ++
|
||||
typeof: bool ++
|
||||
default: true ++
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
project(
|
||||
'waybar', 'cpp', 'c',
|
||||
version: '0.10.3',
|
||||
version: '0.10.4',
|
||||
license: 'MIT',
|
||||
meson_version: '>= 0.59.0',
|
||||
default_options : [
|
||||
|
||||
+11
-1
@@ -6,6 +6,8 @@
|
||||
#include <iostream>
|
||||
#include <util/command.hpp>
|
||||
|
||||
#include "config.hpp"
|
||||
|
||||
namespace waybar {
|
||||
|
||||
ALabel::ALabel(const Json::Value& config, const std::string& name, const std::string& id,
|
||||
@@ -61,6 +63,14 @@ ALabel::ALabel(const Json::Value& config, const std::string& name, const std::st
|
||||
try {
|
||||
// Check that the file exists
|
||||
std::string menuFile = config_["menu-file"].asString();
|
||||
|
||||
// there might be "~" or "$HOME" in original path, try to expand it.
|
||||
auto result = Config::tryExpandPath(menuFile, "");
|
||||
if (!result.has_value()) {
|
||||
throw std::runtime_error("Failed to expand file: " + menuFile);
|
||||
}
|
||||
|
||||
menuFile = result.value();
|
||||
// Read the menu descriptor file
|
||||
std::ifstream file(menuFile);
|
||||
if (!file.is_open()) {
|
||||
@@ -170,7 +180,7 @@ bool waybar::ALabel::handleToggle(GdkEventButton* const& e) {
|
||||
return AModule::handleToggle(e);
|
||||
}
|
||||
|
||||
void ALabel::handleGtkMenuEvent(GtkMenuItem* menuitem, gpointer data) {
|
||||
void ALabel::handleGtkMenuEvent(GtkMenuItem* /*menuitem*/, gpointer data) {
|
||||
waybar::util::command::res res = waybar::util::command::exec((char*)data, "GtkMenu");
|
||||
}
|
||||
|
||||
|
||||
+33
-3
@@ -1,9 +1,13 @@
|
||||
#include "AModule.hpp"
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#include <util/command.hpp>
|
||||
|
||||
#include "gdk/gdk.h"
|
||||
#include "gdkmm/cursor.h"
|
||||
|
||||
namespace waybar {
|
||||
|
||||
AModule::AModule(const Json::Value& config, const std::string& name, const std::string& id,
|
||||
@@ -64,6 +68,17 @@ AModule::AModule(const Json::Value& config, const std::string& name, const std::
|
||||
event_box_.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK);
|
||||
event_box_.signal_scroll_event().connect(sigc::mem_fun(*this, &AModule::handleScroll));
|
||||
}
|
||||
|
||||
// Respect user configuration of cursor
|
||||
if (config_.isMember("cursor")) {
|
||||
if (config_["cursor"].isBool() && config_["cursor"].asBool()) {
|
||||
setCursor(Gdk::HAND2);
|
||||
} else if (config_["cursor"].isInt()) {
|
||||
setCursor(Gdk::CursorType(config_["cursor"].asInt()));
|
||||
} else {
|
||||
spdlog::warn("unknown cursor option configured on module {}", name_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AModule::~AModule() {
|
||||
@@ -91,9 +106,20 @@ auto AModule::doAction(const std::string& name) -> void {
|
||||
}
|
||||
|
||||
void AModule::setCursor(Gdk::CursorType const& c) {
|
||||
auto cursor = Gdk::Cursor::create(c);
|
||||
auto gdk_window = event_box_.get_window();
|
||||
if (gdk_window) {
|
||||
auto cursor = Gdk::Cursor::create(c);
|
||||
gdk_window->set_cursor(cursor);
|
||||
} else {
|
||||
// window may not be accessible yet, in this case,
|
||||
// schedule another call for setting the cursor in 1 sec
|
||||
Glib::signal_timeout().connect_seconds(
|
||||
[this, c]() {
|
||||
setCursor(c);
|
||||
return false;
|
||||
},
|
||||
1);
|
||||
}
|
||||
}
|
||||
|
||||
bool AModule::handleMouseEnter(GdkEventCrossing* const& e) {
|
||||
@@ -101,9 +127,11 @@ bool AModule::handleMouseEnter(GdkEventCrossing* const& e) {
|
||||
module->set_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT);
|
||||
}
|
||||
|
||||
if (hasUserEvents_) {
|
||||
// Default behavior indicating event availability
|
||||
if (hasUserEvents_ && !config_.isMember("cursor")) {
|
||||
setCursor(Gdk::HAND2);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -112,9 +140,11 @@ bool AModule::handleMouseLeave(GdkEventCrossing* const& e) {
|
||||
module->unset_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT);
|
||||
}
|
||||
|
||||
if (hasUserEvents_) {
|
||||
// Default behavior indicating event availability
|
||||
if (hasUserEvents_ && !config_.isMember("cursor")) {
|
||||
setCursor(Gdk::ARROW);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -404,7 +404,8 @@ void waybar::Bar::onMap(GdkEventAny* /*unused*/) {
|
||||
setPassThrough(passthrough_);
|
||||
}
|
||||
|
||||
void waybar::Bar::setVisible(bool visible) {
|
||||
void waybar::Bar::setVisible(bool value) {
|
||||
visible = value;
|
||||
if (auto mode = config.get("mode", {}); mode.isString()) {
|
||||
setMode(visible ? config["mode"].asString() : MODE_INVISIBLE);
|
||||
} else {
|
||||
|
||||
+2
-1
@@ -123,7 +123,8 @@ void waybar::Client::handleMonitorAdded(Glib::RefPtr<Gdk::Monitor> monitor) {
|
||||
}
|
||||
|
||||
void waybar::Client::handleMonitorRemoved(Glib::RefPtr<Gdk::Monitor> monitor) {
|
||||
spdlog::debug("Output removed: {} {}", monitor->get_manufacturer().c_str(), monitor->get_model().c_str());
|
||||
spdlog::debug("Output removed: {} {}", monitor->get_manufacturer().c_str(),
|
||||
monitor->get_model().c_str());
|
||||
/* This event can be triggered from wl_display_roundtrip called by GTK or our code.
|
||||
* Defer destruction of bars for the output to the next iteration of the event loop to avoid
|
||||
* deleting objects referenced by currently executed code.
|
||||
|
||||
+2
-1
@@ -21,7 +21,8 @@ const std::vector<std::string> Config::CONFIG_DIRS = {
|
||||
|
||||
const char *Config::CONFIG_PATH_ENV = "WAYBAR_CONFIG_DIR";
|
||||
|
||||
std::optional<std::string> tryExpandPath(const std::string &base, const std::string &filename) {
|
||||
std::optional<std::string> Config::tryExpandPath(const std::string &base,
|
||||
const std::string &filename) {
|
||||
fs::path path;
|
||||
|
||||
if (!filename.empty()) {
|
||||
|
||||
+29
-4
@@ -9,7 +9,7 @@
|
||||
|
||||
namespace waybar {
|
||||
|
||||
const Gtk::RevealerTransitionType getPreferredTransitionType(bool is_vertical) {
|
||||
Gtk::RevealerTransitionType getPreferredTransitionType(bool is_vertical) {
|
||||
/* The transition direction of a drawer is not actually determined by the transition type,
|
||||
* but rather by the order of 'box' and 'revealer_box':
|
||||
* 'REVEALER_TRANSITION_TYPE_SLIDE_LEFT' and 'REVEALER_TRANSITION_TYPE_SLIDE_RIGHT'
|
||||
@@ -62,6 +62,7 @@ Group::Group(const std::string& name, const std::string& id, const Json::Value&
|
||||
const bool left_to_right = (drawer_config["transition-left-to-right"].isBool()
|
||||
? drawer_config["transition-left-to-right"].asBool()
|
||||
: true);
|
||||
click_to_reveal = drawer_config["click-to-reveal"].asBool();
|
||||
|
||||
auto transition_type = getPreferredTransitionType(vertical);
|
||||
|
||||
@@ -83,18 +84,42 @@ Group::Group(const std::string& name, const std::string& id, const Json::Value&
|
||||
event_box_.add(box);
|
||||
}
|
||||
|
||||
bool Group::handleMouseEnter(GdkEventCrossing* const& e) {
|
||||
void Group::show_group() {
|
||||
box.set_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT);
|
||||
revealer.set_reveal_child(true);
|
||||
}
|
||||
|
||||
void Group::hide_group() {
|
||||
box.unset_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT);
|
||||
revealer.set_reveal_child(false);
|
||||
}
|
||||
|
||||
bool Group::handleMouseEnter(GdkEventCrossing* const& e) {
|
||||
if (!click_to_reveal) {
|
||||
show_group();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Group::handleMouseLeave(GdkEventCrossing* const& e) {
|
||||
box.unset_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT);
|
||||
revealer.set_reveal_child(false);
|
||||
if (!click_to_reveal && e->detail != GDK_NOTIFY_INFERIOR) {
|
||||
hide_group();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Group::handleToggle(GdkEventButton* const& e) {
|
||||
if (!click_to_reveal || e->button != 1) {
|
||||
return false;
|
||||
}
|
||||
if ((box.get_state_flags() & Gtk::StateFlags::STATE_FLAG_PRELIGHT) != 0U) {
|
||||
hide_group();
|
||||
} else {
|
||||
show_group();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
auto Group::update() -> void {
|
||||
// noop
|
||||
}
|
||||
|
||||
@@ -163,15 +163,16 @@ auto waybar::modules::Clock::update() -> void {
|
||||
// std::vformat doesn't support named arguments.
|
||||
m_tlpText_ =
|
||||
std::regex_replace(m_tlpFmt_, std::regex("\\{" + kTZPlaceholder + "\\}"), tzText_);
|
||||
m_tlpText_ =
|
||||
std::regex_replace(m_tlpText_, std::regex("\\{" + kCldPlaceholder + "\\}"), cldText_);
|
||||
m_tlpText_ = std::regex_replace(
|
||||
m_tlpText_, std::regex("\\{" + kCldPlaceholder + "\\}"),
|
||||
fmt_lib::vformat(m_locale_, cldText_, fmt_lib::make_format_args(shiftedNow)));
|
||||
m_tlpText_ =
|
||||
std::regex_replace(m_tlpText_, std::regex("\\{" + kOrdPlaceholder + "\\}"), ordText_);
|
||||
} else {
|
||||
m_tlpText_ = m_tlpFmt_;
|
||||
}
|
||||
|
||||
m_tlpText_ = fmt_lib::vformat(m_locale_, m_tlpText_, fmt_lib::make_format_args(shiftedNow));
|
||||
m_tlpText_ = fmt_lib::vformat(m_locale_, m_tlpText_, fmt_lib::make_format_args(now));
|
||||
m_tooltip_->set_markup(m_tlpText_);
|
||||
label_.trigger_tooltip_query();
|
||||
}
|
||||
|
||||
@@ -61,9 +61,36 @@ std::tuple<std::vector<uint16_t>, std::string> waybar::modules::CpuUsage::getCpu
|
||||
std::vector<std::tuple<size_t, size_t>> curr_times = CpuUsage::parseCpuinfo();
|
||||
std::string tooltip;
|
||||
std::vector<uint16_t> usage;
|
||||
|
||||
if (curr_times.size() != prev_times.size()) {
|
||||
// The number of CPUs has changed, eg. due to CPU hotplug
|
||||
// We don't know which CPU came up or went down
|
||||
// so only give total usage (if we can)
|
||||
if (!curr_times.empty() && !prev_times.empty()) {
|
||||
auto [curr_idle, curr_total] = curr_times[0];
|
||||
auto [prev_idle, prev_total] = prev_times[0];
|
||||
const float delta_idle = curr_idle - prev_idle;
|
||||
const float delta_total = curr_total - prev_total;
|
||||
uint16_t tmp = 100 * (1 - delta_idle / delta_total);
|
||||
tooltip = fmt::format("Total: {}%\nCores: (pending)", tmp);
|
||||
usage.push_back(tmp);
|
||||
} else {
|
||||
tooltip = "(pending)";
|
||||
usage.push_back(0);
|
||||
}
|
||||
prev_times = curr_times;
|
||||
return {usage, tooltip};
|
||||
}
|
||||
|
||||
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];
|
||||
if (i > 0 && (curr_total == 0 || prev_total == 0)) {
|
||||
// This CPU is offline
|
||||
tooltip = tooltip + fmt::format("\nCore{}: offline", i - 1);
|
||||
usage.push_back(0);
|
||||
continue;
|
||||
}
|
||||
const float delta_idle = curr_idle - prev_idle;
|
||||
const float delta_total = curr_total - prev_total;
|
||||
uint16_t tmp = 100 * (1 - delta_idle / delta_total);
|
||||
|
||||
@@ -3,6 +3,23 @@
|
||||
#include "modules/cpu_usage.hpp"
|
||||
|
||||
std::vector<std::tuple<size_t, size_t>> waybar::modules::CpuUsage::parseCpuinfo() {
|
||||
// Get the "existing CPU count" from /sys/devices/system/cpu/present
|
||||
// Probably this is what the user wants the offline CPUs accounted from
|
||||
// For further details see:
|
||||
// https://www.kernel.org/doc/html/latest/core-api/cpu_hotplug.html
|
||||
const std::string sys_cpu_present_path = "/sys/devices/system/cpu/present";
|
||||
size_t cpu_present_last = 0;
|
||||
std::ifstream cpu_present_file(sys_cpu_present_path);
|
||||
std::string cpu_present_text;
|
||||
if (cpu_present_file.is_open()) {
|
||||
getline(cpu_present_file, cpu_present_text);
|
||||
// This is a comma-separated list of ranges, eg. 0,2-4,7
|
||||
size_t last_separator = cpu_present_text.find_last_of("-,");
|
||||
if (last_separator < cpu_present_text.size()) {
|
||||
std::stringstream(cpu_present_text.substr(last_separator + 1)) >> cpu_present_last;
|
||||
}
|
||||
}
|
||||
|
||||
const std::string data_dir_ = "/proc/stat";
|
||||
std::ifstream info(data_dir_);
|
||||
if (!info.is_open()) {
|
||||
@@ -10,14 +27,23 @@ std::vector<std::tuple<size_t, size_t>> waybar::modules::CpuUsage::parseCpuinfo(
|
||||
}
|
||||
std::vector<std::tuple<size_t, size_t>> cpuinfo;
|
||||
std::string line;
|
||||
size_t current_cpu_number = -1; // First line is total, second line is cpu 0
|
||||
while (getline(info, line)) {
|
||||
if (line.substr(0, 3).compare("cpu") != 0) {
|
||||
break;
|
||||
}
|
||||
size_t line_cpu_number;
|
||||
if (current_cpu_number >= 0) {
|
||||
std::stringstream(line.substr(3)) >> line_cpu_number;
|
||||
while (line_cpu_number > current_cpu_number) {
|
||||
// Fill in 0 for offline CPUs missing inside the lines of /proc/stat
|
||||
cpuinfo.emplace_back(0, 0);
|
||||
current_cpu_number++;
|
||||
}
|
||||
}
|
||||
std::stringstream sline(line.substr(5));
|
||||
std::vector<size_t> times;
|
||||
for (size_t time = 0; sline >> time; times.push_back(time))
|
||||
;
|
||||
for (size_t time = 0; sline >> time; times.push_back(time));
|
||||
|
||||
size_t idle_time = 0;
|
||||
size_t total_time = 0;
|
||||
@@ -27,6 +53,14 @@ std::vector<std::tuple<size_t, size_t>> waybar::modules::CpuUsage::parseCpuinfo(
|
||||
total_time = std::accumulate(times.begin(), times.end(), 0);
|
||||
}
|
||||
cpuinfo.emplace_back(idle_time, total_time);
|
||||
current_cpu_number++;
|
||||
}
|
||||
|
||||
while (cpu_present_last >= current_cpu_number) {
|
||||
// Fill in 0 for offline CPUs missing after the lines of /proc/stat
|
||||
cpuinfo.emplace_back(0, 0);
|
||||
current_cpu_number++;
|
||||
}
|
||||
|
||||
return cpuinfo;
|
||||
}
|
||||
|
||||
@@ -148,30 +148,18 @@ void IPC::unregisterForIPC(EventHandler* ev_handler) {
|
||||
std::string IPC::getSocket1Reply(const std::string& rq) {
|
||||
// basically hyprctl
|
||||
|
||||
struct addrinfo aiHints;
|
||||
struct addrinfo* aiRes = nullptr;
|
||||
const auto serverSocket = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
|
||||
if (serverSocket < 0) {
|
||||
spdlog::error("Hyprland IPC: Couldn't open a socket (1)");
|
||||
return "";
|
||||
}
|
||||
|
||||
memset(&aiHints, 0, sizeof(struct addrinfo));
|
||||
aiHints.ai_family = AF_UNSPEC;
|
||||
aiHints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
if (getaddrinfo("localhost", nullptr, &aiHints, &aiRes) != 0) {
|
||||
spdlog::error("Hyprland IPC: Couldn't get host (2)");
|
||||
return "";
|
||||
throw std::runtime_error("Hyprland IPC: Couldn't open a socket (1)");
|
||||
}
|
||||
|
||||
// get the instance signature
|
||||
auto* instanceSig = getenv("HYPRLAND_INSTANCE_SIGNATURE");
|
||||
|
||||
if (instanceSig == nullptr) {
|
||||
spdlog::error("Hyprland IPC: HYPRLAND_INSTANCE_SIGNATURE was not set! (Is Hyprland running?)");
|
||||
return "";
|
||||
throw std::runtime_error(
|
||||
"Hyprland IPC: HYPRLAND_INSTANCE_SIGNATURE was not set! (Is Hyprland running?)");
|
||||
}
|
||||
|
||||
sockaddr_un serverAddress = {0};
|
||||
@@ -182,14 +170,12 @@ std::string IPC::getSocket1Reply(const std::string& rq) {
|
||||
// Use snprintf to copy the socketPath string into serverAddress.sun_path
|
||||
if (snprintf(serverAddress.sun_path, sizeof(serverAddress.sun_path), "%s", socketPath.c_str()) <
|
||||
0) {
|
||||
spdlog::error("Hyprland IPC: Couldn't copy socket path (6)");
|
||||
return "";
|
||||
throw std::runtime_error("Hyprland IPC: Couldn't copy socket path (6)");
|
||||
}
|
||||
|
||||
if (connect(serverSocket, reinterpret_cast<sockaddr*>(&serverAddress), sizeof(serverAddress)) <
|
||||
0) {
|
||||
spdlog::error("Hyprland IPC: Couldn't connect to " + socketPath + ". (3)");
|
||||
return "";
|
||||
throw std::runtime_error("Hyprland IPC: Couldn't connect to " + socketPath + ". (3)");
|
||||
}
|
||||
|
||||
auto sizeWritten = write(serverSocket, rq.c_str(), rq.length());
|
||||
|
||||
@@ -92,15 +92,20 @@ auto Window::update() -> void {
|
||||
|
||||
auto Window::getActiveWorkspace() -> Workspace {
|
||||
const auto workspace = gIPC->getSocket1JsonReply("activeworkspace");
|
||||
assert(workspace.isObject());
|
||||
|
||||
if (workspace.isObject()) {
|
||||
return Workspace::parse(workspace);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
auto Window::getActiveWorkspace(const std::string& monitorName) -> Workspace {
|
||||
const auto monitors = gIPC->getSocket1JsonReply("monitors");
|
||||
assert(monitors.isArray());
|
||||
auto monitor = std::find_if(monitors.begin(), monitors.end(),
|
||||
[&](Json::Value monitor) { return monitor["name"] == monitorName; });
|
||||
if (monitors.isArray()) {
|
||||
auto monitor = std::find_if(monitors.begin(), monitors.end(), [&](Json::Value monitor) {
|
||||
return monitor["name"] == monitorName;
|
||||
});
|
||||
if (monitor == std::end(monitors)) {
|
||||
spdlog::warn("Monitor not found: {}", monitorName);
|
||||
return Workspace{-1, 0, "", ""};
|
||||
@@ -108,7 +113,7 @@ auto Window::getActiveWorkspace(const std::string& monitorName) -> Workspace {
|
||||
const int id = (*monitor)["activeWorkspace"]["id"].asInt();
|
||||
|
||||
const auto workspaces = gIPC->getSocket1JsonReply("workspaces");
|
||||
assert(workspaces.isArray());
|
||||
if (workspaces.isArray()) {
|
||||
auto workspace = std::find_if(workspaces.begin(), workspaces.end(),
|
||||
[&](Json::Value workspace) { return workspace["id"] == id; });
|
||||
if (workspace == std::end(workspaces)) {
|
||||
@@ -116,6 +121,10 @@ auto Window::getActiveWorkspace(const std::string& monitorName) -> Workspace {
|
||||
return Workspace{-1, 0, "", ""};
|
||||
}
|
||||
return Workspace::parse(*workspace);
|
||||
};
|
||||
};
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
auto Window::Workspace::parse(const Json::Value& value) -> Window::Workspace {
|
||||
@@ -146,7 +155,7 @@ void Window::queryActiveWorkspace() {
|
||||
focused_ = true;
|
||||
if (workspace_.windows > 0) {
|
||||
const auto clients = gIPC->getSocket1JsonReply("clients");
|
||||
assert(clients.isArray());
|
||||
if (clients.isArray()) {
|
||||
auto activeWindow = std::find_if(clients.begin(), clients.end(), [&](Json::Value window) {
|
||||
return window["address"] == workspace_.last_window;
|
||||
});
|
||||
@@ -193,6 +202,7 @@ void Window::queryActiveWorkspace() {
|
||||
} else {
|
||||
soloClass_ = "";
|
||||
}
|
||||
};
|
||||
} else {
|
||||
focused_ = false;
|
||||
windowData_ = WindowData{};
|
||||
|
||||
+17
-3
@@ -332,8 +332,8 @@ auto waybar::modules::Network::update() -> void {
|
||||
getState(signal_strength_);
|
||||
|
||||
auto text = fmt::format(
|
||||
fmt::runtime(format_), fmt::arg("essid", essid_), fmt::arg("signaldBm", signal_strength_dbm_),
|
||||
fmt::arg("signalStrength", signal_strength_),
|
||||
fmt::runtime(format_), fmt::arg("essid", essid_), fmt::arg("bssid", bssid_),
|
||||
fmt::arg("signaldBm", signal_strength_dbm_), fmt::arg("signalStrength", signal_strength_),
|
||||
fmt::arg("signalStrengthApp", signal_strength_app_), fmt::arg("ifname", ifname_),
|
||||
fmt::arg("netmask", netmask_), fmt::arg("ipaddr", ipaddr_), fmt::arg("gwaddr", gwaddr_),
|
||||
fmt::arg("cidr", cidr_), fmt::arg("frequency", fmt::format("{:.1f}", frequency_)),
|
||||
@@ -364,7 +364,7 @@ auto waybar::modules::Network::update() -> void {
|
||||
}
|
||||
if (!tooltip_format.empty()) {
|
||||
auto tooltip_text = fmt::format(
|
||||
fmt::runtime(tooltip_format), fmt::arg("essid", essid_),
|
||||
fmt::runtime(tooltip_format), fmt::arg("essid", essid_), fmt::arg("bssid", bssid_),
|
||||
fmt::arg("signaldBm", signal_strength_dbm_), fmt::arg("signalStrength", signal_strength_),
|
||||
fmt::arg("signalStrengthApp", signal_strength_app_), fmt::arg("ifname", ifname_),
|
||||
fmt::arg("netmask", netmask_), fmt::arg("ipaddr", ipaddr_), fmt::arg("gwaddr", gwaddr_),
|
||||
@@ -407,6 +407,7 @@ void waybar::modules::Network::clearIface() {
|
||||
ifid_ = -1;
|
||||
ifname_.clear();
|
||||
essid_.clear();
|
||||
bssid_.clear();
|
||||
ipaddr_.clear();
|
||||
gwaddr_.clear();
|
||||
netmask_.clear();
|
||||
@@ -481,6 +482,7 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) {
|
||||
} else {
|
||||
// clear state related to WiFi connection
|
||||
net->essid_.clear();
|
||||
net->bssid_.clear();
|
||||
net->signal_strength_dbm_ = 0;
|
||||
net->signal_strength_ = 0;
|
||||
net->signal_strength_app_.clear();
|
||||
@@ -772,6 +774,7 @@ int waybar::modules::Network::handleScan(struct nl_msg *msg, void *data) {
|
||||
net->parseEssid(bss);
|
||||
net->parseSignal(bss);
|
||||
net->parseFreq(bss);
|
||||
net->parseBssid(bss);
|
||||
return NL_OK;
|
||||
}
|
||||
|
||||
@@ -837,6 +840,17 @@ void waybar::modules::Network::parseFreq(struct nlattr **bss) {
|
||||
}
|
||||
}
|
||||
|
||||
void waybar::modules::Network::parseBssid(struct nlattr **bss) {
|
||||
if (bss[NL80211_BSS_BSSID] != nullptr) {
|
||||
auto bssid = static_cast<uint8_t *>(nla_data(bss[NL80211_BSS_BSSID]));
|
||||
auto bssid_len = nla_len(bss[NL80211_BSS_BSSID]);
|
||||
if (bssid_len == 6) {
|
||||
bssid_ = fmt::format("{:x}:{:x}:{:x}:{:x}:{:x}:{:x}", bssid[0], bssid[1], bssid[2], bssid[3],
|
||||
bssid[4], bssid[5]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool waybar::modules::Network::associatedOrJoined(struct nlattr **bss) {
|
||||
if (bss[NL80211_BSS_STATUS] == nullptr) {
|
||||
return false;
|
||||
|
||||
@@ -106,7 +106,7 @@ auto waybar::modules::Pulseaudio::update() -> void {
|
||||
}
|
||||
} else {
|
||||
label_.get_style_context()->remove_class("source-muted");
|
||||
if (config_["format-source-muted"].isString()) {
|
||||
if (config_["format-source"].isString()) {
|
||||
format_source = config_["format-source"].asString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,14 +14,14 @@
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<Glib::VariantBase> : formatter<std::string> {
|
||||
bool is_printable(const Glib::VariantBase& value) {
|
||||
bool is_printable(const Glib::VariantBase& value) const {
|
||||
auto type = value.get_type_string();
|
||||
/* Print only primitive (single character excluding 'v') and short complex types */
|
||||
return (type.length() == 1 && islower(type[0]) && type[0] != 'v') || value.get_size() <= 32;
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
auto format(const Glib::VariantBase& value, FormatContext& ctx) {
|
||||
auto format(const Glib::VariantBase& value, FormatContext& ctx) const {
|
||||
if (is_printable(value)) {
|
||||
return formatter<std::string>::format(static_cast<std::string>(value.print()), ctx);
|
||||
} else {
|
||||
|
||||
@@ -67,10 +67,9 @@ gboolean Watcher::handleRegisterHost(Watcher* obj, GDBusMethodInvocation* invoca
|
||||
}
|
||||
auto watch = gfWatchFind(obj->hosts_, bus_name, object_path);
|
||||
if (watch != nullptr) {
|
||||
g_dbus_method_invocation_return_error(
|
||||
invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
|
||||
"Status Notifier Host with bus name '%s' and object path '%s' is already registered",
|
||||
g_warning("Status Notifier Host with bus name '%s' and object path '%s' is already registered",
|
||||
bus_name, object_path);
|
||||
sn_watcher_complete_register_item(obj->watcher_, invocation);
|
||||
return TRUE;
|
||||
}
|
||||
watch = gfWatchNew(GF_WATCH_TYPE_HOST, service, bus_name, object_path, obj);
|
||||
|
||||
@@ -11,15 +11,14 @@ namespace waybar::modules::sway {
|
||||
// Helper function to assign a number to a workspace, just like sway. In fact
|
||||
// this is taken quite verbatim from `sway/ipc-json.c`.
|
||||
int Workspaces::convertWorkspaceNameToNum(std::string name) {
|
||||
if (isdigit(name[0])) {
|
||||
if (isdigit(name[0]) != 0) {
|
||||
errno = 0;
|
||||
char *endptr = NULL;
|
||||
char *endptr = nullptr;
|
||||
long long parsed_num = strtoll(name.c_str(), &endptr, 10);
|
||||
if (errno != 0 || parsed_num > INT32_MAX || parsed_num < 0 || endptr == name.c_str()) {
|
||||
return -1;
|
||||
} else {
|
||||
return (int)parsed_num;
|
||||
}
|
||||
return (int)parsed_num;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
@@ -47,7 +46,7 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value
|
||||
bar_(bar),
|
||||
box_(bar.orientation, 0) {
|
||||
if (config["format-icons"]["high-priority-named"].isArray()) {
|
||||
for (auto &it : config["format-icons"]["high-priority-named"]) {
|
||||
for (const auto &it : config["format-icons"]["high-priority-named"]) {
|
||||
high_priority_named_.push_back(it.asString());
|
||||
}
|
||||
}
|
||||
@@ -70,7 +69,7 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value
|
||||
|
||||
m_windowRewriteRules = waybar::util::RegexCollection(
|
||||
windowRewrite, m_windowRewriteDefault,
|
||||
[this](std::string &window_rule) { return windowRewritePriorityFunction(window_rule); });
|
||||
[](std::string &window_rule) { return windowRewritePriorityFunction(window_rule); });
|
||||
ipc_.subscribe(R"(["workspace"])");
|
||||
ipc_.subscribe(R"(["window"])");
|
||||
ipc_.signal_event.connect(sigc::mem_fun(*this, &Workspaces::onEvent));
|
||||
@@ -125,18 +124,10 @@ void Workspaces::onCmd(const struct Ipc::ipc_response &res) {
|
||||
std::copy(output["floating_nodes"].begin(), output["floating_nodes"].end(),
|
||||
std::back_inserter(workspaces_));
|
||||
}
|
||||
if (config_["persistent_workspaces"].isObject()) {
|
||||
spdlog::warn(
|
||||
"persistent_workspaces is deprecated. Please change config to use "
|
||||
"persistent-workspaces.");
|
||||
}
|
||||
|
||||
// adding persistent workspaces (as per the config file)
|
||||
if (config_["persistent-workspaces"].isObject() ||
|
||||
config_["persistent_workspaces"].isObject()) {
|
||||
const Json::Value &p_workspaces = config_["persistent-workspaces"].isObject()
|
||||
? config_["persistent-workspaces"]
|
||||
: config_["persistent_workspaces"];
|
||||
if (config_["persistent-workspaces"].isObject()) {
|
||||
const Json::Value &p_workspaces = config_["persistent-workspaces"];
|
||||
const std::vector<std::string> p_workspaces_names = p_workspaces.getMemberNames();
|
||||
|
||||
for (const std::string &p_w_name : p_workspaces_names) {
|
||||
@@ -270,14 +261,18 @@ void Workspaces::updateWindows(const Json::Value &node, std::string &windows) {
|
||||
node["name"].isString()) {
|
||||
std::string title = g_markup_escape_text(node["name"].asString().c_str(), -1);
|
||||
std::string windowClass = node["app_id"].asString();
|
||||
|
||||
// Only add window rewrites that can be looked up
|
||||
if (!windowClass.empty()) {
|
||||
std::string windowReprKey = fmt::format("class<{}> title<{}>", windowClass, title);
|
||||
std::string window = m_windowRewriteRules.get(windowReprKey);
|
||||
// allow result to have formatting
|
||||
window =
|
||||
fmt::format(fmt::runtime(window), fmt::arg("name", title), fmt::arg("class", windowClass));
|
||||
window = fmt::format(fmt::runtime(window), fmt::arg("name", title),
|
||||
fmt::arg("class", windowClass));
|
||||
windows.append(window);
|
||||
windows.append(m_formatWindowSeperator);
|
||||
}
|
||||
}
|
||||
for (const Json::Value &child : node["nodes"]) {
|
||||
updateWindows(child, windows);
|
||||
}
|
||||
@@ -422,7 +417,7 @@ std::string Workspaces::getIcon(const std::string &name, const Json::Value &node
|
||||
}
|
||||
|
||||
bool Workspaces::handleScroll(GdkEventScroll *e) {
|
||||
if (gdk_event_get_pointer_emulated((GdkEvent *)e)) {
|
||||
if (gdk_event_get_pointer_emulated((GdkEvent *)e) != 0) {
|
||||
/**
|
||||
* Ignore emulated scroll events on window
|
||||
*/
|
||||
@@ -472,8 +467,7 @@ bool Workspaces::handleScroll(GdkEventScroll *e) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::string Workspaces::getCycleWorkspace(std::vector<Json::Value>::iterator it,
|
||||
bool prev) const {
|
||||
std::string Workspaces::getCycleWorkspace(std::vector<Json::Value>::iterator it, bool prev) const {
|
||||
if (prev && it == workspaces_.begin() && !config_["disable-scroll-wraparound"].asBool()) {
|
||||
return (*(--workspaces_.end()))["name"].asString();
|
||||
}
|
||||
|
||||
@@ -163,7 +163,8 @@ void waybar::modules::Wireplumber::onDefaultNodesApiChanged(waybar::modules::Wir
|
||||
"[{}]: (onDefaultNodesApiChanged) - got the following default node: Node(name: {}, id: {})",
|
||||
self->name_, defaultNodeName, defaultNodeId);
|
||||
|
||||
if (g_strcmp0(self->default_node_name_, defaultNodeName) == 0) {
|
||||
if (g_strcmp0(self->default_node_name_, defaultNodeName) == 0 &&
|
||||
self->node_id_ == defaultNodeId) {
|
||||
spdlog::debug(
|
||||
"[{}]: (onDefaultNodesApiChanged) - Default node has not changed. Node(name: {}, id: {}). "
|
||||
"Ignoring.",
|
||||
|
||||
@@ -387,6 +387,11 @@ void Task::handle_title(const char *title) {
|
||||
hide_if_ignored();
|
||||
}
|
||||
|
||||
void Task::set_minimize_hint() {
|
||||
zwlr_foreign_toplevel_handle_v1_set_rectangle(handle_, bar_.surface, minimize_hint.x,
|
||||
minimize_hint.y, minimize_hint.w, minimize_hint.h);
|
||||
}
|
||||
|
||||
void Task::hide_if_ignored() {
|
||||
if (tbar_->ignore_list().count(app_id_) || tbar_->ignore_list().count(title_)) {
|
||||
ignored_ = true;
|
||||
@@ -447,6 +452,13 @@ void Task::handle_app_id(const char *app_id) {
|
||||
spdlog::debug("Couldn't find icon for {}", app_id_);
|
||||
}
|
||||
|
||||
void Task::on_button_size_allocated(Gtk::Allocation &alloc) {
|
||||
gtk_widget_translate_coordinates(GTK_WIDGET(button.gobj()), GTK_WIDGET(bar_.window.gobj()), 0, 0,
|
||||
&minimize_hint.x, &minimize_hint.y);
|
||||
minimize_hint.w = button.get_width();
|
||||
minimize_hint.h = button.get_height();
|
||||
}
|
||||
|
||||
void Task::handle_output_enter(struct wl_output *output) {
|
||||
if (ignored_) {
|
||||
spdlog::debug("{} is ignored", repr());
|
||||
@@ -457,6 +469,8 @@ void Task::handle_output_enter(struct wl_output *output) {
|
||||
|
||||
if (!button_visible_ && (tbar_->all_outputs() || tbar_->show_output(output))) {
|
||||
/* The task entered the output of the current bar make the button visible */
|
||||
button.signal_size_allocate().connect_notify(
|
||||
sigc::mem_fun(this, &Task::on_button_size_allocated));
|
||||
tbar_->add_button(button);
|
||||
button.show();
|
||||
button_visible_ = true;
|
||||
@@ -553,9 +567,11 @@ bool Task::handle_clicked(GdkEventButton *bt) {
|
||||
return true;
|
||||
else if (action == "activate")
|
||||
activate();
|
||||
else if (action == "minimize")
|
||||
else if (action == "minimize") {
|
||||
set_minimize_hint();
|
||||
minimize(!minimized());
|
||||
else if (action == "minimize-raise") {
|
||||
} else if (action == "minimize-raise") {
|
||||
set_minimize_hint();
|
||||
if (minimized())
|
||||
minimize(false);
|
||||
else if (active())
|
||||
|
||||
@@ -144,6 +144,12 @@ void AudioBackend::sinkInfoCb(pa_context * /*context*/, const pa_sink_info *i, i
|
||||
if (!backend->ignored_sinks_.empty()) {
|
||||
for (const auto &ignored_sink : backend->ignored_sinks_) {
|
||||
if (ignored_sink == i->description) {
|
||||
if (i->name == backend->current_sink_name_) {
|
||||
// If the current sink happens to be ignored it is never considered running
|
||||
// so it will be replaced with another sink.
|
||||
backend->current_sink_running_ = false;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,8 +43,14 @@ std::string waybar::CssReloadHelper::findPath(const std::string& filename) {
|
||||
}
|
||||
|
||||
// File monitor does not work with symlinks, so resolve them
|
||||
if (std::filesystem::is_symlink(result)) {
|
||||
std::string original = result;
|
||||
while (std::filesystem::is_symlink(result)) {
|
||||
result = std::filesystem::read_symlink(result);
|
||||
|
||||
// prevent infinite cycle
|
||||
if (result == original) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
@@ -52,10 +52,8 @@ TEST_CASE_METHOD(IPCTestFixture, "XDGRuntimeDirExistsNoHyprDir", "[getSocketFold
|
||||
REQUIRE(actualPath == expectedPath);
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(IPCMock, "getSocket1JsonReply handles empty response", "[getSocket1JsonReply]") {
|
||||
TEST_CASE_METHOD(IPCTestFixture, "getSocket1Reply throws on no socket", "[getSocket1Reply]") {
|
||||
std::string request = "test_request";
|
||||
|
||||
Json::Value jsonResponse = getSocket1JsonReply(request);
|
||||
|
||||
REQUIRE(jsonResponse.isNull());
|
||||
CHECK_THROWS(getSocket1Reply(request));
|
||||
}
|
||||
|
||||
@@ -19,4 +19,7 @@ class IPCMock : public IPCTestFixture {
|
||||
public:
|
||||
// Mock getSocket1Reply to return an empty string
|
||||
static std::string getSocket1Reply(const std::string& rq) { return ""; }
|
||||
|
||||
protected:
|
||||
const char* instanceSig = "instance_sig";
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user