Merge branch 'master' into feat-hyprland-submap-icon-handling

This commit is contained in:
szbmrk
2026-02-04 12:38:49 +01:00
committed by GitHub
92 changed files with 1357 additions and 1335 deletions
+3 -7
View File
@@ -11,12 +11,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
# TODO: bump to clang 19 release
# - uses: DoozyX/clang-format-lint-action@v0.18.2
- uses: DoozyX/clang-format-lint-action@558090054b3f39e3d6af24f0cd73b319535da809
- uses: RafikFarhad/clang-format-github-action@v6
name: clang-format
with:
source: "."
extensions: "hpp,h,cpp,c"
style: "file:.clang-format"
clangFormatVersion: 19
sources: "src/**/*.hpp,src/**/*.cpp"
style: "file"
+1
View File
@@ -51,3 +51,4 @@ result
result-*
.ccls-cache
_codeql_detected_source_root
+4
View File
@@ -151,6 +151,10 @@ Contributions welcome!<br>
Have fun :)<br>
The style guidelines are [Google's](https://google.github.io/styleguide/cppguide.html)
> [!CAUTION]
> Distributions of Waybar are only released on the [official GitHub page](https://github.com/Alexays/Waybar).<br/>
> Waybar does **not** have an official website. Do not trust any sites that claim to be official.
## License
Waybar is licensed under the MIT license. [See LICENSE for more information](https://github.com/Alexays/Waybar/blob/master/LICENSE).
+1
View File
@@ -0,0 +1 @@
.
Generated
+3 -3
View File
@@ -18,11 +18,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1766902085,
"narHash": "sha256-coBu0ONtFzlwwVBzmjacUQwj3G+lybcZ1oeNSQkgC0M=",
"lastModified": 1769461804,
"narHash": "sha256-msG8SU5WsBUfVVa/9RPLaymvi5bI8edTavbIq3vRlhI=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "c0b0e0fddf73fd517c3471e546c0df87a42d53f4",
"rev": "bfc1b8a4574108ceef22f02bafcf6611380c100d",
"type": "github"
},
"original": {
+1
View File
@@ -27,6 +27,7 @@ class Submap : public waybar::ALabel, public EventHandler {
util::JsonParser parser_;
std::string submap_;
std::string icon_;
std::string prev_submap_;
bool always_on_ = false;
std::string default_submap_ = "Default";
std::unordered_map<std::string, std::string> icons_;
+2 -2
View File
@@ -33,8 +33,8 @@ class Taskbar;
class Task {
public:
Task(const waybar::Bar &, const Json::Value &, Taskbar *,
struct zwlr_foreign_toplevel_handle_v1 *, struct wl_seat *);
Task(const waybar::Bar&, const Json::Value&, Taskbar*, struct zwlr_foreign_toplevel_handle_v1*,
struct wl_seat*);
~Task();
public:
+9 -2
View File
@@ -109,7 +109,7 @@ inline FILE* open(const std::string& cmd, int& pid, const std::string& output_na
::close(fd[0]);
dup2(fd[1], 1);
setpgid(child_pid, child_pid);
if (output_name != "") {
if (!output_name.empty()) {
setenv("WAYBAR_OUTPUT_NAME", output_name.c_str(), 1);
}
execlp("/bin/sh", "sh", "-c", cmd.c_str(), (char*)0);
@@ -138,7 +138,7 @@ inline struct res execNoRead(const std::string& cmd) {
return {WEXITSTATUS(stat), ""};
}
inline int32_t forkExec(const std::string& cmd) {
inline int32_t forkExec(const std::string& cmd, const std::string& output_name) {
if (cmd == "") return -1;
pid_t pid = fork();
@@ -157,6 +157,9 @@ inline int32_t forkExec(const std::string& cmd) {
err = pthread_sigmask(SIG_UNBLOCK, &mask, nullptr);
if (err != 0) spdlog::error("pthread_sigmask in forkExec failed: {}", strerror(err));
setpgid(pid, pid);
if (!output_name.empty()) {
setenv("WAYBAR_OUTPUT_NAME", output_name.c_str(), 1);
}
execl("/bin/sh", "sh", "-c", cmd.c_str(), (char*)0);
exit(0);
} else {
@@ -169,4 +172,8 @@ inline int32_t forkExec(const std::string& cmd) {
return pid;
}
inline int32_t forkExec(const std::string& cmd) {
return forkExec(cmd, "");
}
} // namespace waybar::util::command
+4 -3
View File
@@ -30,15 +30,16 @@ class JsonParser {
std::istringstream jsonStream(modifiedJsonStr);
std::string errs;
if (!Json::parseFromStream(m_readerBuilder, jsonStream, &root, &errs)) {
// Use local CharReaderBuilder for thread safety - the IPC singleton's
// parser can be called concurrently from multiple module threads
Json::CharReaderBuilder readerBuilder;
if (!Json::parseFromStream(readerBuilder, jsonStream, &root, &errs)) {
throw std::runtime_error("Error parsing JSON: " + errs);
}
return root;
}
private:
Json::CharReaderBuilder m_readerBuilder;
static std::string replaceHexadecimalEscape(const std::string& str) {
static std::regex re("\\\\x");
return std::regex_replace(str, re, "\\u00");
+1 -1
View File
@@ -190,7 +190,7 @@ bool waybar::ALabel::handleToggle(GdkEventButton* const& e) {
}
void ALabel::handleGtkMenuEvent(GtkMenuItem* /*menuitem*/, gpointer data) {
waybar::util::command::res res = waybar::util::command::exec((char*)data, "GtkMenu");
waybar::util::command::forkExec((char*)data, "GtkMenu");
}
std::string ALabel::getState(uint8_t value, bool lesser) {
+2 -1
View File
@@ -229,7 +229,8 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config)
gtk_layer_init_for_window(gtk_window);
gtk_layer_set_keyboard_mode(gtk_window, GTK_LAYER_SHELL_KEYBOARD_MODE_NONE);
gtk_layer_set_monitor(gtk_window, output->monitor->gobj());
gtk_layer_set_namespace(gtk_window, "waybar");
gtk_layer_set_namespace(gtk_window,
config["name"].isString() ? config["name"].asCString() : "waybar");
gtk_layer_set_margin(gtk_window, GTK_LAYER_SHELL_EDGE_LEFT, margins_.left);
gtk_layer_set_margin(gtk_window, GTK_LAYER_SHELL_EDGE_RIGHT, margins_.right);
+1 -1
View File
@@ -264,7 +264,7 @@ auto waybar::modules::Bluetooth::update() -> void {
device_enumerate_.erase(0, 1);
}
}
label_.set_tooltip_text(fmt::format(
label_.set_tooltip_markup(fmt::format(
fmt::runtime(tooltip_format), fmt::arg("status", state_),
fmt::arg("num_connections", connected_devices_.size()),
fmt::arg("controller_address", cur_controller_ ? cur_controller_->address : "null"),
+4 -2
View File
@@ -47,9 +47,11 @@ auto waybar::modules::cava::Cava::onSilence() -> void {
if (label_.get_style_context()->has_class("updated"))
label_.get_style_context()->remove_class("updated");
if (hide_on_silence_)
if (hide_on_silence_) {
// Clear the label markup before hiding to prevent GTK from rendering a NULL Pango layout
label_.set_markup("");
label_.hide();
else if (config_["format_silent"].isString())
} else if (config_["format_silent"].isString())
label_.set_markup(format_silent_);
silence_ = true;
label_.get_style_context()->add_class("silent");
+3 -3
View File
@@ -183,10 +183,10 @@ void waybar::modules::cava::CavaBackend::loadConfig() {
prm_.inAtty = 0;
auto const output{prm_.output};
// prm_.output = ::cava::output_method::OUTPUT_RAW;
if (config_["data_format"].isString()) {
if (prm_.data_format) free(prm_.data_format);
prm_.data_format = strdup(config_["data_format"].asString().c_str());
}
// Default to ascii for format-icons output; allow user override
prm_.data_format = strdup(
config_["data_format"].isString() ? config_["data_format"].asString().c_str() : "ascii");
if (config_["raw_target"].isString()) {
if (prm_.raw_target) free(prm_.raw_target);
prm_.raw_target = strdup(config_["raw_target"].asString().c_str());
+1 -5
View File
@@ -136,7 +136,7 @@ void waybar::modules::Custom::waitingWorker() {
}
void waybar::modules::Custom::refresh(int sig) {
if (sig == SIGRTMIN + config_["signal"].asInt()) {
if (config_["signal"].isInt() && sig == SIGRTMIN + config_["signal"].asInt()) {
thread_.wake_up();
}
}
@@ -187,15 +187,11 @@ auto waybar::modules::Custom::update() -> void {
fmt::arg("icon", getIcon(percentage_, alt_)), fmt::arg("percentage", percentage_));
label_.set_tooltip_markup(tooltip);
} else if (text_ == tooltip_) {
if (label_.get_tooltip_markup() != str) {
label_.set_tooltip_markup(str);
}
} else {
if (label_.get_tooltip_markup() != tooltip_) {
label_.set_tooltip_markup(tooltip_);
}
}
}
auto style = label_.get_style_context();
auto classes = style->list_classes();
for (auto const& c : classes) {
+2 -2
View File
@@ -76,8 +76,8 @@ static void handle_global(void *data, struct wl_registry *registry, uint32_t nam
}
if (std::strcmp(interface, wl_seat_interface.name) == 0) {
version = std::min<uint32_t>(version, 1);
static_cast<Tags *>(data)->seat_ = static_cast<struct wl_seat *>(
wl_registry_bind(registry, name, &wl_seat_interface, version));
static_cast<Tags*>(data)->seat_ =
static_cast<struct wl_seat*>(wl_registry_bind(registry, name, &wl_seat_interface, version));
}
}
+11 -6
View File
@@ -56,6 +56,17 @@ auto Submap::parseConfig(const Json::Value& config) -> void {
auto Submap::update() -> void {
std::lock_guard<std::mutex> lg(mutex_);
// Handle style class changes
if (!prev_submap_.empty()) {
label_.get_style_context()->remove_class(prev_submap_);
}
if (!submap_.empty()) {
label_.get_style_context()->add_class(submap_);
}
prev_submap_ = submap_;
if (submap_.empty()) {
event_box_.hide();
} else {
@@ -79,10 +90,6 @@ void Submap::onEvent(const std::string& ev) {
auto submapName = ev.substr(ev.find_first_of('>') + 2);
if (!submap_.empty()) {
label_.get_style_context()->remove_class(submap_);
}
submap_ = submapName;
if (!icons_[submap_].empty()) {
@@ -95,8 +102,6 @@ void Submap::onEvent(const std::string& ev) {
submap_ = default_submap_;
}
label_.get_style_context()->add_class(submap_);
spdlog::debug("hyprland submap onevent with {}", submap_);
dp.emit();
+2 -2
View File
@@ -45,6 +45,8 @@ Window::~Window() {
auto Window::update() -> void {
std::shared_lock<std::shared_mutex> windowIpcShareLock(windowIpcSmtx);
queryActiveWorkspace();
std::string windowName = waybar::util::sanitize_string(workspace_.last_window_title);
std::string windowAddress = workspace_.last_window;
@@ -236,8 +238,6 @@ void Window::queryActiveWorkspace() {
}
void Window::onEvent(const std::string& ev) {
queryActiveWorkspace();
dp.emit();
}
+2 -3
View File
@@ -39,6 +39,8 @@ WindowCount::~WindowCount() {
auto WindowCount::update() -> void {
std::lock_guard<std::mutex> lg(mutex_);
queryActiveWorkspace();
std::string format = config_["format"].asString();
std::string formatEmpty = config_["format-empty"].asString();
std::string formatWindowed = config_["format-windowed"].asString();
@@ -116,8 +118,6 @@ auto WindowCount::Workspace::parse(const Json::Value& value) -> WindowCount::Wor
}
void WindowCount::queryActiveWorkspace() {
std::lock_guard<std::mutex> lg(mutex_);
if (separateOutputs_) {
workspace_ = getActiveWorkspace(this->bar_.output->name);
} else {
@@ -126,7 +126,6 @@ void WindowCount::queryActiveWorkspace() {
}
void WindowCount::onEvent(const std::string& ev) {
queryActiveWorkspace();
dp.emit();
}
+1 -1
View File
@@ -939,7 +939,7 @@ void Workspaces::sortWorkspaces() {
case SortMethod::NUMBER:
try {
return std::stoi(a->name()) < std::stoi(b->name());
} catch (const std::invalid_argument &) {
} catch (const std::exception& e) {
// Handle the exception if necessary.
break;
}
+1 -1
View File
@@ -41,7 +41,7 @@ void waybar::modules::Image::delayWorker() {
}
void waybar::modules::Image::refresh(int sig) {
if (sig == SIGRTMIN + config_["signal"].asInt()) {
if (config_["signal"].isInt() && sig == SIGRTMIN + config_["signal"].asInt()) {
thread_.wake_up();
}
}
+2 -2
View File
@@ -86,8 +86,8 @@ static void handle_global(void *data, struct wl_registry *registry, uint32_t nam
if (std::strcmp(interface, wl_seat_interface.name) == 0) {
version = std::min<uint32_t>(version, 1);
static_cast<Layout *>(data)->seat_ = static_cast<struct wl_seat *>(
wl_registry_bind(registry, name, &wl_seat_interface, version));
static_cast<Layout*>(data)->seat_ =
static_cast<struct wl_seat*>(wl_registry_bind(registry, name, &wl_seat_interface, version));
}
}
+2 -2
View File
@@ -46,8 +46,8 @@ static void handle_global(void *data, struct wl_registry *registry, uint32_t nam
wl_registry_bind(registry, name, &zriver_status_manager_v1_interface, version));
} else if (std::strcmp(interface, wl_seat_interface.name) == 0) {
version = std::min<uint32_t>(version, 1);
static_cast<Mode *>(data)->seat_ = static_cast<struct wl_seat *>(
wl_registry_bind(registry, name, &wl_seat_interface, version));
static_cast<Mode*>(data)->seat_ =
static_cast<struct wl_seat*>(wl_registry_bind(registry, name, &wl_seat_interface, version));
}
}
+2 -2
View File
@@ -69,8 +69,8 @@ static void handle_global(void *data, struct wl_registry *registry, uint32_t nam
if (std::strcmp(interface, wl_seat_interface.name) == 0) {
version = std::min(version, 1u);
static_cast<Tags *>(data)->seat_ = static_cast<struct wl_seat *>(
wl_registry_bind(registry, name, &wl_seat_interface, version));
static_cast<Tags*>(data)->seat_ =
static_cast<struct wl_seat*>(wl_registry_bind(registry, name, &wl_seat_interface, version));
}
}
+2 -2
View File
@@ -46,8 +46,8 @@ static void handle_global(void *data, struct wl_registry *registry, uint32_t nam
if (std::strcmp(interface, wl_seat_interface.name) == 0) {
version = std::min<uint32_t>(version, 1);
static_cast<Window *>(data)->seat_ = static_cast<struct wl_seat *>(
wl_registry_bind(registry, name, &wl_seat_interface, version));
static_cast<Window*>(data)->seat_ =
static_cast<struct wl_seat*>(wl_registry_bind(registry, name, &wl_seat_interface, version));
}
}
+8
View File
@@ -79,6 +79,10 @@ Item::~Item() {
this->gtk_menu->popdown();
this->gtk_menu->detach();
}
if (this->dbus_menu != nullptr) {
g_object_weak_unref(G_OBJECT(this->dbus_menu), (GWeakNotify)onMenuDestroyed, this);
this->dbus_menu = nullptr;
}
}
bool Item::handleMouseEnter(GdkEventCrossing* const& e) {
@@ -233,9 +237,13 @@ void Item::setCustomIcon(const std::string& id) {
std::string custom_icon = IconManager::instance().getIconForApp(id);
if (!custom_icon.empty()) {
if (std::filesystem::exists(custom_icon)) {
try {
Glib::RefPtr<Gdk::Pixbuf> custom_pixbuf = Gdk::Pixbuf::create_from_file(custom_icon);
icon_name = ""; // icon_name has priority over pixmap
icon_pixmap = custom_pixbuf;
} catch (const Glib::Error& e) {
spdlog::error("Failed to load custom icon {}: {}", custom_icon, e.what());
}
} else { // if file doesn't exist it's most likely an icon_name
icon_name = custom_icon;
}