Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add Hyprland/windowcount module #3544

Open
wants to merge 18 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions include/modules/hyprland/windowcount.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#pragma once

#include <fmt/format.h>

#include <string>

#include "AAppIconLabel.hpp"
#include "bar.hpp"
#include "modules/hyprland/backend.hpp"
#include "util/json.hpp"

namespace waybar::modules::hyprland {

class WindowCount : public waybar::AAppIconLabel, public EventHandler {
public:
WindowCount(const std::string&, const waybar::Bar&, const Json::Value&);
~WindowCount() override;

auto update() -> void override;

private:
struct Workspace {
int id;
int windows;
bool hasfullscreen;
static auto parse(const Json::Value& value) -> Workspace;
};

static auto getActiveWorkspace(const std::string&) -> Workspace;
static auto getActiveWorkspace() -> Workspace;
void onEvent(const std::string& ev) override;
void queryActiveWorkspace();
void setClass(const std::string&, bool enable);

bool separateOutputs_;
std::mutex mutex_;
const Bar& bar_;
Workspace workspace_;
};

} // namespace waybar::modules::hyprland
46 changes: 46 additions & 0 deletions man/waybar-hyprland-windowcount.5.scd
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
waybar-hyprland-windowcount(5)

# NAME

waybar - hyprland window count module

# DESCRIPTION

The *windowcount* module displays the number of windows in the current Hyprland workspace.

# CONFIGURATION

Addressed by *hyprland/windowcount*

*format*: ++
typeof: string ++
default: {} ++
The format for how information should be displayed. On {} the current workspace window count is displayed.

*format-empty*: ++
typeof: string ++
Override the format when the workspace contains no windows window

*format-windowed*: ++
typeof: string ++
Override the format when the workspace contains no fullscreen windows

*format-fullscreen*: ++
typeof: string ++
Override the format when the workspace contains a fullscreen window

*separate-outputs*: ++
typeof: bool ++
default: true ++
Show the active workspace window count of the monitor the bar belongs to, instead of the focused workspace.

# STYLE

- *#windowcount*

The following classes are applied to the entire Waybar rather than just the
windowcount widget:

- *window#waybar.empty* When no windows are in the workspace
- *window#waybar.fullscreen* When there is a fullscreen window in the workspace;
useful with Hyprland's *fullscreen, 1* mode
1 change: 1 addition & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,7 @@ if true
'src/modules/hyprland/language.cpp',
'src/modules/hyprland/submap.cpp',
'src/modules/hyprland/window.cpp',
'src/modules/hyprland/windowcount.cpp',
'src/modules/hyprland/workspace.cpp',
'src/modules/hyprland/workspaces.cpp',
'src/modules/hyprland/windowcreationpayload.cpp',
Expand Down
4 changes: 4 additions & 0 deletions src/factory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include "modules/hyprland/language.hpp"
#include "modules/hyprland/submap.hpp"
#include "modules/hyprland/window.hpp"
#include "modules/hyprland/windowcount.hpp"
#include "modules/hyprland/workspaces.hpp"
#endif
#ifdef HAVE_NIRI
Expand Down Expand Up @@ -201,6 +202,9 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name,
if (ref == "hyprland/window") {
return new waybar::modules::hyprland::Window(id, bar_, config_[name]);
}
if (ref == "hyprland/windowcount") {
return new waybar::modules::hyprland::WindowCount(id, bar_, config_[name]);
}
if (ref == "hyprland/language") {
return new waybar::modules::hyprland::Language(id, bar_, config_[name]);
}
Expand Down
142 changes: 142 additions & 0 deletions src/modules/hyprland/windowcount.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
#include "modules/hyprland/windowcount.hpp"

#include <glibmm/fileutils.h>
#include <glibmm/keyfile.h>
#include <glibmm/miscutils.h>
#include <spdlog/spdlog.h>

#include <algorithm>
#include <vector>

#include "modules/hyprland/backend.hpp"
#include "util/sanitize_str.hpp"

