Merge branch 'Alexays:master' into clock_fix
This commit is contained in:
commit
fbd62e0071
|
@ -16,6 +16,7 @@
|
||||||
inherit (nixpkgs) lib;
|
inherit (nixpkgs) lib;
|
||||||
genSystems = lib.genAttrs [
|
genSystems = lib.genAttrs [
|
||||||
"x86_64-linux"
|
"x86_64-linux"
|
||||||
|
"aarch64-linux"
|
||||||
];
|
];
|
||||||
|
|
||||||
pkgsFor = genSystems (system:
|
pkgsFor = genSystems (system:
|
||||||
|
|
|
@ -22,6 +22,7 @@ class Custom : public ALabel {
|
||||||
private:
|
private:
|
||||||
void delayWorker();
|
void delayWorker();
|
||||||
void continuousWorker();
|
void continuousWorker();
|
||||||
|
void waitingWorker();
|
||||||
void parseOutputRaw();
|
void parseOutputRaw();
|
||||||
void parseOutputJson();
|
void parseOutputJson();
|
||||||
void handleEvent();
|
void handleEvent();
|
||||||
|
|
|
@ -2,9 +2,13 @@
|
||||||
|
|
||||||
#include <gtkmm/button.h>
|
#include <gtkmm/button.h>
|
||||||
#include <gtkmm/label.h>
|
#include <gtkmm/label.h>
|
||||||
|
#include <json/value.h>
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
@ -13,13 +17,15 @@
|
||||||
#include "modules/hyprland/backend.hpp"
|
#include "modules/hyprland/backend.hpp"
|
||||||
#include "util/enum.hpp"
|
#include "util/enum.hpp"
|
||||||
|
|
||||||
|
using WindowAddress = std::string;
|
||||||
namespace waybar::modules::hyprland {
|
namespace waybar::modules::hyprland {
|
||||||
|
|
||||||
class Workspaces;
|
class Workspaces;
|
||||||
|
|
||||||
class Workspace {
|
class Workspace {
|
||||||
public:
|
public:
|
||||||
explicit Workspace(const Json::Value& workspace_data, Workspaces& workspace_manager);
|
explicit Workspace(const Json::Value& workspace_data, Workspaces& workspace_manager,
|
||||||
|
const Json::Value& clients_json = Json::Value::nullRef);
|
||||||
std::string& select_icon(std::map<std::string, std::string>& icons_map);
|
std::string& select_icon(std::map<std::string, std::string>& icons_map);
|
||||||
Gtk::Button& button() { return button_; };
|
Gtk::Button& button() { return button_; };
|
||||||
|
|
||||||
|
@ -40,6 +46,16 @@ class Workspace {
|
||||||
void set_visible(bool value = true) { is_visible_ = value; };
|
void set_visible(bool value = true) { is_visible_ = value; };
|
||||||
void set_windows(uint value) { windows_ = value; };
|
void set_windows(uint value) { windows_ = value; };
|
||||||
void set_name(std::string value) { name_ = value; };
|
void set_name(std::string value) { name_ = value; };
|
||||||
|
bool contains_window(WindowAddress addr) { return window_map_.contains(addr); }
|
||||||
|
void insert_window(WindowAddress addr, std::string window_repr);
|
||||||
|
std::string remove_window(WindowAddress addr);
|
||||||
|
void initialize_window_map(const Json::Value& clients_data);
|
||||||
|
|
||||||
|
bool on_window_opened(WindowAddress& addr, std::string& workspace_name, std::string window_repr);
|
||||||
|
bool on_window_opened(WindowAddress& addr, std::string& workspace_name, std::string& window_class,
|
||||||
|
std::string& window_title);
|
||||||
|
|
||||||
|
std::optional<std::string> on_window_closed(WindowAddress& addr);
|
||||||
|
|
||||||
void update(const std::string& format, const std::string& icon);
|
void update(const std::string& format, const std::string& icon);
|
||||||
|
|
||||||
|
@ -56,6 +72,8 @@ class Workspace {
|
||||||
bool is_urgent_ = false;
|
bool is_urgent_ = false;
|
||||||
bool is_visible_ = false;
|
bool is_visible_ = false;
|
||||||
|
|
||||||
|
std::map<WindowAddress, std::string> window_map_;
|
||||||
|
|
||||||
Gtk::Button button_;
|
Gtk::Button button_;
|
||||||
Gtk::Box content_;
|
Gtk::Box content_;
|
||||||
Gtk::Label label_;
|
Gtk::Label label_;
|
||||||
|
@ -74,16 +92,25 @@ class Workspaces : public AModule, public EventHandler {
|
||||||
|
|
||||||
auto get_bar_output() const -> std::string { return bar_.output->name; }
|
auto get_bar_output() const -> std::string { return bar_.output->name; }
|
||||||
|
|
||||||
|
std::string get_rewrite(std::string window_class);
|
||||||
|
std::string& get_window_separator() { return format_window_separator_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onEvent(const std::string&) override;
|
void onEvent(const std::string&) override;
|
||||||
void update_window_count();
|
void update_window_count();
|
||||||
|
void initialize_window_maps();
|
||||||
void sort_workspaces();
|
void sort_workspaces();
|
||||||
void create_workspace(Json::Value& value);
|
void create_workspace(Json::Value& workspace_data,
|
||||||
|
const Json::Value& clients_data = Json::Value::nullRef);
|
||||||
void remove_workspace(std::string name);
|
void remove_workspace(std::string name);
|
||||||
void set_urgent_workspace(std::string windowaddress);
|
void set_urgent_workspace(std::string windowaddress);
|
||||||
void parse_config(const Json::Value& config);
|
void parse_config(const Json::Value& config);
|
||||||
void register_ipc();
|
void register_ipc();
|
||||||
|
|
||||||
|
void on_window_opened(std::string payload);
|
||||||
|
void on_window_closed(std::string payload);
|
||||||
|
void on_window_moved(std::string payload);
|
||||||
|
|
||||||
bool all_outputs_ = false;
|
bool all_outputs_ = false;
|
||||||
bool show_special_ = false;
|
bool show_special_ = false;
|
||||||
bool active_only_ = false;
|
bool active_only_ = false;
|
||||||
|
@ -103,6 +130,10 @@ class Workspaces : public AModule, public EventHandler {
|
||||||
|
|
||||||
std::string format_;
|
std::string format_;
|
||||||
std::map<std::string, std::string> icons_map_;
|
std::map<std::string, std::string> icons_map_;
|
||||||
|
Json::Value window_rewrite_rules_;
|
||||||
|
std::map<std::string, std::string> regex_cache_;
|
||||||
|
std::string format_window_separator_;
|
||||||
|
std::string window_rewrite_default_;
|
||||||
bool with_icon_;
|
bool with_icon_;
|
||||||
uint64_t monitor_id_;
|
uint64_t monitor_id_;
|
||||||
std::string active_workspace_name_;
|
std::string active_workspace_name_;
|
||||||
|
|
|
@ -5,4 +5,6 @@
|
||||||
|
|
||||||
namespace waybar::util {
|
namespace waybar::util {
|
||||||
std::string rewriteString(const std::string&, const Json::Value&);
|
std::string rewriteString(const std::string&, const Json::Value&);
|
||||||
}
|
std::string rewriteStringOnce(const std::string& value, const Json::Value& rules,
|
||||||
|
bool& matched_any);
|
||||||
|
} // namespace waybar::util
|
||||||
|
|
|
@ -58,6 +58,12 @@ class SleeperThread {
|
||||||
|
|
||||||
bool isRunning() const { return do_run_; }
|
bool isRunning() const { return do_run_; }
|
||||||
|
|
||||||
|
auto sleep() {
|
||||||
|
std::unique_lock lk(mutex_);
|
||||||
|
CancellationGuard cancel_lock;
|
||||||
|
return condvar_.wait(lk);
|
||||||
|
}
|
||||||
|
|
||||||
auto sleep_for(std::chrono::system_clock::duration dur) {
|
auto sleep_for(std::chrono::system_clock::duration dur) {
|
||||||
std::unique_lock lk(mutex_);
|
std::unique_lock lk(mutex_);
|
||||||
CancellationGuard cancel_lock;
|
CancellationGuard cancel_lock;
|
||||||
|
|
|
@ -100,6 +100,11 @@ The *battery* module displays the current capacity and state (eg. charging) of y
|
||||||
default: true ++
|
default: true ++
|
||||||
Option to disable tooltip on hover.
|
Option to disable tooltip on hover.
|
||||||
|
|
||||||
|
*bat-compatibility*: ++
|
||||||
|
typeof: bool ++
|
||||||
|
default: false ++
|
||||||
|
Option to enable battery compatibility if not detected.
|
||||||
|
|
||||||
# FORMAT REPLACEMENTS
|
# FORMAT REPLACEMENTS
|
||||||
|
|
||||||
*{capacity}*: Capacity in percentage
|
*{capacity}*: Capacity in percentage
|
||||||
|
|
|
@ -34,7 +34,8 @@ Addressed by *custom/<name>*
|
||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
The interval (in seconds) in which the information gets polled. ++
|
The interval (in seconds) in which the information gets polled. ++
|
||||||
Use *once* if you want to execute the module only on startup. ++
|
Use *once* if you want to execute the module only on startup. ++
|
||||||
You can update it manually with a signal. If no *interval* is defined, it is assumed that the out script loops it self.
|
You can update it manually with a signal. If no *interval* or *signal* is defined, it is assumed that the out script loops it self. ++
|
||||||
|
If a *signal* is defined then the script will run once on startup and will will only update with a signal.
|
||||||
|
|
||||||
*restart-interval*: ++
|
*restart-interval*: ++
|
||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
|
@ -45,7 +46,8 @@ Addressed by *custom/<name>*
|
||||||
*signal*: ++
|
*signal*: ++
|
||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
The signal number used to update the module. ++
|
The signal number used to update the module. ++
|
||||||
The number is valid between 1 and N, where *SIGRTMIN+N* = *SIGRTMAX*.
|
The number is valid between 1 and N, where *SIGRTMIN+N* = *SIGRTMAX*. ++
|
||||||
|
If no interval is defined then a signal will be the only way to update the module.
|
||||||
|
|
||||||
*format*: ++
|
*format*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
|
|
|
@ -21,6 +21,21 @@ Addressed by *hyprland/workspaces*
|
||||||
typeof: array ++
|
typeof: array ++
|
||||||
Based on the workspace id and state, the corresponding icon gets selected. See *icons*.
|
Based on the workspace id and state, the corresponding icon gets selected. See *icons*.
|
||||||
|
|
||||||
|
*window-rewrite*: ++
|
||||||
|
typeof: object ++
|
||||||
|
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.
|
||||||
|
|
||||||
|
*window-rewrite-default*:
|
||||||
|
typeof: string ++
|
||||||
|
default: "?" ++
|
||||||
|
The default method of representation for a workspace's window. This will be used for windows whose classes do not match any of the rules in *window-rewrite*.
|
||||||
|
|
||||||
|
*format-window-separator*: ++
|
||||||
|
typeof: string ++
|
||||||
|
default: " " ++
|
||||||
|
The separator to be used between windows in a workspace.
|
||||||
|
|
||||||
*show-special*: ++
|
*show-special*: ++
|
||||||
typeof: bool ++
|
typeof: bool ++
|
||||||
default: false ++
|
default: false ++
|
||||||
|
@ -103,6 +118,19 @@ Additional to workspace name matching, the following *format-icons* can be set.
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
"hyprland/workspaces": {
|
||||||
|
"format": "{name}\n{windows}",
|
||||||
|
"format-window-separator": "\n",
|
||||||
|
"window-rewrite-default": "",
|
||||||
|
"window-rewrite": {
|
||||||
|
"firefox": "",
|
||||||
|
"foot": "",
|
||||||
|
"code": "",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
# Style
|
# Style
|
||||||
|
|
||||||
- *#workspaces*
|
- *#workspaces*
|
||||||
|
|
|
@ -244,7 +244,9 @@ void waybar::modules::Backlight::upsert_device(ForwardIt first, ForwardIt last,
|
||||||
check_nn(name);
|
check_nn(name);
|
||||||
|
|
||||||
const char *actual_brightness_attr =
|
const char *actual_brightness_attr =
|
||||||
strncmp(name, "amdgpu_bl", 9) == 0 ? "brightness" : "actual_brightness";
|
strncmp(name, "amdgpu_bl", 9) == 0 || strcmp(name, "apple-panel-bl") == 0
|
||||||
|
? "brightness"
|
||||||
|
: "actual_brightness";
|
||||||
|
|
||||||
const char *actual = udev_device_get_sysattr_value(dev, actual_brightness_attr);
|
const char *actual = udev_device_get_sysattr_value(dev, actual_brightness_attr);
|
||||||
const char *max = udev_device_get_sysattr_value(dev, "max_brightness");
|
const char *max = udev_device_get_sysattr_value(dev, "max_brightness");
|
||||||
|
|
|
@ -100,9 +100,11 @@ void waybar::modules::Battery::refreshBatteries() {
|
||||||
}
|
}
|
||||||
auto dir_name = node.path().filename();
|
auto dir_name = node.path().filename();
|
||||||
auto bat_defined = config_["bat"].isString();
|
auto bat_defined = config_["bat"].isString();
|
||||||
|
bool bat_compatibility = config_["bat-compatibility"].asBool();
|
||||||
if (((bat_defined && dir_name == config_["bat"].asString()) || !bat_defined) &&
|
if (((bat_defined && dir_name == config_["bat"].asString()) || !bat_defined) &&
|
||||||
(fs::exists(node.path() / "capacity") || fs::exists(node.path() / "charge_now")) &&
|
(fs::exists(node.path() / "capacity") || fs::exists(node.path() / "charge_now")) &&
|
||||||
fs::exists(node.path() / "uevent") && fs::exists(node.path() / "status") &&
|
fs::exists(node.path() / "uevent") &&
|
||||||
|
(fs::exists(node.path() / "status") || bat_compatibility) &&
|
||||||
fs::exists(node.path() / "type")) {
|
fs::exists(node.path() / "type")) {
|
||||||
std::string type;
|
std::string type;
|
||||||
std::ifstream(node.path() / "type") >> type;
|
std::ifstream(node.path() / "type") >> type;
|
||||||
|
@ -252,7 +254,13 @@ const std::tuple<uint8_t, float, std::string, float> waybar::modules::Battery::g
|
||||||
for (auto const& item : batteries_) {
|
for (auto const& item : batteries_) {
|
||||||
auto bat = item.first;
|
auto bat = item.first;
|
||||||
std::string _status;
|
std::string _status;
|
||||||
std::getline(std::ifstream(bat / "status"), _status);
|
|
||||||
|
/* Check for adapter status if battery is not available */
|
||||||
|
if (!std::ifstream(bat / "status")) {
|
||||||
|
std::getline(std::ifstream(adapter_ / "status"), _status);
|
||||||
|
} else {
|
||||||
|
std::getline(std::ifstream(bat / "status"), _status);
|
||||||
|
}
|
||||||
|
|
||||||
// Some battery will report current and charge in μA/μAh.
|
// Some battery will report current and charge in μA/μAh.
|
||||||
// Scale these by the voltage to get μW/μWh.
|
// Scale these by the voltage to get μW/μWh.
|
||||||
|
|
|
@ -11,7 +11,9 @@ waybar::modules::Custom::Custom(const std::string& name, const std::string& id,
|
||||||
fp_(nullptr),
|
fp_(nullptr),
|
||||||
pid_(-1) {
|
pid_(-1) {
|
||||||
dp.emit();
|
dp.emit();
|
||||||
if (interval_.count() > 0) {
|
if (!config_["signal"].empty() && config_["interval"].empty()) {
|
||||||
|
waitingWorker();
|
||||||
|
} else if (interval_.count() > 0) {
|
||||||
delayWorker();
|
delayWorker();
|
||||||
} else if (config_["exec"].isString()) {
|
} else if (config_["exec"].isString()) {
|
||||||
continuousWorker();
|
continuousWorker();
|
||||||
|
@ -92,6 +94,26 @@ void waybar::modules::Custom::continuousWorker() {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void waybar::modules::Custom::waitingWorker() {
|
||||||
|
thread_ = [this] {
|
||||||
|
bool can_update = true;
|
||||||
|
if (config_["exec-if"].isString()) {
|
||||||
|
output_ = util::command::execNoRead(config_["exec-if"].asString());
|
||||||
|
if (output_.exit_code != 0) {
|
||||||
|
can_update = false;
|
||||||
|
dp.emit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (can_update) {
|
||||||
|
if (config_["exec"].isString()) {
|
||||||
|
output_ = util::command::exec(config_["exec"].asString());
|
||||||
|
}
|
||||||
|
dp.emit();
|
||||||
|
}
|
||||||
|
thread_.sleep();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
void waybar::modules::Custom::refresh(int sig) {
|
void waybar::modules::Custom::refresh(int sig) {
|
||||||
if (sig == SIGRTMIN + config_["signal"].asInt()) {
|
if (sig == SIGRTMIN + config_["signal"].asInt()) {
|
||||||
thread_.wake_up();
|
thread_.wake_up();
|
||||||
|
|
|
@ -1,13 +1,17 @@
|
||||||
#include "modules/hyprland/workspaces.hpp"
|
#include "modules/hyprland/workspaces.hpp"
|
||||||
|
|
||||||
|
#include <fmt/ostream.h>
|
||||||
#include <json/value.h>
|
#include <json/value.h>
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <charconv>
|
#include <charconv>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include "util/rewrite_string.hpp"
|
||||||
|
|
||||||
namespace waybar::modules::hyprland {
|
namespace waybar::modules::hyprland {
|
||||||
|
|
||||||
Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value &config)
|
Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value &config)
|
||||||
|
@ -68,6 +72,16 @@ auto Workspaces::parse_config(const Json::Value &config) -> void {
|
||||||
g_warning("Invalid string representation for sort-by. Falling back to default sort method.");
|
g_warning("Invalid string representation for sort-by. Falling back to default sort method.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Json::Value format_window_separator = config["format-window-separator"];
|
||||||
|
format_window_separator_ =
|
||||||
|
format_window_separator.isString() ? format_window_separator.asString() : " ";
|
||||||
|
|
||||||
|
window_rewrite_rules_ = config["window-rewrite"];
|
||||||
|
|
||||||
|
Json::Value window_rewrite_default = config["window-rewrite-default"];
|
||||||
|
window_rewrite_default_ =
|
||||||
|
window_rewrite_default.isString() ? window_rewrite_default.asString() : "?";
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Workspaces::register_ipc() -> void {
|
auto Workspaces::register_ipc() -> void {
|
||||||
|
@ -134,6 +148,14 @@ auto Workspaces::update() -> void {
|
||||||
AModule::update();
|
AModule::update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isDoubleSpecial(std::string &workspace_name) {
|
||||||
|
// Hyprland's IPC sometimes reports the creation of workspaces strangely named
|
||||||
|
// `special:special:<some_name>`. This function checks for that and is used
|
||||||
|
// to avoid creating (and then removing) such workspaces.
|
||||||
|
// See hyprwm/Hyprland#3424 for more info.
|
||||||
|
return workspace_name.find("special:special:") != std::string::npos;
|
||||||
|
}
|
||||||
|
|
||||||
void Workspaces::onEvent(const std::string &ev) {
|
void Workspaces::onEvent(const std::string &ev) {
|
||||||
std::lock_guard<std::mutex> lock(mutex_);
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
std::string eventName(begin(ev), begin(ev) + ev.find_first_of('>'));
|
std::string eventName(begin(ev), begin(ev) + ev.find_first_of('>'));
|
||||||
|
@ -143,15 +165,16 @@ void Workspaces::onEvent(const std::string &ev) {
|
||||||
active_workspace_name_ = payload;
|
active_workspace_name_ = payload;
|
||||||
|
|
||||||
} else if (eventName == "destroyworkspace") {
|
} else if (eventName == "destroyworkspace") {
|
||||||
workspaces_to_remove_.push_back(payload);
|
if (!isDoubleSpecial(payload)) {
|
||||||
|
workspaces_to_remove_.push_back(payload);
|
||||||
|
}
|
||||||
} else if (eventName == "createworkspace") {
|
} else if (eventName == "createworkspace") {
|
||||||
const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces");
|
const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces");
|
||||||
for (Json::Value workspace_json : workspaces_json) {
|
for (Json::Value workspace_json : workspaces_json) {
|
||||||
std::string name = workspace_json["name"].asString();
|
std::string name = workspace_json["name"].asString();
|
||||||
if (name == payload &&
|
if (name == payload &&
|
||||||
(all_outputs() || bar_.output->name == workspace_json["monitor"].asString()) &&
|
(all_outputs() || bar_.output->name == workspace_json["monitor"].asString()) &&
|
||||||
(show_special() || !name.starts_with("special"))) {
|
(show_special() || !name.starts_with("special")) && !isDoubleSpecial(payload)) {
|
||||||
workspaces_to_create_.push_back(workspace_json);
|
workspaces_to_create_.push_back(workspace_json);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -175,8 +198,15 @@ void Workspaces::onEvent(const std::string &ev) {
|
||||||
} else {
|
} else {
|
||||||
workspaces_to_remove_.push_back(workspace);
|
workspaces_to_remove_.push_back(workspace);
|
||||||
}
|
}
|
||||||
} else if (eventName == "openwindow" || eventName == "closewindow" || eventName == "movewindow") {
|
} else if (eventName == "openwindow") {
|
||||||
update_window_count();
|
update_window_count();
|
||||||
|
on_window_opened(payload);
|
||||||
|
} else if (eventName == "closewindow") {
|
||||||
|
update_window_count();
|
||||||
|
on_window_closed(payload);
|
||||||
|
} else if (eventName == "movewindow") {
|
||||||
|
update_window_count();
|
||||||
|
on_window_moved(payload);
|
||||||
} else if (eventName == "urgent") {
|
} else if (eventName == "urgent") {
|
||||||
set_urgent_workspace(payload);
|
set_urgent_workspace(payload);
|
||||||
} else if (eventName == "renameworkspace") {
|
} else if (eventName == "renameworkspace") {
|
||||||
|
@ -197,6 +227,67 @@ void Workspaces::onEvent(const std::string &ev) {
|
||||||
dp.emit();
|
dp.emit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Workspaces::on_window_opened(std::string payload) {
|
||||||
|
size_t last_comma_idx = 0;
|
||||||
|
size_t next_comma_idx = payload.find(',');
|
||||||
|
std::string window_address = payload.substr(last_comma_idx, next_comma_idx - last_comma_idx);
|
||||||
|
|
||||||
|
last_comma_idx = next_comma_idx;
|
||||||
|
next_comma_idx = payload.find(',', next_comma_idx + 1);
|
||||||
|
std::string workspace_name =
|
||||||
|
payload.substr(last_comma_idx + 1, next_comma_idx - last_comma_idx - 1);
|
||||||
|
|
||||||
|
last_comma_idx = next_comma_idx;
|
||||||
|
next_comma_idx = payload.find(',', next_comma_idx + 1);
|
||||||
|
std::string window_class =
|
||||||
|
payload.substr(last_comma_idx + 1, next_comma_idx - last_comma_idx - 1);
|
||||||
|
|
||||||
|
std::string window_title = payload.substr(next_comma_idx + 1, payload.length() - next_comma_idx);
|
||||||
|
|
||||||
|
for (auto &workspace : workspaces_) {
|
||||||
|
if (workspace->on_window_opened(window_address, workspace_name, window_class, window_title)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Workspaces::on_window_closed(std::string addr) {
|
||||||
|
for (auto &workspace : workspaces_) {
|
||||||
|
if (workspace->on_window_closed(addr)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Workspaces::on_window_moved(std::string payload) {
|
||||||
|
size_t last_comma_idx = 0;
|
||||||
|
size_t next_comma_idx = payload.find(',');
|
||||||
|
std::string window_address = payload.substr(last_comma_idx, next_comma_idx - last_comma_idx);
|
||||||
|
|
||||||
|
std::string workspace_name =
|
||||||
|
payload.substr(next_comma_idx + 1, payload.length() - next_comma_idx);
|
||||||
|
|
||||||
|
std::string window_repr;
|
||||||
|
|
||||||
|
// Take the window's representation from the old workspace...
|
||||||
|
for (auto &workspace : workspaces_) {
|
||||||
|
try {
|
||||||
|
window_repr = workspace->on_window_closed(window_address).value();
|
||||||
|
break;
|
||||||
|
} catch (const std::bad_optional_access &e) {
|
||||||
|
// window was not found in this workspace
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...and add it to the new workspace
|
||||||
|
for (auto &workspace : workspaces_) {
|
||||||
|
if (workspace->on_window_opened(window_address, workspace_name, window_repr)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Workspaces::update_window_count() {
|
void Workspaces::update_window_count() {
|
||||||
const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces");
|
const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces");
|
||||||
for (auto &workspace : workspaces_) {
|
for (auto &workspace : workspaces_) {
|
||||||
|
@ -215,22 +306,86 @@ void Workspaces::update_window_count() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Workspaces::create_workspace(Json::Value &value) {
|
void Workspaces::initialize_window_maps() {
|
||||||
|
Json::Value clients_data = gIPC->getSocket1JsonReply("clients");
|
||||||
|
for (auto &workspace : workspaces_) {
|
||||||
|
workspace->initialize_window_map(clients_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Workspace::initialize_window_map(const Json::Value &clients_data) {
|
||||||
|
window_map_.clear();
|
||||||
|
for (auto client : clients_data) {
|
||||||
|
if (client["workspace"]["id"].asInt() == id()) {
|
||||||
|
// substr(2, ...) is necessary because Hyprland's JSON follows this format:
|
||||||
|
// 0x{ADDR}
|
||||||
|
// While Hyprland's IPC follows this format:
|
||||||
|
// {ADDR}
|
||||||
|
WindowAddress client_address = client["address"].asString();
|
||||||
|
client_address = client_address.substr(2, client_address.length() - 2);
|
||||||
|
insert_window(client_address, client["class"].asString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Workspace::insert_window(WindowAddress addr, std::string window_class) {
|
||||||
|
auto window_repr = workspace_manager_.get_rewrite(window_class);
|
||||||
|
if (!window_repr.empty()) {
|
||||||
|
window_map_.emplace(addr, window_repr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string Workspace::remove_window(WindowAddress addr) {
|
||||||
|
std::string window_repr = window_map_[addr];
|
||||||
|
window_map_.erase(addr);
|
||||||
|
|
||||||
|
return window_repr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Workspace::on_window_opened(WindowAddress &addr, std::string &workspace_name,
|
||||||
|
std::string window_repr) {
|
||||||
|
if (workspace_name == name()) {
|
||||||
|
window_map_.emplace(addr, window_repr);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Workspace::on_window_opened(WindowAddress &addr, std::string &workspace_name,
|
||||||
|
std::string &window_class, std::string &window_title) {
|
||||||
|
if (workspace_name == name()) {
|
||||||
|
insert_window(addr, window_class);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> Workspace::on_window_closed(WindowAddress &addr) {
|
||||||
|
if (window_map_.contains(addr)) {
|
||||||
|
return remove_window(addr);
|
||||||
|
} else {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Workspaces::create_workspace(Json::Value &workspace_data, const Json::Value &clients_data) {
|
||||||
// replace the existing persistent workspace if it exists
|
// replace the existing persistent workspace if it exists
|
||||||
auto workspace = std::find_if(
|
auto workspace = std::find_if(
|
||||||
workspaces_.begin(), workspaces_.end(), [&](std::unique_ptr<Workspace> const &x) {
|
workspaces_.begin(), workspaces_.end(), [&](std::unique_ptr<Workspace> const &x) {
|
||||||
auto name = value["name"].asString();
|
auto name = workspace_data["name"].asString();
|
||||||
return x->is_persistent() &&
|
return x->is_persistent() &&
|
||||||
((name.starts_with("special:") && name.substr(8) == x->name()) || name == x->name());
|
((name.starts_with("special:") && name.substr(8) == x->name()) || name == x->name());
|
||||||
});
|
});
|
||||||
if (workspace != workspaces_.end()) {
|
if (workspace != workspaces_.end()) {
|
||||||
// replace workspace, but keep persistent flag
|
// replace workspace, but keep persistent flag
|
||||||
workspaces_.erase(workspace);
|
workspaces_.erase(workspace);
|
||||||
value["persistent"] = true;
|
workspace_data["persistent"] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// create new workspace
|
// create new workspace
|
||||||
workspaces_.emplace_back(std::make_unique<Workspace>(value, *this));
|
workspaces_.emplace_back(std::make_unique<Workspace>(workspace_data, *this, clients_data));
|
||||||
Gtk::Button &new_workspace_button = workspaces_.back()->button();
|
Gtk::Button &new_workspace_button = workspaces_.back()->button();
|
||||||
box_.pack_start(new_workspace_button, false, false);
|
box_.pack_start(new_workspace_button, false, false);
|
||||||
sort_workspaces();
|
sort_workspaces();
|
||||||
|
@ -238,8 +393,10 @@ void Workspaces::create_workspace(Json::Value &value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Workspaces::remove_workspace(std::string name) {
|
void Workspaces::remove_workspace(std::string name) {
|
||||||
auto workspace = std::find_if(workspaces_.begin(), workspaces_.end(),
|
auto workspace =
|
||||||
[&](std::unique_ptr<Workspace> &x) { return x->name() == name; });
|
std::find_if(workspaces_.begin(), workspaces_.end(), [&](std::unique_ptr<Workspace> &x) {
|
||||||
|
return (name.starts_with("special:") && name.substr(8) == x->name()) || name == x->name();
|
||||||
|
});
|
||||||
|
|
||||||
if (workspace == workspaces_.end()) {
|
if (workspace == workspaces_.end()) {
|
||||||
// happens when a workspace on another monitor is destroyed
|
// happens when a workspace on another monitor is destroyed
|
||||||
|
@ -351,10 +508,12 @@ void Workspaces::init() {
|
||||||
create_persistent_workspaces();
|
create_persistent_workspaces();
|
||||||
|
|
||||||
const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces");
|
const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces");
|
||||||
|
const Json::Value clients_json = gIPC->getSocket1JsonReply("clients");
|
||||||
|
|
||||||
for (Json::Value workspace_json : workspaces_json) {
|
for (Json::Value workspace_json : workspaces_json) {
|
||||||
if ((all_outputs() || bar_.output->name == workspace_json["monitor"].asString()) &&
|
if ((all_outputs() || bar_.output->name == workspace_json["monitor"].asString()) &&
|
||||||
(!workspace_json["name"].asString().starts_with("special") || show_special())) {
|
(!workspace_json["name"].asString().starts_with("special") || show_special())) {
|
||||||
create_workspace(workspace_json);
|
create_workspace(workspace_json, clients_json);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -371,7 +530,8 @@ Workspaces::~Workspaces() {
|
||||||
std::lock_guard<std::mutex> lg(mutex_);
|
std::lock_guard<std::mutex> lg(mutex_);
|
||||||
}
|
}
|
||||||
|
|
||||||
Workspace::Workspace(const Json::Value &workspace_data, Workspaces &workspace_manager)
|
Workspace::Workspace(const Json::Value &workspace_data, Workspaces &workspace_manager,
|
||||||
|
const Json::Value &clients_data)
|
||||||
: workspace_manager_(workspace_manager),
|
: workspace_manager_(workspace_manager),
|
||||||
id_(workspace_data["id"].asInt()),
|
id_(workspace_data["id"].asInt()),
|
||||||
name_(workspace_data["name"].asString()),
|
name_(workspace_data["name"].asString()),
|
||||||
|
@ -396,6 +556,8 @@ Workspace::Workspace(const Json::Value &workspace_data, Workspaces &workspace_ma
|
||||||
button_.set_relief(Gtk::RELIEF_NONE);
|
button_.set_relief(Gtk::RELIEF_NONE);
|
||||||
content_.set_center_widget(label_);
|
content_.set_center_widget(label_);
|
||||||
button_.add(content_);
|
button_.add(content_);
|
||||||
|
|
||||||
|
initialize_window_map(clients_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void add_or_remove_class(const Glib::RefPtr<Gtk::StyleContext> &context, bool condition,
|
void add_or_remove_class(const Glib::RefPtr<Gtk::StyleContext> &context, bool condition,
|
||||||
|
@ -429,8 +591,22 @@ void Workspace::update(const std::string &format, const std::string &icon) {
|
||||||
add_or_remove_class(style_context, is_urgent(), "urgent");
|
add_or_remove_class(style_context, is_urgent(), "urgent");
|
||||||
add_or_remove_class(style_context, is_visible(), "visible");
|
add_or_remove_class(style_context, is_visible(), "visible");
|
||||||
|
|
||||||
|
std::string windows;
|
||||||
|
auto window_separator = workspace_manager_.get_window_separator();
|
||||||
|
|
||||||
|
bool is_not_first = false;
|
||||||
|
|
||||||
|
for (auto &[_pid, window_repr] : window_map_) {
|
||||||
|
if (is_not_first) {
|
||||||
|
windows.append(window_separator);
|
||||||
|
}
|
||||||
|
is_not_first = true;
|
||||||
|
windows.append(window_repr);
|
||||||
|
}
|
||||||
|
|
||||||
label_.set_markup(fmt::format(fmt::runtime(format), fmt::arg("id", id()),
|
label_.set_markup(fmt::format(fmt::runtime(format), fmt::arg("id", id()),
|
||||||
fmt::arg("name", name()), fmt::arg("icon", icon)));
|
fmt::arg("name", name()), fmt::arg("icon", icon),
|
||||||
|
fmt::arg("windows", windows)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Workspaces::sort_workspaces() {
|
void Workspaces::sort_workspaces() {
|
||||||
|
@ -549,7 +725,8 @@ std::string &Workspace::select_icon(std::map<std::string, std::string> &icons_ma
|
||||||
if (default_icon_it != icons_map.end()) {
|
if (default_icon_it != icons_map.end()) {
|
||||||
return default_icon_it->second;
|
return default_icon_it->second;
|
||||||
}
|
}
|
||||||
return icons_map[""];
|
|
||||||
|
return name_;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Workspace::handle_clicked(GdkEventButton *bt) -> bool {
|
auto Workspace::handle_clicked(GdkEventButton *bt) -> bool {
|
||||||
|
@ -589,4 +766,23 @@ void Workspaces::set_urgent_workspace(std::string windowaddress) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string Workspaces::get_rewrite(std::string window_class) {
|
||||||
|
if (regex_cache_.contains(window_class)) {
|
||||||
|
return regex_cache_[window_class];
|
||||||
|
}
|
||||||
|
|
||||||
|
bool matched_any;
|
||||||
|
|
||||||
|
std::string window_class_rewrite =
|
||||||
|
waybar::util::rewriteStringOnce(window_class, window_rewrite_rules_, matched_any);
|
||||||
|
|
||||||
|
if (!matched_any) {
|
||||||
|
window_class_rewrite = window_rewrite_default_;
|
||||||
|
}
|
||||||
|
|
||||||
|
regex_cache_.emplace(window_class, window_class_rewrite);
|
||||||
|
|
||||||
|
return window_class_rewrite;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace waybar::modules::hyprland
|
} // namespace waybar::modules::hyprland
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "util/rewrite_string.hpp"
|
#include "util/rewrite_string.hpp"
|
||||||
|
|
||||||
|
#include <fmt/core.h>
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
#include <regex>
|
#include <regex>
|
||||||
|
@ -17,7 +18,7 @@ std::string rewriteString(const std::string& value, const Json::Value& rules) {
|
||||||
try {
|
try {
|
||||||
// malformated regexes will cause an exception.
|
// malformated regexes will cause an exception.
|
||||||
// in this case, log error and try the next rule.
|
// in this case, log error and try the next rule.
|
||||||
const std::regex rule{it.key().asString()};
|
const std::regex rule{it.key().asString(), std::regex_constants::icase};
|
||||||
if (std::regex_match(value, rule)) {
|
if (std::regex_match(value, rule)) {
|
||||||
res = std::regex_replace(res, rule, it->asString());
|
res = std::regex_replace(res, rule, it->asString());
|
||||||
}
|
}
|
||||||
|
@ -29,4 +30,31 @@ std::string rewriteString(const std::string& value, const Json::Value& rules) {
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string rewriteStringOnce(const std::string& value, const Json::Value& rules,
|
||||||
|
bool& matched_any) {
|
||||||
|
if (!rules.isObject()) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
matched_any = false;
|
||||||
|
|
||||||
|
std::string res = value;
|
||||||
|
|
||||||
|
for (auto it = rules.begin(); it != rules.end(); ++it) {
|
||||||
|
if (it.key().isString() && it->isString()) {
|
||||||
|
try {
|
||||||
|
const std::regex rule{it.key().asString(), std::regex_constants::icase};
|
||||||
|
if (std::regex_match(value, rule)) {
|
||||||
|
matched_any = true;
|
||||||
|
return std::regex_replace(res, rule, it->asString());
|
||||||
|
}
|
||||||
|
} catch (const std::regex_error& e) {
|
||||||
|
spdlog::error("Invalid rule {}: {}", it.key().asString(), e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
} // namespace waybar::util
|
} // namespace waybar::util
|
||||||
|
|
Loading…
Reference in New Issue