diff --git a/include/modules/sway/workspaces.hpp b/include/modules/sway/workspaces.hpp index d07edb4..0efffe6 100644 --- a/include/modules/sway/workspaces.hpp +++ b/include/modules/sway/workspaces.hpp @@ -41,6 +41,7 @@ class Workspaces : public AModule, public sigc::trackable { const Bar& bar_; std::vector workspaces_; + std::vector high_priority_named_; std::vector workspaces_order_; Gtk::Box box_; util::JsonParser parser_; diff --git a/include/util/sleeper_thread.hpp b/include/util/sleeper_thread.hpp index a724b1e..861d5f1 100644 --- a/include/util/sleeper_thread.hpp +++ b/include/util/sleeper_thread.hpp @@ -95,6 +95,7 @@ class SleeperThread { } ~SleeperThread() { + connection_.disconnect(); stop(); if (thread_.joinable()) { thread_.join(); diff --git a/man/waybar-cava.5.scd b/man/waybar-cava.5.scd new file mode 100644 index 0000000..3d3762b --- /dev/null +++ b/man/waybar-cava.5.scd @@ -0,0 +1,182 @@ +waybar-cava(5) "waybar-cava" "User Manual" + +# NAME + +cava + +# DESCRIPTION + +*cava* module for karlstav/cava project. See it on github: https://github.com/karlstav/cava. + + +# FILES + +$XDG_CONFIG_HOME/waybar/config ++ + Per user configuration file + +# ADDITIONAL FILES + +libcava lives in: + +. /usr/lib/libcava.so or /usr/lib64/libcava.so +. /usr/lib/pkgconfig/cava.pc or /usr/lib64/pkgconfig/cava.pc +. /usr/include/cava + +# CONFIGURATION + +[- *Option* +:- *Typeof* +:- *Default* +:- *Description* +|[ *cava_config* +:[ string +:[ +:< Path where cava configuration file is placed to +|[ *framerate* +:[ integer +:[ 30 +:[ rames per second. Is used as a replacement for *interval* +|[ *autosens* +:[ integer +:[ 1 +:[ Will attempt to decrease sensitivity if the bars peak +|[ *sensitivity* +:[ integer +:[ 100 +:[ Manual sensitivity in %. It's recommended to be omitted when *autosens* = 1 +|[ *bars* +:[ integer +:[ 12 +:[ The number of bars +|[ *lower_cutoff_freq* +:[ long integer +:[ 50 +:[ Lower cutoff frequencies for lowest bars the bandwidth of the visualizer +|[ *higher_cutoff_freq* +:[ long integer +:[ 10000 +:[ Higher cutoff frequencies for highest bars the bandwidth of the visualizer +|[ *sleep_timer* +:[ integer +:[ 5 +:[ Seconds with no input before cava main thread goes to sleep mode +|[ *method* +:[ string +:[ pulse +:[ Audio capturing method. Possible methods are: pipewire, pulse, alsa, fifo, sndio or shmem +|[ *source* +:[ string +:[ auto +:[ See cava configuration +|[ *sample_rate* +:[ long integer +:[ 44100 +:[ See cava configuration +|[ *sample_bits* +:[ integer +:[ 16 +:[ See cava configuration +|[ *stereo* +:[ bool +:[ true +:[ Visual channels +|[ *reverse* +:[ bool +:[ false +:[ Displays frequencies the other way around +|[ *bar_delimiter* +:[ integer +:[ 0 +:[ Each bar is separated by a delimiter. Use decimal value in ascii table(i.e. 59 = ";"). 0 means no delimiter +|[ *monstercat* +:[ bool +:[ false +:[ Disables or enables the so-called "Monstercat smoothing" with of without "waves" +|[ *waves* +:[ bool +:[ false +:[ Disables or enables the so-called "Monstercat smoothing" with of without "waves" +|[ *noise_reduction* +:[ double +:[ 0.77 +:[ Range between 0 - 1. The raw visualization is very noisy, this factor adjust the integral and gravity filters to keep the signal smooth. 1 - will be very slow and smooth, 0 - will be fast but noisy +|[ *input_delay* +:[ integer +:[ 2 +:[ Sets the delay before fetching audio source thread start working. On author machine Waybar starts much faster then pipewire audio server, and without a little delay cava module fails due to pipewire is not ready +|[ *ascii_max_range* +:[ integer +:[ 7 +:[ It's impossible to set it directly. The value is dictated by the number of icons in the array *format-icons* +|[ *data_format* +:[ string +:[ asci +:[ It's impossible to set it. Waybar sets it to = asci for internal needs +|[ *raw_target* +:[ string +:[ /dev/stdout +:[ It's impossible to set it. Waybar sets it to = /dev/stdout for internal needs + +Configuration can be provided as: +- The only cava configuration file which is provided through *cava_config*. The rest configuration can be skipped +- Without cava configuration file. In such case cava should be configured through provided list of the configuration option +- Mix. When provided both And cava configuration file And configuration options. In such case waybar applies configuration file first then overrides particular options by the provided list of configuration options + +# ACTIONS + +[- *String* +:- *Action* +|[ *mode* +:< Switch main cava thread and fetching audio source thread from/to pause/resume + +# DEPENDENCIES + +- iniparser +- fftw3 + +# SOLVING ISSUES + +. On start Waybar throws an exception "error while loading shared libraries: libcava.so: cannot open shared object file: No such file or directory". + It might happen when libcava for some reason hasn't been registered in the system. sudo ldconfig should help +. Waybar is starting but cava module doesn't react on the music + 1. In such case for at first need to make sure usual cava application is working as well + 2. If so, need to comment all configuration options. Uncomment cava_config and provide the path to the working cava config + 3. You might set too huge or too small input_delay. Try to setup to 4 seconds, restart waybar and check again 4 seconds past. Usual even on weak machines it should be enough + 4. You might accidentally switched action mode to pause mode + +# RISING ISSUES + +For clear understanding: this module is a cava API's consumer. So for any bugs related to cava engine you should contact to Cava upstream(https://github.com/karlstav/cava) ++ +with the one Exception. Cava upstream doesn't provide cava as a shared library. For that this module author made a fork libcava(https://github.com/LukashonakV/cava). ++ +So the order is: +. cava upstream +. libcava upstream. +In case when cava releases new version and you're wanna get it, it should be raised an issue to libcava(https://github.com/LukashonakV/cava) with title ++ +\[Bump\]x.x.x where x.x.x is cava release version. + +# EXAMPLES + +``` +"cava": { +// "cava_config": "$XDG_CONFIG_HOME/cava/cava.conf", + "framerate": 30, + "autosens": 1, +// "sensitivity": 100, + "bars": 14, + "lower_cutoff_freq": 50, + "higher_cutoff_freq": 10000, + "method": "pulse", + "source": "auto", + "stereo": true, + "reverse": false, + "bar_delimiter": 0, + "monstercat": false, + "waves": false, + "noise_reduction": 0.77, + "input_delay": 2, + "format-icons" : ["▁", "▂", "▃", "▄", "▅", "▆", "▇", "█" ], + "actions": { + "on-click-right": "mode" + } + }, +``` diff --git a/man/waybar-hyprland-window.5.scd b/man/waybar-hyprland-window.5.scd index 64e48e4..5e7b4d5 100644 --- a/man/waybar-hyprland-window.5.scd +++ b/man/waybar-hyprland-window.5.scd @@ -63,7 +63,7 @@ Invalid expressions (e.g., mismatched parentheses) are skipped. # STYLE - *#window* -- *#window.empty* When no windows are in the workspace +- *window#waybar.empty #window* When no windows are in the workspace The following classes are applied to the entire Waybar rather than just the window widget: diff --git a/man/waybar-sway-workspaces.5.scd b/man/waybar-sway-workspaces.5.scd index 1e5f45d..c934a32 100644 --- a/man/waybar-sway-workspaces.5.scd +++ b/man/waybar-sway-workspaces.5.scd @@ -102,6 +102,7 @@ Additional to workspace name matching, the following *format-icons* can be set. - *urgent*: Will be shown, when workspace is flagged as urgent - *focused*: Will be shown, when workspace is focused - *persistent*: Will be shown, when workspace is persistent one. +- *high-priority-named*: Icons by names will be shown always for that workspaces, independent by state. # PERSISTENT WORKSPACES @@ -134,6 +135,7 @@ n.b.: the list of outputs can be obtained from command line using *swaymsg -t ge "3": "", "4": "", "5": "", + "high-priority-named": [ "1", "2" ], "urgent": "", "focused": "", "default": "" diff --git a/man/waybar-wlr-taskbar.5.scd b/man/waybar-wlr-taskbar.5.scd index 5626eae..2fc8693 100644 --- a/man/waybar-wlr-taskbar.5.scd +++ b/man/waybar-wlr-taskbar.5.scd @@ -89,7 +89,7 @@ Addressed by *wlr/taskbar* *{icon}*: The icon of the application. -*{title}*: The application name as in desktop file if appropriate desktop fils found, otherwise same as {app_id} +*{name}*: The application name as in desktop file if appropriate desktop fils found, otherwise same as {app_id} *{title}*: The title of the application. diff --git a/man/waybar.5.scd.in b/man/waybar.5.scd.in index 0b3dc74..a837669 100644 --- a/man/waybar.5.scd.in +++ b/man/waybar.5.scd.in @@ -268,6 +268,7 @@ Valid options for the (optional) "orientation" property are: "horizontal", "vert - *waybar-backlight(5)* - *waybar-battery(5)* - *waybar-bluetooth(5)* +- *waybar-cava(5)* - *waybar-clock(5)* - *waybar-cpu(5)* - *waybar-custom(5)* diff --git a/meson.build b/meson.build index 6e188c4..18b056a 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project( 'waybar', 'cpp', 'c', - version: '0.9.19', + version: '0.9.20', license: 'MIT', meson_version: '>= 0.50.0', default_options : [ @@ -346,7 +346,7 @@ if get_option('experimental') endif cava = dependency('cava', - version : '>=0.8.4', + version : '>=0.8.5', required: get_option('cava'), fallback : ['cava', 'cava_dep'], not_found_message: 'cava is not found. Building waybar without cava') @@ -423,6 +423,7 @@ if scdoc.found() main_manpage_path, 'waybar-backlight.5.scd', 'waybar-battery.5.scd', + 'waybar-cava.5.scd', 'waybar-clock.5.scd', 'waybar-cpu.5.scd', 'waybar-custom.5.scd', diff --git a/src/modules/hyprland/language.cpp b/src/modules/hyprland/language.cpp index aa22a48..423e2b5 100644 --- a/src/modules/hyprland/language.cpp +++ b/src/modules/hyprland/language.cpp @@ -38,7 +38,10 @@ auto Language::update() -> void { std::lock_guard lg(mutex_); std::string layoutName = std::string{}; - if (config_.isMember("format-" + layout_.short_description)) { + if (config_.isMember("format-" + layout_.short_description + "-" + layout_.variant)) { + const auto propName = "format-" + layout_.short_description + "-" + layout_.variant; + layoutName = fmt::format(fmt::runtime(format_), config_[propName].asString()); + } else if (config_.isMember("format-" + layout_.short_description)) { const auto propName = "format-" + layout_.short_description; layoutName = fmt::format(fmt::runtime(format_), config_[propName].asString()); } else { diff --git a/src/modules/hyprland/window.cpp b/src/modules/hyprland/window.cpp index d01ac9d..81c864d 100644 --- a/src/modules/hyprland/window.cpp +++ b/src/modules/hyprland/window.cpp @@ -48,14 +48,7 @@ auto Window::update() -> void { std::string window_name = waybar::util::sanitize_string(workspace_.last_window_title); std::string window_address = workspace_.last_window; - if (window_name != window_data_.title) { - if (window_name.empty()) { - label_.get_style_context()->add_class("empty"); - } else { - label_.get_style_context()->remove_class("empty"); - } - window_data_.title = window_name; - } + window_data_.title = window_name; if (!format_.empty()) { label_.show(); diff --git a/src/modules/sway/window.cpp b/src/modules/sway/window.cpp index 06acdca..25e430a 100644 --- a/src/modules/sway/window.cpp +++ b/src/modules/sway/window.cpp @@ -145,6 +145,40 @@ std::pair leafNodesInWorkspace(const Json::Value& node) { return {sum, floating_sum}; } +std::optional> getSingleChildNode( + const Json::Value& node) { + auto const& nodes = node["nodes"]; + if (nodes.empty()) { + if (node["type"].asString() == "workspace") + return {}; + else if (node["type"].asString() == "floating_con") { + return {}; + } else { + return {std::cref(node)}; + } + } + auto it = std::cbegin(nodes); + if (it == std::cend(nodes)) { + return {}; + } + auto const& child = *it; + ++it; + if (it != std::cend(nodes)) { + return {}; + } + return {getSingleChildNode(child)}; +} + +std::tuple getWindowInfo(const Json::Value& node) { + const auto app_id = node["app_id"].isString() ? node["app_id"].asString() + : node["window_properties"]["instance"].asString(); + const auto app_class = node["window_properties"]["class"].isString() + ? node["window_properties"]["class"].asString() + : ""; + const auto shell = node["shell"].isString() ? node["shell"].asString() : ""; + return {app_id, app_class, shell}; +} + std::tuple gfnWithWorkspace(const Json::Value& nodes, std::string& output, const Json::Value& config_, const Bar& bar_, Json::Value& parentWorkspace, @@ -181,12 +215,7 @@ gfnWithWorkspace(const Json::Value& nodes, std::string& output, const Json::Valu // found node spdlog::trace("actual output {}, output found {}, node (focused) found {}", bar_.output->name, output, node["name"].asString()); - auto app_id = node["app_id"].isString() ? node["app_id"].asString() - : node["window_properties"]["instance"].asString(); - const auto app_class = node["window_properties"]["class"].isString() - ? node["window_properties"]["class"].asString() - : ""; - const auto shell = node["shell"].isString() ? node["shell"].asString() : ""; + const auto [app_id, app_class, shell] = getWindowInfo(node); int nb = node.size(); int floating_count = 0; std::string workspace_layout = ""; @@ -226,15 +255,24 @@ gfnWithWorkspace(const Json::Value& nodes, std::string& output, const Json::Valu std::pair all_leaf_nodes = leafNodesInWorkspace(immediateParent); // using an empty string as default ensures that no window depending styles are set due to the // checks above for !name.empty() + std::string app_id = ""; + std::string app_class = ""; + std::string workspace_layout = ""; + if (all_leaf_nodes.first == 1) { + const auto single_child = getSingleChildNode(immediateParent); + if (single_child.has_value()) { + std::tie(app_id, app_class, workspace_layout) = getWindowInfo(single_child.value()); + } + } return {all_leaf_nodes.first, all_leaf_nodes.second, 0, (all_leaf_nodes.first > 0 || all_leaf_nodes.second > 0) ? config_["offscreen-css-text"].asString() : "", - "", - "", - "", + app_id, + app_class, + workspace_layout, immediateParent["layout"].asString()}; } diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index 90efe7a..a5e5fa7 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -28,6 +28,11 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value : AModule(config, "workspaces", id, false, !config["disable-scroll"].asBool()), bar_(bar), box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0) { + if (config["format-icons"]["high-priority-named"].isArray()) { + for (auto &it : config["format-icons"]["high-priority-named"]) { + high_priority_named_.push_back(it.asString()); + } + } box_.set_name("workspaces"); if (!id.empty()) { box_.get_style_context()->add_class(id); @@ -279,9 +284,24 @@ Gtk::Button &Workspaces::addButton(const Json::Value &node) { } std::string Workspaces::getIcon(const std::string &name, const Json::Value &node) { - std::vector keys = {"urgent", "focused", name, "visible", "default"}; + std::vector keys = {"high-priority-named", "urgent", "focused", name, "default"}; for (auto const &key : keys) { - if (key == "focused" || key == "visible" || key == "urgent") { + if (key == "high-priority-named") { + auto it = std::find_if(high_priority_named_.begin(), high_priority_named_.end(), + [&](const std::string &member) { return member == name; }); + if (it != high_priority_named_.end()) { + return config_["format-icons"][name].asString(); + } + + it = std::find_if(high_priority_named_.begin(), high_priority_named_.end(), + [&](const std::string &member) { + return trimWorkspaceName(member) == trimWorkspaceName(name); + }); + if (it != high_priority_named_.end()) { + return config_["format-icons"][trimWorkspaceName(name)].asString(); + } + } + if (key == "focused" || key == "urgent") { if (config_["format-icons"][key].isString() && node[key].asBool()) { return config_["format-icons"][key].asString(); } diff --git a/subprojects/cava.wrap b/subprojects/cava.wrap index 59044b1..f6973c8 100644 --- a/subprojects/cava.wrap +++ b/subprojects/cava.wrap @@ -1,7 +1,7 @@ [wrap-file] -directory = cava-0.8.4 -source_url = https://github.com/LukashonakV/cava/archive/0.8.4.tar.gz -source_filename = cava-0.8.4.tar.gz -source_hash = 523353f446570277d40b8e1efb84468d70fdec53e1356a555c14bf466557a3ed +directory = cava-0.8.5 +source_url = https://github.com/LukashonakV/cava/archive/0.8.5.tar.gz +source_filename = cava-0.8.5.tar.gz +source_hash = 9ce3df7d374dc83ed0704fe3caef5e00600ce061d85608aad4142d2c59aa4647 [provide] cava = cava_dep