namespace waybar::modules::hyprland {

WindowCount::WindowCount(const std::string& id, const Bar& bar, const Json::Value& config)
: AAppIconLabel(config, "windowcount", id, "{count}", 0, true), bar_(bar) {
modulesReady = true;
separateOutputs_ =
config.isMember("separate-outputs") ? config["separate-outputs"].asBool() : true;

if (!gIPC) {
gIPC = std::make_unique<IPC>();
}

queryActiveWorkspace();
update();
dp.emit();

// register for hyprland ipc
gIPC->registerForIPC("fullscreen", this);
gIPC->registerForIPC("workspace", this);
gIPC->registerForIPC("focusedmon", this);
gIPC->registerForIPC("openwindow", this);
gIPC->registerForIPC("closewindow", this);
gIPC->registerForIPC("movewindow", this);
}

WindowCount::~WindowCount() {
gIPC->unregisterForIPC(this);
// wait for possible event handler to finish
std::lock_guard<std::mutex> lg(mutex_);
}

auto WindowCount::update() -> void {
std::lock_guard<std::mutex> lg(mutex_);

std::string format = config_["format"].asString();
std::string formatEmpty = config_["format-empty"].asString();
std::string formatWindowed = config_["format-windowed"].asString();
std::string formatFullscreen = config_["format-fullscreen"].asString();

setClass("empty", workspace_.windows == 0);
setClass("fullscreen", workspace_.hasfullscreen);

if (workspace_.windows == 0 && !formatEmpty.empty()) {
label_.set_markup(fmt::format(fmt::runtime(formatEmpty), workspace_.windows));
} else if (!workspace_.hasfullscreen && !formatWindowed.empty()) {
label_.set_markup(fmt::format(fmt::runtime(formatWindowed), workspace_.windows));
} else if (workspace_.hasfullscreen && !formatFullscreen.empty()) {
label_.set_markup(fmt::format(fmt::runtime(formatFullscreen), workspace_.windows));
} else if (!format.empty()) {
label_.set_markup(fmt::format(fmt::runtime(format), workspace_.windows));
} else {
label_.set_text(fmt::format("{}", workspace_.windows));
}

label_.show();
AAppIconLabel::update();
}

auto WindowCount::getActiveWorkspace() -> Workspace {
const auto workspace = gIPC->getSocket1JsonReply("activeworkspace");

if (workspace.isObject()) {
return Workspace::parse(workspace);
}

return {};
}

auto WindowCount::getActiveWorkspace(const std::string& monitorName) -> Workspace {
const auto monitors = gIPC->getSocket1JsonReply("monitors");
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, false};
}
const int id = (*monitor)["activeWorkspace"]["id"].asInt();

const auto workspaces = gIPC->getSocket1JsonReply("workspaces");
if (workspaces.isArray()) {
auto workspace = std::find_if(workspaces.begin(), workspaces.end(),
[&](Json::Value workspace) { return workspace["id"] == id; });
if (workspace == std::end(workspaces)) {
spdlog::warn("No workspace with id {}", id);
return Workspace{-1, 0, false};
}
return Workspace::parse(*workspace);
};
};

return {};
}

auto WindowCount::Workspace::parse(const Json::Value& value) -> WindowCount::Workspace {
return Workspace{
value["id"].asInt(),
value["windows"].asInt(),
value["hasfullscreen"].asBool(),
};
}

void WindowCount::queryActiveWorkspace() {
std::lock_guard<std::mutex> lg(mutex_);

if (separateOutputs_) {
workspace_ = getActiveWorkspace(this->bar_.output->name);
} else {
workspace_ = getActiveWorkspace();
}
}

void WindowCount::onEvent(const std::string& ev) {
queryActiveWorkspace();
dp.emit();
}

void WindowCount::setClass(const std::string& classname, bool enable) {
if (enable) {
if (!bar_.window.get_style_context()->has_class(classname)) {
bar_.window.get_style_context()->add_class(classname);
}
} else {
bar_.window.get_style_context()->remove_class(classname);
}
}

} // namespace waybar::modules::hyprland