Merge branch 'master' into river-tags-monitor-focus

This commit is contained in:
lilly-lizard
2024-09-05 16:27:21 +12:00
36 changed files with 376 additions and 156 deletions
+2 -3
View File
@@ -14,18 +14,17 @@ jobs:
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Test in FreeBSD VM - name: Test in FreeBSD VM
uses: cross-platform-actions/action@v0.23.0 uses: cross-platform-actions/action@v0.25.0
timeout-minutes: 180 timeout-minutes: 180
env: env:
CPPFLAGS: '-isystem/usr/local/include' CPPFLAGS: '-isystem/usr/local/include'
LDFLAGS: '-L/usr/local/lib' LDFLAGS: '-L/usr/local/lib'
with: with:
operating_system: freebsd operating_system: freebsd
version: "13.2" version: "14.1"
environment_variables: CPPFLAGS LDFLAGS environment_variables: CPPFLAGS LDFLAGS
sync_files: runner-to-vm sync_files: runner-to-vm
run: | run: |
sudo sed -i '' 's/quarterly/latest/' /etc/pkg/FreeBSD.conf
sudo pkg install -y git # subprojects/date sudo pkg install -y git # subprojects/date
sudo pkg install -y catch evdev-proto gtk-layer-shell gtkmm30 jsoncpp \ sudo pkg install -y catch evdev-proto gtk-layer-shell gtkmm30 jsoncpp \
libdbusmenu libevdev libfmt libmpdclient libudev-devd meson \ libdbusmenu libevdev libfmt libmpdclient libudev-devd meson \
Generated
+3 -3
View File
@@ -18,11 +18,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1719506693, "lastModified": 1722185531,
"narHash": "sha256-C8e9S7RzshSdHB7L+v9I51af1gDM5unhJ2xO1ywxNH8=", "narHash": "sha256-veKR07psFoJjINLC8RK4DiLniGGMgF3QMlS4tb74S6k=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "b2852eb9365c6de48ffb0dc2c9562591f652242a", "rev": "52ec9ac3b12395ad677e8b62106f0b98c1f8569d",
"type": "github" "type": "github"
}, },
"original": { "original": {
+2 -2
View File
@@ -54,7 +54,7 @@ class BarIpcClient;
} }
#endif // HAVE_SWAY #endif // HAVE_SWAY
class Bar { class Bar : public sigc::trackable {
public: public:
using bar_mode_map = std::map<std::string, struct bar_mode>; using bar_mode_map = std::map<std::string, struct bar_mode>;
static const bar_mode_map PRESET_MODES; static const bar_mode_map PRESET_MODES;
@@ -66,7 +66,7 @@ class Bar {
~Bar(); ~Bar();
void setMode(const std::string &mode); void setMode(const std::string &mode);
void setVisible(bool visible); void setVisible(bool value);
void toggle(); void toggle();
void handleSignal(int); void handleSignal(int);
+3
View File
@@ -20,6 +20,9 @@ class Config {
static std::optional<std::string> findConfigPath( static std::optional<std::string> findConfigPath(
const std::vector<std::string> &names, const std::vector<std::string> &dirs = CONFIG_DIRS); 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; Config() = default;
void load(const std::string &config); void load(const std::string &config);
+5 -1
View File
@@ -12,7 +12,7 @@ namespace waybar {
class Group : public AModule { class Group : public AModule {
public: public:
Group(const std::string &, const std::string &, const Json::Value &, bool); Group(const std::string &, const std::string &, const Json::Value &, bool);
virtual ~Group() = default; ~Group() override = default;
auto update() -> void override; auto update() -> void override;
operator Gtk::Widget &() override; operator Gtk::Widget &() override;
@@ -25,9 +25,13 @@ class Group : public AModule {
Gtk::Revealer revealer; Gtk::Revealer revealer;
bool is_first_widget = true; bool is_first_widget = true;
bool is_drawer = false; bool is_drawer = false;
bool click_to_reveal = false;
std::string add_class_to_drawer_children; std::string add_class_to_drawer_children;
bool handleMouseEnter(GdkEventCrossing *const &ev) override; bool handleMouseEnter(GdkEventCrossing *const &ev) override;
bool handleMouseLeave(GdkEventCrossing *const &ev) override; bool handleMouseLeave(GdkEventCrossing *const &ev) override;
bool handleToggle(GdkEventButton *const &ev) override;
void show_group();
void hide_group();
}; };
} // namespace waybar } // namespace waybar
+2
View File
@@ -40,6 +40,7 @@ class Network : public ALabel {
void parseEssid(struct nlattr**); void parseEssid(struct nlattr**);
void parseSignal(struct nlattr**); void parseSignal(struct nlattr**);
void parseFreq(struct nlattr**); void parseFreq(struct nlattr**);
void parseBssid(struct nlattr**);
bool associatedOrJoined(struct nlattr**); bool associatedOrJoined(struct nlattr**);
bool checkInterface(std::string name); bool checkInterface(std::string name);
auto getInfo() -> void; auto getInfo() -> void;
@@ -69,6 +70,7 @@ class Network : public ALabel {
std::string state_; std::string state_;
std::string essid_; std::string essid_;
std::string bssid_;
bool carrier_; bool carrier_;
std::string ifname_; std::string ifname_;
std::string ipaddr_; std::string ipaddr_;
+4 -4
View File
@@ -19,7 +19,7 @@ namespace waybar::modules::sway {
class Workspaces : public AModule, public sigc::trackable { class Workspaces : public AModule, public sigc::trackable {
public: public:
Workspaces(const std::string&, const waybar::Bar&, const Json::Value&); Workspaces(const std::string&, const waybar::Bar&, const Json::Value&);
virtual ~Workspaces() = default; ~Workspaces() override = default;
auto update() -> void override; auto update() -> void override;
private: private:
@@ -38,10 +38,10 @@ class Workspaces : public AModule, public sigc::trackable {
Gtk::Button& addButton(const Json::Value&); Gtk::Button& addButton(const Json::Value&);
void onButtonReady(const Json::Value&, Gtk::Button&); void onButtonReady(const Json::Value&, Gtk::Button&);
std::string getIcon(const std::string&, const Json::Value&); 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; uint16_t getWorkspaceIndex(const std::string& name) const;
std::string trimWorkspaceName(std::string); static std::string trimWorkspaceName(std::string);
bool handleScroll(GdkEventScroll*) override; bool handleScroll(GdkEventScroll* /*unused*/) override;
const Bar& bar_; const Bar& bar_;
std::vector<Json::Value> workspaces_; std::vector<Json::Value> workspaces_;
+7
View File
@@ -24,6 +24,10 @@
namespace waybar::modules::wlr { namespace waybar::modules::wlr {
struct widget_geometry {
int x, y, w, h;
};
class Taskbar; class Taskbar;
class Task { class Task {
@@ -42,6 +46,7 @@ class Task {
}; };
// made public so TaskBar can reorder based on configuration. // made public so TaskBar can reorder based on configuration.
Gtk::Button button; Gtk::Button button;
struct widget_geometry minimize_hint;
private: private:
static uint32_t global_id; static uint32_t global_id;
@@ -82,6 +87,8 @@ class Task {
private: private:
std::string repr() const; std::string repr() const;
std::string state_string(bool = false) 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); 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, bool image_load_icon(Gtk::Image &image, const Glib::RefPtr<Gtk::IconTheme> &icon_theme,
Glib::RefPtr<Gio::DesktopAppInfo> app_info, int size); Glib::RefPtr<Gio::DesktopAppInfo> app_info, int size);
+1 -1
View File
@@ -92,7 +92,7 @@ struct formatter<pow_format> {
template <> template <>
struct formatter<Glib::ustring> : formatter<std::string> { struct formatter<Glib::ustring> : formatter<std::string> {
template <typename FormatContext> 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); return formatter<std::string>::format(static_cast<std::string>(value), ctx);
} }
}; };
+2
View File
@@ -156,6 +156,8 @@ Addressed by *network*
*{essid}*: Name (SSID) of the wireless network. *{essid}*: Name (SSID) of the wireless network.
*{bssid}*: MAC address (BSSID) of the wireless access point.
*{signalStrength}*: Signal strength of the wireless network. *{signalStrength}*: Signal strength of the wireless network.
*{signaldBm}*: Signal strength of the wireless network in dBm. *{signaldBm}*: Signal strength of the wireless network in dBm.
+34
View File
@@ -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 # SEE ALSO
- *waybar(5)* - *waybar(5)*
+1
View File
@@ -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. 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. 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. 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*: *window-rewrite-default*:
typeof: string ++ typeof: string ++
+5
View File
@@ -278,6 +278,11 @@ A group may hide all but one element, showing them only on mouse hover. In order
default: "hidden" ++ default: "hidden" ++
Defines the CSS class to be applied to the hidden elements. 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*: ++ *transition-left-to-right*: ++
typeof: bool ++ typeof: bool ++
default: true ++ default: true ++
+1 -1
View File
@@ -1,6 +1,6 @@
project( project(
'waybar', 'cpp', 'c', 'waybar', 'cpp', 'c',
version: '0.10.3', version: '0.10.4',
license: 'MIT', license: 'MIT',
meson_version: '>= 0.59.0', meson_version: '>= 0.59.0',
default_options : [ default_options : [
+11 -1
View File
@@ -6,6 +6,8 @@
#include <iostream> #include <iostream>
#include <util/command.hpp> #include <util/command.hpp>
#include "config.hpp"
namespace waybar { namespace waybar {
ALabel::ALabel(const Json::Value& config, const std::string& name, const std::string& id, 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 { try {
// Check that the file exists // Check that the file exists
std::string menuFile = config_["menu-file"].asString(); 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 // Read the menu descriptor file
std::ifstream file(menuFile); std::ifstream file(menuFile);
if (!file.is_open()) { if (!file.is_open()) {
@@ -170,7 +180,7 @@ bool waybar::ALabel::handleToggle(GdkEventButton* const& e) {
return AModule::handleToggle(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"); waybar::util::command::res res = waybar::util::command::exec((char*)data, "GtkMenu");
} }
+34 -4
View File
@@ -1,9 +1,13 @@
#include "AModule.hpp" #include "AModule.hpp"
#include <fmt/format.h> #include <fmt/format.h>
#include <spdlog/spdlog.h>
#include <util/command.hpp> #include <util/command.hpp>
#include "gdk/gdk.h"
#include "gdkmm/cursor.h"
namespace waybar { namespace waybar {
AModule::AModule(const Json::Value& config, const std::string& name, const std::string& id, 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_.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK);
event_box_.signal_scroll_event().connect(sigc::mem_fun(*this, &AModule::handleScroll)); 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() { AModule::~AModule() {
@@ -91,9 +106,20 @@ auto AModule::doAction(const std::string& name) -> void {
} }
void AModule::setCursor(Gdk::CursorType const& c) { void AModule::setCursor(Gdk::CursorType const& c) {
auto cursor = Gdk::Cursor::create(c);
auto gdk_window = event_box_.get_window(); auto gdk_window = event_box_.get_window();
gdk_window->set_cursor(cursor); 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) { 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); module->set_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT);
} }
if (hasUserEvents_) { // Default behavior indicating event availability
if (hasUserEvents_ && !config_.isMember("cursor")) {
setCursor(Gdk::HAND2); setCursor(Gdk::HAND2);
} }
return false; return false;
} }
@@ -112,9 +140,11 @@ bool AModule::handleMouseLeave(GdkEventCrossing* const& e) {
module->unset_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT); module->unset_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT);
} }
if (hasUserEvents_) { // Default behavior indicating event availability
if (hasUserEvents_ && !config_.isMember("cursor")) {
setCursor(Gdk::ARROW); setCursor(Gdk::ARROW);
} }
return false; return false;
} }
+2 -1
View File
@@ -404,7 +404,8 @@ void waybar::Bar::onMap(GdkEventAny* /*unused*/) {
setPassThrough(passthrough_); setPassThrough(passthrough_);
} }
void waybar::Bar::setVisible(bool visible) { void waybar::Bar::setVisible(bool value) {
visible = value;
if (auto mode = config.get("mode", {}); mode.isString()) { if (auto mode = config.get("mode", {}); mode.isString()) {
setMode(visible ? config["mode"].asString() : MODE_INVISIBLE); setMode(visible ? config["mode"].asString() : MODE_INVISIBLE);
} else { } else {
+2 -1
View File
@@ -123,7 +123,8 @@ void waybar::Client::handleMonitorAdded(Glib::RefPtr<Gdk::Monitor> monitor) {
} }
void waybar::Client::handleMonitorRemoved(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. /* 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 * Defer destruction of bars for the output to the next iteration of the event loop to avoid
* deleting objects referenced by currently executed code. * deleting objects referenced by currently executed code.
+2 -1
View File
@@ -21,7 +21,8 @@ const std::vector<std::string> Config::CONFIG_DIRS = {
const char *Config::CONFIG_PATH_ENV = "WAYBAR_CONFIG_DIR"; 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; fs::path path;
if (!filename.empty()) { if (!filename.empty()) {
+29 -4
View File
@@ -9,7 +9,7 @@
namespace waybar { 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, /* The transition direction of a drawer is not actually determined by the transition type,
* but rather by the order of 'box' and 'revealer_box': * but rather by the order of 'box' and 'revealer_box':
* 'REVEALER_TRANSITION_TYPE_SLIDE_LEFT' and 'REVEALER_TRANSITION_TYPE_SLIDE_RIGHT' * '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() const bool left_to_right = (drawer_config["transition-left-to-right"].isBool()
? drawer_config["transition-left-to-right"].asBool() ? drawer_config["transition-left-to-right"].asBool()
: true); : true);
click_to_reveal = drawer_config["click-to-reveal"].asBool();
auto transition_type = getPreferredTransitionType(vertical); 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); event_box_.add(box);
} }
bool Group::handleMouseEnter(GdkEventCrossing* const& e) { void Group::show_group() {
box.set_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT); box.set_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT);
revealer.set_reveal_child(true); 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; return false;
} }
bool Group::handleMouseLeave(GdkEventCrossing* const& e) { bool Group::handleMouseLeave(GdkEventCrossing* const& e) {
box.unset_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT); if (!click_to_reveal && e->detail != GDK_NOTIFY_INFERIOR) {
revealer.set_reveal_child(false); hide_group();
}
return false; 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 { auto Group::update() -> void {
// noop // noop
} }
+4 -3
View File
@@ -163,15 +163,16 @@ auto waybar::modules::Clock::update() -> void {
// std::vformat doesn't support named arguments. // std::vformat doesn't support named arguments.
m_tlpText_ = m_tlpText_ =
std::regex_replace(m_tlpFmt_, std::regex("\\{" + kTZPlaceholder + "\\}"), tzText_); std::regex_replace(m_tlpFmt_, std::regex("\\{" + kTZPlaceholder + "\\}"), tzText_);
m_tlpText_ = m_tlpText_ = std::regex_replace(
std::regex_replace(m_tlpText_, std::regex("\\{" + kCldPlaceholder + "\\}"), cldText_); m_tlpText_, std::regex("\\{" + kCldPlaceholder + "\\}"),
fmt_lib::vformat(m_locale_, cldText_, fmt_lib::make_format_args(shiftedNow)));
m_tlpText_ = m_tlpText_ =
std::regex_replace(m_tlpText_, std::regex("\\{" + kOrdPlaceholder + "\\}"), ordText_); std::regex_replace(m_tlpText_, std::regex("\\{" + kOrdPlaceholder + "\\}"), ordText_);
} else { } else {
m_tlpText_ = m_tlpFmt_; 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_); m_tooltip_->set_markup(m_tlpText_);
label_.trigger_tooltip_query(); label_.trigger_tooltip_query();
} }
+27
View File
@@ -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::vector<std::tuple<size_t, size_t>> curr_times = CpuUsage::parseCpuinfo();
std::string tooltip; std::string tooltip;
std::vector<uint16_t> usage; 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) { for (size_t i = 0; i < curr_times.size(); ++i) {
auto [curr_idle, curr_total] = curr_times[i]; auto [curr_idle, curr_total] = curr_times[i];
auto [prev_idle, prev_total] = prev_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_idle = curr_idle - prev_idle;
const float delta_total = curr_total - prev_total; const float delta_total = curr_total - prev_total;
uint16_t tmp = 100 * (1 - delta_idle / delta_total); uint16_t tmp = 100 * (1 - delta_idle / delta_total);
+36 -2
View File
@@ -3,6 +3,23 @@
#include "modules/cpu_usage.hpp" #include "modules/cpu_usage.hpp"
std::vector<std::tuple<size_t, size_t>> waybar::modules::CpuUsage::parseCpuinfo() { 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"; const std::string data_dir_ = "/proc/stat";
std::ifstream info(data_dir_); std::ifstream info(data_dir_);
if (!info.is_open()) { 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::vector<std::tuple<size_t, size_t>> cpuinfo;
std::string line; std::string line;
size_t current_cpu_number = -1; // First line is total, second line is cpu 0
while (getline(info, line)) { while (getline(info, line)) {
if (line.substr(0, 3).compare("cpu") != 0) { if (line.substr(0, 3).compare("cpu") != 0) {
break; 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::stringstream sline(line.substr(5));
std::vector<size_t> times; 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 idle_time = 0;
size_t total_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); total_time = std::accumulate(times.begin(), times.end(), 0);
} }
cpuinfo.emplace_back(idle_time, total_time); 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; return cpuinfo;
} }
+5 -19
View File
@@ -148,30 +148,18 @@ void IPC::unregisterForIPC(EventHandler* ev_handler) {
std::string IPC::getSocket1Reply(const std::string& rq) { std::string IPC::getSocket1Reply(const std::string& rq) {
// basically hyprctl // basically hyprctl
struct addrinfo aiHints;
struct addrinfo* aiRes = nullptr;
const auto serverSocket = socket(AF_UNIX, SOCK_STREAM, 0); const auto serverSocket = socket(AF_UNIX, SOCK_STREAM, 0);
if (serverSocket < 0) { if (serverSocket < 0) {
spdlog::error("Hyprland IPC: Couldn't open a socket (1)"); throw std::runtime_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 "";
} }
// get the instance signature // get the instance signature
auto* instanceSig = getenv("HYPRLAND_INSTANCE_SIGNATURE"); auto* instanceSig = getenv("HYPRLAND_INSTANCE_SIGNATURE");
if (instanceSig == nullptr) { if (instanceSig == nullptr) {
spdlog::error("Hyprland IPC: HYPRLAND_INSTANCE_SIGNATURE was not set! (Is Hyprland running?)"); throw std::runtime_error(
return ""; "Hyprland IPC: HYPRLAND_INSTANCE_SIGNATURE was not set! (Is Hyprland running?)");
} }
sockaddr_un serverAddress = {0}; 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 // Use snprintf to copy the socketPath string into serverAddress.sun_path
if (snprintf(serverAddress.sun_path, sizeof(serverAddress.sun_path), "%s", socketPath.c_str()) < if (snprintf(serverAddress.sun_path, sizeof(serverAddress.sun_path), "%s", socketPath.c_str()) <
0) { 0) {
spdlog::error("Hyprland IPC: Couldn't copy socket path (6)"); throw std::runtime_error("Hyprland IPC: Couldn't copy socket path (6)");
return "";
} }
if (connect(serverSocket, reinterpret_cast<sockaddr*>(&serverAddress), sizeof(serverAddress)) < if (connect(serverSocket, reinterpret_cast<sockaddr*>(&serverAddress), sizeof(serverAddress)) <
0) { 0) {
spdlog::error("Hyprland IPC: Couldn't connect to " + socketPath + ". (3)"); throw std::runtime_error("Hyprland IPC: Couldn't connect to " + socketPath + ". (3)");
return "";
} }
auto sizeWritten = write(serverSocket, rq.c_str(), rq.length()); auto sizeWritten = write(serverSocket, rq.c_str(), rq.length());
+71 -61
View File
@@ -92,30 +92,39 @@ auto Window::update() -> void {
auto Window::getActiveWorkspace() -> Workspace { auto Window::getActiveWorkspace() -> Workspace {
const auto workspace = gIPC->getSocket1JsonReply("activeworkspace"); const auto workspace = gIPC->getSocket1JsonReply("activeworkspace");
assert(workspace.isObject());
return Workspace::parse(workspace); if (workspace.isObject()) {
return Workspace::parse(workspace);
}
return {};
} }
auto Window::getActiveWorkspace(const std::string& monitorName) -> Workspace { auto Window::getActiveWorkspace(const std::string& monitorName) -> Workspace {
const auto monitors = gIPC->getSocket1JsonReply("monitors"); const auto monitors = gIPC->getSocket1JsonReply("monitors");
assert(monitors.isArray()); if (monitors.isArray()) {
auto monitor = std::find_if(monitors.begin(), monitors.end(), auto monitor = std::find_if(monitors.begin(), monitors.end(), [&](Json::Value monitor) {
[&](Json::Value monitor) { return monitor["name"] == monitorName; }); return monitor["name"] == monitorName;
if (monitor == std::end(monitors)) { });
spdlog::warn("Monitor not found: {}", monitorName); if (monitor == std::end(monitors)) {
return Workspace{-1, 0, "", ""}; spdlog::warn("Monitor not found: {}", monitorName);
} return Workspace{-1, 0, "", ""};
const int id = (*monitor)["activeWorkspace"]["id"].asInt(); }
const int id = (*monitor)["activeWorkspace"]["id"].asInt();
const auto workspaces = gIPC->getSocket1JsonReply("workspaces"); const auto workspaces = gIPC->getSocket1JsonReply("workspaces");
assert(workspaces.isArray()); if (workspaces.isArray()) {
auto workspace = std::find_if(workspaces.begin(), workspaces.end(), auto workspace = std::find_if(workspaces.begin(), workspaces.end(),
[&](Json::Value workspace) { return workspace["id"] == id; }); [&](Json::Value workspace) { return workspace["id"] == id; });
if (workspace == std::end(workspaces)) { if (workspace == std::end(workspaces)) {
spdlog::warn("No workspace with id {}", id); spdlog::warn("No workspace with id {}", id);
return Workspace{-1, 0, "", ""}; return Workspace{-1, 0, "", ""};
} }
return Workspace::parse(*workspace); return Workspace::parse(*workspace);
};
};
return {};
} }
auto Window::Workspace::parse(const Json::Value& value) -> Window::Workspace { auto Window::Workspace::parse(const Json::Value& value) -> Window::Workspace {
@@ -146,53 +155,54 @@ void Window::queryActiveWorkspace() {
focused_ = true; focused_ = true;
if (workspace_.windows > 0) { if (workspace_.windows > 0) {
const auto clients = gIPC->getSocket1JsonReply("clients"); const auto clients = gIPC->getSocket1JsonReply("clients");
assert(clients.isArray()); if (clients.isArray()) {
auto activeWindow = std::find_if(clients.begin(), clients.end(), [&](Json::Value window) { auto activeWindow = std::find_if(clients.begin(), clients.end(), [&](Json::Value window) {
return window["address"] == workspace_.last_window; return window["address"] == workspace_.last_window;
}); });
if (activeWindow == std::end(clients)) { if (activeWindow == std::end(clients)) {
focused_ = false; focused_ = false;
return; return;
} }
windowData_ = WindowData::parse(*activeWindow); windowData_ = WindowData::parse(*activeWindow);
updateAppIconName(windowData_.class_name, windowData_.initial_class_name); updateAppIconName(windowData_.class_name, windowData_.initial_class_name);
std::vector<Json::Value> workspaceWindows; std::vector<Json::Value> workspaceWindows;
std::copy_if(clients.begin(), clients.end(), std::back_inserter(workspaceWindows), std::copy_if(clients.begin(), clients.end(), std::back_inserter(workspaceWindows),
[&](Json::Value window) { [&](Json::Value window) {
return window["workspace"]["id"] == workspace_.id && window["mapped"].asBool(); return window["workspace"]["id"] == workspace_.id && window["mapped"].asBool();
}); });
swallowing_ = swallowing_ =
std::any_of(workspaceWindows.begin(), workspaceWindows.end(), [&](Json::Value window) { std::any_of(workspaceWindows.begin(), workspaceWindows.end(), [&](Json::Value window) {
return !window["swallowing"].isNull() && window["swallowing"].asString() != "0x0"; return !window["swallowing"].isNull() && window["swallowing"].asString() != "0x0";
}); });
std::vector<Json::Value> visibleWindows; std::vector<Json::Value> visibleWindows;
std::copy_if(workspaceWindows.begin(), workspaceWindows.end(), std::copy_if(workspaceWindows.begin(), workspaceWindows.end(),
std::back_inserter(visibleWindows), std::back_inserter(visibleWindows),
[&](Json::Value window) { return !window["hidden"].asBool(); }); [&](Json::Value window) { return !window["hidden"].asBool(); });
solo_ = 1 == std::count_if(visibleWindows.begin(), visibleWindows.end(), solo_ = 1 == std::count_if(visibleWindows.begin(), visibleWindows.end(),
[&](Json::Value window) { return !window["floating"].asBool(); }); [&](Json::Value window) { return !window["floating"].asBool(); });
allFloating_ = std::all_of(visibleWindows.begin(), visibleWindows.end(), allFloating_ = std::all_of(visibleWindows.begin(), visibleWindows.end(),
[&](Json::Value window) { return window["floating"].asBool(); }); [&](Json::Value window) { return window["floating"].asBool(); });
fullscreen_ = windowData_.fullscreen; fullscreen_ = windowData_.fullscreen;
// Fullscreen windows look like they are solo // Fullscreen windows look like they are solo
if (fullscreen_) { if (fullscreen_) {
solo_ = true; solo_ = true;
} }
// Grouped windows have a tab bar and therefore don't look fullscreen or solo // Grouped windows have a tab bar and therefore don't look fullscreen or solo
if (windowData_.grouped) { if (windowData_.grouped) {
fullscreen_ = false; fullscreen_ = false;
solo_ = false; solo_ = false;
} }
if (solo_) { if (solo_) {
soloClass_ = windowData_.class_name; soloClass_ = windowData_.class_name;
} else { } else {
soloClass_ = ""; soloClass_ = "";
} }
};
} else { } else {
focused_ = false; focused_ = false;
windowData_ = WindowData{}; windowData_ = WindowData{};
+17 -3
View File
@@ -332,8 +332,8 @@ auto waybar::modules::Network::update() -> void {
getState(signal_strength_); getState(signal_strength_);
auto text = fmt::format( auto text = fmt::format(
fmt::runtime(format_), fmt::arg("essid", essid_), fmt::arg("signaldBm", signal_strength_dbm_), fmt::runtime(format_), fmt::arg("essid", essid_), fmt::arg("bssid", bssid_),
fmt::arg("signalStrength", signal_strength_), fmt::arg("signaldBm", signal_strength_dbm_), fmt::arg("signalStrength", signal_strength_),
fmt::arg("signalStrengthApp", signal_strength_app_), fmt::arg("ifname", ifname_), fmt::arg("signalStrengthApp", signal_strength_app_), fmt::arg("ifname", ifname_),
fmt::arg("netmask", netmask_), fmt::arg("ipaddr", ipaddr_), fmt::arg("gwaddr", gwaddr_), fmt::arg("netmask", netmask_), fmt::arg("ipaddr", ipaddr_), fmt::arg("gwaddr", gwaddr_),
fmt::arg("cidr", cidr_), fmt::arg("frequency", fmt::format("{:.1f}", frequency_)), 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()) { if (!tooltip_format.empty()) {
auto tooltip_text = fmt::format( 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("signaldBm", signal_strength_dbm_), fmt::arg("signalStrength", signal_strength_),
fmt::arg("signalStrengthApp", signal_strength_app_), fmt::arg("ifname", ifname_), fmt::arg("signalStrengthApp", signal_strength_app_), fmt::arg("ifname", ifname_),
fmt::arg("netmask", netmask_), fmt::arg("ipaddr", ipaddr_), fmt::arg("gwaddr", gwaddr_), fmt::arg("netmask", netmask_), fmt::arg("ipaddr", ipaddr_), fmt::arg("gwaddr", gwaddr_),
@@ -407,6 +407,7 @@ void waybar::modules::Network::clearIface() {
ifid_ = -1; ifid_ = -1;
ifname_.clear(); ifname_.clear();
essid_.clear(); essid_.clear();
bssid_.clear();
ipaddr_.clear(); ipaddr_.clear();
gwaddr_.clear(); gwaddr_.clear();
netmask_.clear(); netmask_.clear();
@@ -481,6 +482,7 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) {
} else { } else {
// clear state related to WiFi connection // clear state related to WiFi connection
net->essid_.clear(); net->essid_.clear();
net->bssid_.clear();
net->signal_strength_dbm_ = 0; net->signal_strength_dbm_ = 0;
net->signal_strength_ = 0; net->signal_strength_ = 0;
net->signal_strength_app_.clear(); net->signal_strength_app_.clear();
@@ -772,6 +774,7 @@ int waybar::modules::Network::handleScan(struct nl_msg *msg, void *data) {
net->parseEssid(bss); net->parseEssid(bss);
net->parseSignal(bss); net->parseSignal(bss);
net->parseFreq(bss); net->parseFreq(bss);
net->parseBssid(bss);
return NL_OK; 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) { bool waybar::modules::Network::associatedOrJoined(struct nlattr **bss) {
if (bss[NL80211_BSS_STATUS] == nullptr) { if (bss[NL80211_BSS_STATUS] == nullptr) {
return false; return false;
+1 -1
View File
@@ -106,7 +106,7 @@ auto waybar::modules::Pulseaudio::update() -> void {
} }
} else { } else {
label_.get_style_context()->remove_class("source-muted"); 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(); format_source = config_["format-source"].asString();
} }
} }
+2 -2
View File
@@ -14,14 +14,14 @@
template <> template <>
struct fmt::formatter<Glib::VariantBase> : formatter<std::string> { 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(); auto type = value.get_type_string();
/* Print only primitive (single character excluding 'v') and short complex types */ /* 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; return (type.length() == 1 && islower(type[0]) && type[0] != 'v') || value.get_size() <= 32;
} }
template <typename FormatContext> template <typename FormatContext>
auto format(const Glib::VariantBase& value, FormatContext& ctx) { auto format(const Glib::VariantBase& value, FormatContext& ctx) const {
if (is_printable(value)) { if (is_printable(value)) {
return formatter<std::string>::format(static_cast<std::string>(value.print()), ctx); return formatter<std::string>::format(static_cast<std::string>(value.print()), ctx);
} else { } else {
+3 -4
View File
@@ -67,10 +67,9 @@ gboolean Watcher::handleRegisterHost(Watcher* obj, GDBusMethodInvocation* invoca
} }
auto watch = gfWatchFind(obj->hosts_, bus_name, object_path); auto watch = gfWatchFind(obj->hosts_, bus_name, object_path);
if (watch != nullptr) { if (watch != nullptr) {
g_dbus_method_invocation_return_error( g_warning("Status Notifier Host with bus name '%s' and object path '%s' is already registered",
invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, bus_name, object_path);
"Status Notifier Host with bus name '%s' and object path '%s' is already registered", sn_watcher_complete_register_item(obj->watcher_, invocation);
bus_name, object_path);
return TRUE; return TRUE;
} }
watch = gfWatchNew(GF_WATCH_TYPE_HOST, service, bus_name, object_path, obj); watch = gfWatchNew(GF_WATCH_TYPE_HOST, service, bus_name, object_path, obj);
+20 -26
View File
@@ -11,15 +11,14 @@ namespace waybar::modules::sway {
// Helper function to assign a number to a workspace, just like sway. In fact // Helper function to assign a number to a workspace, just like sway. In fact
// this is taken quite verbatim from `sway/ipc-json.c`. // this is taken quite verbatim from `sway/ipc-json.c`.
int Workspaces::convertWorkspaceNameToNum(std::string name) { int Workspaces::convertWorkspaceNameToNum(std::string name) {
if (isdigit(name[0])) { if (isdigit(name[0]) != 0) {
errno = 0; errno = 0;
char *endptr = NULL; char *endptr = nullptr;
long long parsed_num = strtoll(name.c_str(), &endptr, 10); long long parsed_num = strtoll(name.c_str(), &endptr, 10);
if (errno != 0 || parsed_num > INT32_MAX || parsed_num < 0 || endptr == name.c_str()) { if (errno != 0 || parsed_num > INT32_MAX || parsed_num < 0 || endptr == name.c_str()) {
return -1; return -1;
} else {
return (int)parsed_num;
} }
return (int)parsed_num;
} }
return -1; return -1;
} }
@@ -47,7 +46,7 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value
bar_(bar), bar_(bar),
box_(bar.orientation, 0) { box_(bar.orientation, 0) {
if (config["format-icons"]["high-priority-named"].isArray()) { 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()); 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( m_windowRewriteRules = waybar::util::RegexCollection(
windowRewrite, m_windowRewriteDefault, 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"(["workspace"])");
ipc_.subscribe(R"(["window"])"); ipc_.subscribe(R"(["window"])");
ipc_.signal_event.connect(sigc::mem_fun(*this, &Workspaces::onEvent)); 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::copy(output["floating_nodes"].begin(), output["floating_nodes"].end(),
std::back_inserter(workspaces_)); 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) // adding persistent workspaces (as per the config file)
if (config_["persistent-workspaces"].isObject() || if (config_["persistent-workspaces"].isObject()) {
config_["persistent_workspaces"].isObject()) { const Json::Value &p_workspaces = config_["persistent-workspaces"];
const Json::Value &p_workspaces = config_["persistent-workspaces"].isObject()
? config_["persistent-workspaces"]
: config_["persistent_workspaces"];
const std::vector<std::string> p_workspaces_names = p_workspaces.getMemberNames(); const std::vector<std::string> p_workspaces_names = p_workspaces.getMemberNames();
for (const std::string &p_w_name : p_workspaces_names) { for (const std::string &p_w_name : p_workspaces_names) {
@@ -270,13 +261,17 @@ void Workspaces::updateWindows(const Json::Value &node, std::string &windows) {
node["name"].isString()) { node["name"].isString()) {
std::string title = g_markup_escape_text(node["name"].asString().c_str(), -1); std::string title = g_markup_escape_text(node["name"].asString().c_str(), -1);
std::string windowClass = node["app_id"].asString(); std::string windowClass = node["app_id"].asString();
std::string windowReprKey = fmt::format("class<{}> title<{}>", windowClass, title);
std::string window = m_windowRewriteRules.get(windowReprKey); // Only add window rewrites that can be looked up
// allow result to have formatting if (!windowClass.empty()) {
window = std::string windowReprKey = fmt::format("class<{}> title<{}>", windowClass, title);
fmt::format(fmt::runtime(window), fmt::arg("name", title), fmt::arg("class", windowClass)); std::string window = m_windowRewriteRules.get(windowReprKey);
windows.append(window); // allow result to have formatting
windows.append(m_formatWindowSeperator); 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"]) { for (const Json::Value &child : node["nodes"]) {
updateWindows(child, windows); updateWindows(child, windows);
@@ -422,7 +417,7 @@ std::string Workspaces::getIcon(const std::string &name, const Json::Value &node
} }
bool Workspaces::handleScroll(GdkEventScroll *e) { 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 * Ignore emulated scroll events on window
*/ */
@@ -472,8 +467,7 @@ bool Workspaces::handleScroll(GdkEventScroll *e) {
return true; return true;
} }
const std::string Workspaces::getCycleWorkspace(std::vector<Json::Value>::iterator it, std::string Workspaces::getCycleWorkspace(std::vector<Json::Value>::iterator it, bool prev) const {
bool prev) const {
if (prev && it == workspaces_.begin() && !config_["disable-scroll-wraparound"].asBool()) { if (prev && it == workspaces_.begin() && !config_["disable-scroll-wraparound"].asBool()) {
return (*(--workspaces_.end()))["name"].asString(); return (*(--workspaces_.end()))["name"].asString();
} }
+2 -1
View File
@@ -163,7 +163,8 @@ void waybar::modules::Wireplumber::onDefaultNodesApiChanged(waybar::modules::Wir
"[{}]: (onDefaultNodesApiChanged) - got the following default node: Node(name: {}, id: {})", "[{}]: (onDefaultNodesApiChanged) - got the following default node: Node(name: {}, id: {})",
self->name_, defaultNodeName, defaultNodeId); 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( spdlog::debug(
"[{}]: (onDefaultNodesApiChanged) - Default node has not changed. Node(name: {}, id: {}). " "[{}]: (onDefaultNodesApiChanged) - Default node has not changed. Node(name: {}, id: {}). "
"Ignoring.", "Ignoring.",
+18 -2
View File
@@ -387,6 +387,11 @@ void Task::handle_title(const char *title) {
hide_if_ignored(); 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() { void Task::hide_if_ignored() {
if (tbar_->ignore_list().count(app_id_) || tbar_->ignore_list().count(title_)) { if (tbar_->ignore_list().count(app_id_) || tbar_->ignore_list().count(title_)) {
ignored_ = true; ignored_ = true;
@@ -447,6 +452,13 @@ void Task::handle_app_id(const char *app_id) {
spdlog::debug("Couldn't find icon for {}", 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) { void Task::handle_output_enter(struct wl_output *output) {
if (ignored_) { if (ignored_) {
spdlog::debug("{} is ignored", repr()); 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))) { if (!button_visible_ && (tbar_->all_outputs() || tbar_->show_output(output))) {
/* The task entered the output of the current bar make the button visible */ /* 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); tbar_->add_button(button);
button.show(); button.show();
button_visible_ = true; button_visible_ = true;
@@ -553,9 +567,11 @@ bool Task::handle_clicked(GdkEventButton *bt) {
return true; return true;
else if (action == "activate") else if (action == "activate")
activate(); activate();
else if (action == "minimize") else if (action == "minimize") {
set_minimize_hint();
minimize(!minimized()); minimize(!minimized());
else if (action == "minimize-raise") { } else if (action == "minimize-raise") {
set_minimize_hint();
if (minimized()) if (minimized())
minimize(false); minimize(false);
else if (active()) else if (active())
+6
View File
@@ -144,6 +144,12 @@ void AudioBackend::sinkInfoCb(pa_context * /*context*/, const pa_sink_info *i, i
if (!backend->ignored_sinks_.empty()) { if (!backend->ignored_sinks_.empty()) {
for (const auto &ignored_sink : backend->ignored_sinks_) { for (const auto &ignored_sink : backend->ignored_sinks_) {
if (ignored_sink == i->description) { 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; return;
} }
} }
+7 -1
View File
@@ -43,8 +43,14 @@ std::string waybar::CssReloadHelper::findPath(const std::string& filename) {
} }
// File monitor does not work with symlinks, so resolve them // 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); result = std::filesystem::read_symlink(result);
// prevent infinite cycle
if (result == original) {
break;
}
} }
return result; return result;
+2 -4
View File
@@ -52,10 +52,8 @@ TEST_CASE_METHOD(IPCTestFixture, "XDGRuntimeDirExistsNoHyprDir", "[getSocketFold
REQUIRE(actualPath == expectedPath); 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"; std::string request = "test_request";
Json::Value jsonResponse = getSocket1JsonReply(request); CHECK_THROWS(getSocket1Reply(request));
REQUIRE(jsonResponse.isNull());
} }
@@ -19,4 +19,7 @@ class IPCMock : public IPCTestFixture {
public: public:
// Mock getSocket1Reply to return an empty string // Mock getSocket1Reply to return an empty string
static std::string getSocket1Reply(const std::string& rq) { return ""; } static std::string getSocket1Reply(const std::string& rq) { return ""; }
protected:
const char* instanceSig = "instance_sig";
}; };