Merge branch 'master' into fix/zjeffer/thread-sanitizer-warning
This commit is contained in:
6
Makefile
6
Makefile
@ -20,8 +20,12 @@ debug-run: build-debug
|
||||
./build/waybar --log-level debug
|
||||
|
||||
test:
|
||||
meson test -C build --no-rebuild --verbose --suite waybar
|
||||
meson test -C build --verbose --suite waybar
|
||||
.PHONY: test
|
||||
|
||||
test-detailed:
|
||||
meson test -C build --verbose --print-errorlogs --test-args='--reporter console -s'
|
||||
.PHONY: test-detailed
|
||||
|
||||
clean:
|
||||
rm -rf build
|
||||
|
6
flake.lock
generated
6
flake.lock
generated
@ -18,11 +18,11 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1753694789,
|
||||
"narHash": "sha256-cKgvtz6fKuK1Xr5LQW/zOUiAC0oSQoA9nOISB0pJZqM=",
|
||||
"lastModified": 1759036355,
|
||||
"narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "dc9637876d0dcc8c9e5e22986b857632effeb727",
|
||||
"rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
42
flake.nix
42
flake.nix
@ -65,7 +65,7 @@
|
||||
nativeBuildInputs =
|
||||
pkgs.waybar.nativeBuildInputs
|
||||
++ (with pkgs; [
|
||||
nixfmt-rfc-style
|
||||
nixfmt
|
||||
clang-tools
|
||||
gdb
|
||||
]);
|
||||
@ -75,28 +75,26 @@
|
||||
formatter = genSystems (
|
||||
pkgs:
|
||||
pkgs.treefmt.withConfig {
|
||||
settings = [
|
||||
{
|
||||
formatter = {
|
||||
clang-format = {
|
||||
options = [ "-i" ];
|
||||
command = lib.getExe' pkgs.clang-tools "clang-format";
|
||||
excludes = [ ];
|
||||
includes = [
|
||||
"*.c"
|
||||
"*.cpp"
|
||||
"*.h"
|
||||
"*.hpp"
|
||||
];
|
||||
};
|
||||
nixfmt = {
|
||||
command = lib.getExe pkgs.nixfmt-rfc-style;
|
||||
includes = [ "*.nix" ];
|
||||
};
|
||||
settings = {
|
||||
formatter = {
|
||||
clang-format = {
|
||||
options = [ "-i" ];
|
||||
command = lib.getExe' pkgs.clang-tools "clang-format";
|
||||
excludes = [ ];
|
||||
includes = [
|
||||
"*.c"
|
||||
"*.cpp"
|
||||
"*.h"
|
||||
"*.hpp"
|
||||
];
|
||||
};
|
||||
tree-root-file = ".git/index";
|
||||
}
|
||||
];
|
||||
nixfmt = {
|
||||
command = lib.getExe pkgs.nixfmt;
|
||||
includes = [ "*.nix" ];
|
||||
};
|
||||
};
|
||||
tree-root-file = ".git/index";
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -21,7 +21,7 @@ class ALabel : public AModule {
|
||||
protected:
|
||||
Gtk::Label label_;
|
||||
std::string format_;
|
||||
const std::chrono::seconds interval_;
|
||||
const std::chrono::milliseconds interval_;
|
||||
bool alt_ = false;
|
||||
std::string default_format_;
|
||||
|
||||
|
@ -35,7 +35,8 @@ class Config {
|
||||
void setupConfig(Json::Value &dst, const std::string &config_file, int depth);
|
||||
void resolveConfigIncludes(Json::Value &config, int depth);
|
||||
void mergeConfig(Json::Value &a_config_, Json::Value &b_config_);
|
||||
static std::optional<std::string> findIncludePath(const std::string &name);
|
||||
static std::vector<std::string> findIncludePath(
|
||||
const std::string &name, const std::vector<std::string> &dirs = CONFIG_DIRS);
|
||||
|
||||
std::string config_file_;
|
||||
|
||||
|
@ -1,59 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "ALabel.hpp"
|
||||
#include "util/sleeper_thread.hpp"
|
||||
|
||||
namespace cava {
|
||||
extern "C" {
|
||||
// Need sdl_glsl output feature to be enabled on libcava
|
||||
#ifndef SDL_GLSL
|
||||
#define SDL_GLSL
|
||||
#endif
|
||||
|
||||
#include <cava/common.h>
|
||||
|
||||
#ifdef SDL_GLSL
|
||||
#undef SDL_GLSL
|
||||
#endif
|
||||
}
|
||||
} // namespace cava
|
||||
|
||||
namespace waybar::modules {
|
||||
using namespace std::literals::chrono_literals;
|
||||
|
||||
class Cava final : public ALabel {
|
||||
public:
|
||||
Cava(const std::string&, const Json::Value&);
|
||||
virtual ~Cava();
|
||||
auto update() -> void override;
|
||||
auto doAction(const std::string& name) -> void override;
|
||||
|
||||
private:
|
||||
util::SleeperThread thread_;
|
||||
util::SleeperThread thread_fetch_input_;
|
||||
|
||||
struct cava::error_s error_{}; // cava errors
|
||||
struct cava::config_params prm_{}; // cava parameters
|
||||
struct cava::audio_raw audio_raw_{}; // cava handled raw audio data(is based on audio_data)
|
||||
struct cava::audio_data audio_data_{}; // cava audio data
|
||||
struct cava::cava_plan* plan_; //{new cava_plan{}};
|
||||
// Cava API to read audio source
|
||||
cava::ptr input_source_;
|
||||
// Delay to handle audio source
|
||||
std::chrono::milliseconds frame_time_milsec_{1s};
|
||||
// Text to display
|
||||
std::string text_{""};
|
||||
int rePaint_{1};
|
||||
std::chrono::seconds fetch_input_delay_{4};
|
||||
std::chrono::seconds suspend_silence_delay_{0};
|
||||
bool silence_{false};
|
||||
bool hide_on_silence_{false};
|
||||
std::string format_silent_{""};
|
||||
int sleep_counter_{0};
|
||||
// Cava method
|
||||
void pause_resume();
|
||||
// ModuleActionMap
|
||||
static inline std::map<const std::string, void (waybar::modules::Cava::* const)()> actionMap_{
|
||||
{"mode", &waybar::modules::Cava::pause_resume}};
|
||||
};
|
||||
} // namespace waybar::modules
|
30
include/modules/cava/cava.hpp
Normal file
30
include/modules/cava/cava.hpp
Normal file
@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include "ALabel.hpp"
|
||||
#include "cava_backend.hpp"
|
||||
|
||||
namespace waybar::modules::cava {
|
||||
|
||||
class Cava final : public ALabel, public sigc::trackable {
|
||||
public:
|
||||
Cava(const std::string&, const Json::Value&);
|
||||
~Cava() = default;
|
||||
auto onUpdate(const std::string& input) -> void;
|
||||
auto onSilence() -> void;
|
||||
auto doAction(const std::string& name) -> void override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<CavaBackend> backend_;
|
||||
// Text to display
|
||||
std::string label_text_{""};
|
||||
bool hide_on_silence_{false};
|
||||
std::string format_silent_{""};
|
||||
int ascii_range_{0};
|
||||
bool silence_{false};
|
||||
// Cava method
|
||||
void pause_resume();
|
||||
// ModuleActionMap
|
||||
static inline std::map<const std::string, void (waybar::modules::cava::Cava::* const)()>
|
||||
actionMap_{{"mode", &waybar::modules::cava::Cava::pause_resume}};
|
||||
};
|
||||
} // namespace waybar::modules::cava
|
74
include/modules/cava/cava_backend.hpp
Normal file
74
include/modules/cava/cava_backend.hpp
Normal file
@ -0,0 +1,74 @@
|
||||
#pragma once
|
||||
|
||||
#include <json/json.h>
|
||||
#include <sigc++/sigc++.h>
|
||||
|
||||
#include "util/sleeper_thread.hpp"
|
||||
|
||||
namespace cava {
|
||||
extern "C" {
|
||||
// Need sdl_glsl output feature to be enabled on libcava
|
||||
#ifndef SDL_GLSL
|
||||
#define SDL_GLSL
|
||||
#endif
|
||||
|
||||
#include <cava/common.h>
|
||||
|
||||
#ifdef SDL_GLSL
|
||||
#undef SDL_GLSL
|
||||
#endif
|
||||
}
|
||||
} // namespace cava
|
||||
|
||||
namespace waybar::modules::cava {
|
||||
using namespace std::literals::chrono_literals;
|
||||
|
||||
class CavaBackend final {
|
||||
public:
|
||||
static std::shared_ptr<CavaBackend> inst(const Json::Value& config);
|
||||
|
||||
virtual ~CavaBackend();
|
||||
// Methods
|
||||
int getAsciiRange();
|
||||
void doPauseResume();
|
||||
void Update();
|
||||
// Signal accessor
|
||||
using type_signal_update = sigc::signal<void(const std::string&)>;
|
||||
type_signal_update signal_update();
|
||||
using type_signal_silence = sigc::signal<void()>;
|
||||
type_signal_silence signal_silence();
|
||||
|
||||
private:
|
||||
CavaBackend(const Json::Value& config);
|
||||
util::SleeperThread thread_;
|
||||
util::SleeperThread read_thread_;
|
||||
// Cava API to read audio source
|
||||
::cava::ptr input_source_;
|
||||
|
||||
struct ::cava::error_s error_{}; // cava errors
|
||||
struct ::cava::config_params prm_{}; // cava parameters
|
||||
struct ::cava::audio_raw audio_raw_{}; // cava handled raw audio data(is based on audio_data)
|
||||
struct ::cava::audio_data audio_data_{}; // cava audio data
|
||||
struct ::cava::cava_plan* plan_; //{new cava_plan{}};
|
||||
|
||||
std::chrono::seconds fetch_input_delay_{4};
|
||||
// Delay to handle audio source
|
||||
std::chrono::milliseconds frame_time_milsec_{1s};
|
||||
|
||||
int re_paint_{0};
|
||||
bool silence_{false};
|
||||
bool silence_prev_{false};
|
||||
std::chrono::seconds suspend_silence_delay_{0};
|
||||
int sleep_counter_{0};
|
||||
std::string output_{};
|
||||
// Methods
|
||||
void invoke();
|
||||
void execute();
|
||||
bool isSilence();
|
||||
void doUpdate(bool force = false);
|
||||
|
||||
// Signal
|
||||
type_signal_update m_signal_update_;
|
||||
type_signal_silence m_signal_silence_;
|
||||
};
|
||||
} // namespace waybar::modules::cava
|
@ -48,8 +48,9 @@ class Clock final : public ALabel {
|
||||
std::string cldYearCached_; // calendar Year mode. Cached calendar
|
||||
date::year_month cldMonShift_; // calendar Month mode. Cached ym
|
||||
std::string cldMonCached_; // calendar Month mode. Cached calendar
|
||||
date::day cldBaseDay_{0}; // calendar Cached day. Is used when today is changing(midnight)
|
||||
std::string cldText_{""}; // calendar text to print
|
||||
date::day cldBaseDay_{0}; // calendar Cached day. Is used when today is changing(midnight)
|
||||
std::string cldText_{""}; // calendar text to print
|
||||
bool iso8601Calendar_{false}; // whether the calendar is in ISO8601
|
||||
CldMode cldMode_{CldMode::MONTH};
|
||||
auto get_calendar(const date::year_month_day& today, const date::year_month_day& ymd,
|
||||
const date::time_zone* tz) -> const std::string;
|
||||
@ -62,6 +63,7 @@ class Clock final : public ALabel {
|
||||
std::vector<const date::time_zone*> tzList_; // time zones list
|
||||
int tzCurrIdx_; // current time zone index for tzList_
|
||||
std::string tzText_{""}; // time zones text to print
|
||||
std::string tzTooltipFormat_{""}; // optional timezone tooltip format
|
||||
util::SleeperThread thread_;
|
||||
|
||||
// ordinal date in tooltip
|
||||
|
@ -136,8 +136,6 @@ class Workspace {
|
||||
Gtk::Button button_;
|
||||
Gtk::Box content_;
|
||||
Gtk::Label label_;
|
||||
|
||||
bool needs_updating_ = false;
|
||||
};
|
||||
|
||||
} // namespace waybar::modules::ext
|
||||
|
@ -46,6 +46,7 @@ class IPC {
|
||||
util::JsonParser parser_;
|
||||
std::list<std::pair<std::string, EventHandler*>> callbacks_;
|
||||
int socketfd_; // the hyprland socket file descriptor
|
||||
pid_t socketOwnerPid_;
|
||||
bool running_ = true; // the ipcThread will stop running when this is false
|
||||
};
|
||||
}; // namespace waybar::modules::hyprland
|
||||
|
@ -42,7 +42,6 @@ class Workspace {
|
||||
bool isPersistentConfig() const { return m_isPersistentConfig; };
|
||||
bool isPersistentRule() const { return m_isPersistentRule; };
|
||||
bool isVisible() const { return m_isVisible; };
|
||||
bool isEmpty() const { return m_windows == 0; };
|
||||
bool isUrgent() const { return m_isUrgent; };
|
||||
|
||||
bool handleClicked(GdkEventButton* bt) const;
|
||||
@ -88,6 +87,7 @@ class Workspace {
|
||||
Gtk::Label m_labelBefore;
|
||||
Gtk::Label m_labelAfter;
|
||||
|
||||
bool isEmpty() const;
|
||||
void updateTaskbar(const std::string& workspace_icon);
|
||||
bool handleClick(const GdkEventButton* event_button, WindowAddress const& addr) const;
|
||||
bool shouldSkipWindow(const WindowRepr& window_repr) const;
|
||||
|
@ -51,9 +51,13 @@ class Workspaces : public AModule, public EventHandler {
|
||||
auto taskbarFormatAfter() const -> std::string { return m_taskbarFormatAfter; }
|
||||
auto taskbarIconSize() const -> int { return m_taskbarIconSize; }
|
||||
auto taskbarOrientation() const -> Gtk::Orientation { return m_taskbarOrientation; }
|
||||
auto taskbarReverseDirection() const -> bool { return m_taskbarReverseDirection; }
|
||||
auto onClickWindow() const -> std::string { return m_onClickWindow; }
|
||||
auto getIgnoredWindows() const -> std::vector<std::regex> { return m_ignoreWindows; }
|
||||
|
||||
enum class ActiveWindowPosition { NONE, FIRST, LAST };
|
||||
auto activeWindowPosition() const -> ActiveWindowPosition { return m_activeWindowPosition; }
|
||||
|
||||
std::string getRewrite(std::string window_class, std::string window_title);
|
||||
std::string& getWindowSeparator() { return m_formatWindowSeparator; }
|
||||
bool isWorkspaceIgnored(std::string const& workspace_name);
|
||||
@ -183,6 +187,14 @@ class Workspaces : public AModule, public EventHandler {
|
||||
std::string m_taskbarFormatAfter;
|
||||
int m_taskbarIconSize = 16;
|
||||
Gtk::Orientation m_taskbarOrientation = Gtk::ORIENTATION_HORIZONTAL;
|
||||
bool m_taskbarReverseDirection = false;
|
||||
util::EnumParser<ActiveWindowPosition> m_activeWindowEnumParser;
|
||||
ActiveWindowPosition m_activeWindowPosition = ActiveWindowPosition::NONE;
|
||||
std::map<std::string, ActiveWindowPosition> m_activeWindowPositionMap = {
|
||||
{"NONE", ActiveWindowPosition::NONE},
|
||||
{"FIRST", ActiveWindowPosition::FIRST},
|
||||
{"LAST", ActiveWindowPosition::LAST},
|
||||
};
|
||||
std::string m_onClickWindow;
|
||||
std::string m_currentActiveWindowAddress;
|
||||
|
||||
|
@ -31,7 +31,7 @@ class Image : public AModule {
|
||||
std::string path_;
|
||||
std::string tooltip_;
|
||||
int size_;
|
||||
int interval_;
|
||||
std::chrono::milliseconds interval_;
|
||||
util::command::res output_;
|
||||
|
||||
util::SleeperThread thread_;
|
||||
|
@ -33,6 +33,7 @@ class Language : public ALabel, public EventHandler {
|
||||
|
||||
std::vector<Layout> layouts_;
|
||||
unsigned current_idx_;
|
||||
std::string last_short_name_;
|
||||
};
|
||||
|
||||
} // namespace waybar::modules::niri
|
||||
|
@ -21,6 +21,7 @@ class Tags : public waybar::AModule {
|
||||
void handle_view_tags(struct wl_array *tags);
|
||||
void handle_urgent_tags(uint32_t tags);
|
||||
|
||||
void handle_show();
|
||||
void handle_primary_clicked(uint32_t tag);
|
||||
bool handle_button_press(GdkEventButton *event_button, uint32_t tag);
|
||||
|
||||
|
@ -26,7 +26,7 @@ struct ToolTip {
|
||||
class Item : public sigc::trackable {
|
||||
public:
|
||||
Item(const std::string&, const std::string&, const Json::Value&, const Bar&);
|
||||
~Item() = default;
|
||||
~Item();
|
||||
|
||||
std::string bus_name;
|
||||
std::string object_path;
|
||||
|
@ -39,6 +39,12 @@ $XDG_CONFIG_HOME/waybar/config ++
|
||||
:[ A list of timezones (as in *timezone*) to use for time display, changed using
|
||||
the scroll wheel. Do not specify *timezone* option when *timezones* is specified.
|
||||
"" represents the system's local timezone
|
||||
|[ *timezone-tooltip-format*
|
||||
:[ string
|
||||
:[
|
||||
:[ Format to use for displaying timezones in the tooltip. When set, this allows showing
|
||||
timezone information (like timezone abbreviations) in the tooltip while keeping the
|
||||
main display clean. Uses the same format options as *format*
|
||||
|[ *locale*
|
||||
:[ string
|
||||
:[
|
||||
@ -126,6 +132,12 @@ View all valid format options in *strftime(3)* or have a look https://en.cpprefe
|
||||
:[ 1
|
||||
:[ Value to scroll months/years forward/backward. Can be negative. Is
|
||||
configured under *on-scroll* option
|
||||
|[ *iso8601*
|
||||
:[ bool
|
||||
:[ false
|
||||
:[ When enabled, the calendar follows the ISO 8601 standard: weeks begin on
|
||||
Monday, and the first week of the year is numbered 1. The default week format is
|
||||
'{:%V}'.
|
||||
|
||||
3. Addressed by *clock: calendar: format*
|
||||
[- *Option*
|
||||
@ -229,6 +241,25 @@ View all valid format options in *strftime(3)* or have a look https://en.cpprefe
|
||||
}
|
||||
```
|
||||
|
||||
4. Show timezone in tooltip only
|
||||
|
||||
```
|
||||
"clock": {
|
||||
"interval": 60,
|
||||
"format": "{:%H:%M}",
|
||||
"timezone-tooltip-format": "{:%H:%M %Z}",
|
||||
"timezones": [
|
||||
"",
|
||||
"America/Chicago",
|
||||
"America/Los_Angeles",
|
||||
"Europe/Paris",
|
||||
"UTC"
|
||||
],
|
||||
"tooltip": true,
|
||||
"tooltip-format": "{tz_list}"
|
||||
}
|
||||
```
|
||||
|
||||
# STYLE
|
||||
|
||||
- *#clock*
|
||||
|
@ -11,9 +11,10 @@ The *cpu* module displays the current CPU utilization.
|
||||
# CONFIGURATION
|
||||
|
||||
*interval*: ++
|
||||
typeof: integer ++
|
||||
typeof: integer or float ++
|
||||
default: 10 ++
|
||||
The interval in which the information gets polled.
|
||||
The interval in which the information gets polled. ++
|
||||
Minimum value is 0.001 (1ms). Values smaller than 1ms will be set to 1ms.
|
||||
|
||||
*format*: ++
|
||||
typeof: string ++
|
||||
|
@ -35,15 +35,17 @@ Addressed by *custom/<name>*
|
||||
See *return-type*
|
||||
|
||||
*interval*: ++
|
||||
typeof: integer ++
|
||||
typeof: integer or float ++
|
||||
The interval (in seconds) in which the information gets polled. ++
|
||||
Minimum value is 0.001 (1ms). Values smaller than 1ms will be set to 1ms. ++
|
||||
Use *once* if you want to execute the module only on startup. ++
|
||||
You can update it manually with a signal. If no *interval* or *signal* is defined, it is assumed that the out script loops itself. ++
|
||||
If a *signal* is defined then the script will run once on startup and will only update with a signal.
|
||||
|
||||
*restart-interval*: ++
|
||||
typeof: integer ++
|
||||
typeof: integer or float ++
|
||||
The restart interval (in seconds). ++
|
||||
Minimum value is 0.001 (1ms). Values smaller than 1ms will be set to 1ms. ++
|
||||
Can't be used with the *interval* option, so only with continuous scripts. ++
|
||||
Once the script exits, it'll be re-executed after the *restart-interval*.
|
||||
|
||||
|
@ -64,6 +64,8 @@ Addressed by *ext/workspaces*
|
||||
|
||||
*activate*: Switch to workspace.
|
||||
|
||||
*deactivate*: Deactivate the workspace.
|
||||
|
||||
*close*: Close the workspace.
|
||||
|
||||
# ICONS
|
||||
|
@ -50,6 +50,21 @@ This setting is ignored if *workspace-taskbar.enable* is set to true.
|
||||
default: false ++
|
||||
Enables the workspace taskbar mode.
|
||||
|
||||
*update-active-window*: ++
|
||||
typeof: bool ++
|
||||
default: false ++
|
||||
If true, the active/focused window will have an 'active' class. Could cause higher CPU usage due to more frequent redraws.
|
||||
|
||||
*reverse-direction*: ++
|
||||
typeof: bool ++
|
||||
default: false ++
|
||||
If true, the taskbar windows will be added in reverse order (right to left if orientation is horizontal, bottom to top if vertical).
|
||||
|
||||
*active-window-position*: ++
|
||||
typeof: "none" | "first" | "last" ++
|
||||
default: "none" ++
|
||||
If set to "first", the active window will be moved at the beginning of the taskbar. If set to "last", it will be moved at the end. It will only work if *update-active-window* is set to true.
|
||||
|
||||
*format*: ++
|
||||
typeof: string ++
|
||||
default: {icon} ++
|
||||
@ -70,6 +85,19 @@ This setting is ignored if *workspace-taskbar.enable* is set to true.
|
||||
default: horizontal ++
|
||||
Direction in which the workspace taskbar is displayed.
|
||||
|
||||
*ignore-list*: ++
|
||||
typeof: array ++
|
||||
default: [] ++
|
||||
Regex patterns to match against window class or window title. If a window's class OR title matches any of the patterns, it will not be shown.
|
||||
|
||||
*on-click-window*: ++
|
||||
typeof: string ++
|
||||
default: "" ++
|
||||
Command to run when a window is clicked. Available placeholders are: ++
|
||||
- {address} Hyprland address of the clicked window. ++
|
||||
- {button} Pressed button number, see https://api.gtkd.org/gdk.c.types.GdkEventButton.button.html. ++
|
||||
See https://github.com/Alexays/Waybar/wiki/Module:-Hyprland#workspace-taskbars-example for a full example.
|
||||
|
||||
*show-special*: ++
|
||||
typeof: bool ++
|
||||
default: false ++
|
||||
@ -216,4 +244,6 @@ Additional to workspace name matching, the following *format-icons* can be set.
|
||||
- *#workspaces button.special*
|
||||
- *#workspaces button.urgent*
|
||||
- *#workspaces button.hosting-monitor* (gets applied if workspace-monitor == waybar-monitor)
|
||||
- *#workspaces .taskbar-window* (each window in the taskbar)
|
||||
- *#workspaces .workspace-label*
|
||||
- *#workspaces .taskbar-window* (each window in the taskbar, only if 'workspace-taskbar.enable' is true)
|
||||
- *#workspaces .taskbar-window.active* (applied to the focused window, only if 'workspace-taskbar.update-active-window' is true)
|
||||
|
@ -24,8 +24,9 @@ The *image* module displays an image from a path.
|
||||
The width/height to render the image.
|
||||
|
||||
*interval*: ++
|
||||
typeof: integer ++
|
||||
typeof: integer or float ++
|
||||
The interval (in seconds) to re-render the image. ++
|
||||
Minimum value is 0.001 (1ms). Values smaller than 1ms will be set to 1ms. ++
|
||||
This is useful if the contents of *path* changes. ++
|
||||
If no *interval* is defined, the image will only be rendered once.
|
||||
|
||||
|
@ -45,7 +45,7 @@ Addressed by *jack*
|
||||
The format of information displayed in the tooltip.
|
||||
|
||||
*interval*: ++
|
||||
typeof: integer ++
|
||||
typeof: integer or float ++
|
||||
default: 1 ++
|
||||
The interval in which the information gets polled.
|
||||
|
||||
|
@ -87,15 +87,15 @@ Module config :
|
||||
```
|
||||
"custom/power": {
|
||||
"format" : "⏻ ",
|
||||
"tooltip": false,
|
||||
"menu": "on-click",
|
||||
"menu-file": "~/.config/waybar/power_menu.xml",
|
||||
"menu-actions": {
|
||||
"shutdown": "shutdown",
|
||||
"reboot": "reboot",
|
||||
"suspend": "systemctl suspend",
|
||||
"hibernate": "systemctl hibernate",
|
||||
},
|
||||
"tooltip": false,
|
||||
"menu": "on-click",
|
||||
"menu-file": "~/.config/waybar/power_menu.xml",
|
||||
"menu-actions": {
|
||||
"shutdown": "shutdown",
|
||||
"reboot": "reboot",
|
||||
"suspend": "systemctl suspend",
|
||||
"hibernate": "systemctl hibernate",
|
||||
},
|
||||
},
|
||||
```
|
||||
|
||||
@ -104,28 +104,28 @@ Module config :
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<object class="GtkMenu" id="menu">
|
||||
<child>
|
||||
<object class="GtkMenuItem" id="suspend">
|
||||
<property name="label">Suspend</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkMenuItem" id="hibernat">
|
||||
<property name="label">Hibernate</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkMenuItem" id="suspend">
|
||||
<property name="label">Suspend</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkMenuItem" id="hibernate">
|
||||
<property name="label">Hibernate</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkMenuItem" id="shutdown">
|
||||
<property name="label">Shutdown</property>
|
||||
</object>
|
||||
<object class="GtkMenuItem" id="shutdown">
|
||||
<property name="label">Shutdown</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSeparatorMenuItem" id="delimiter1"/>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkMenuItem" id="reboot">
|
||||
<property name="label">Reboot</property>
|
||||
</object>
|
||||
<object class="GtkMenuItem" id="reboot">
|
||||
<property name="label">Reboot</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
||||
|
@ -52,8 +52,8 @@ Addressed by *niri/language*
|
||||
|
||||
```
|
||||
"niri/language": {
|
||||
"format": "Lang: {long}"
|
||||
"format-en": "AMERICA, HELL YEAH!"
|
||||
"format": "Lang: {long}",
|
||||
"format-en": "AMERICA, HELL YEAH!",
|
||||
"format-tr": "As bayrakları"
|
||||
}
|
||||
```
|
||||
@ -61,3 +61,12 @@ Addressed by *niri/language*
|
||||
# STYLE
|
||||
|
||||
- *#language*
|
||||
|
||||
Additionally, a CSS class matching the current layout's short name is added to the widget. This
|
||||
allows per-language styling, for example:
|
||||
|
||||
```
|
||||
#language.us { color: #00ff00; }
|
||||
#language.de { color: #ff0000; }
|
||||
#language.fr { color: #0000ff; }
|
||||
```
|
||||
|
@ -40,7 +40,7 @@ Addressed by *temperature*
|
||||
The threshold before it is considered critical (Celsius).
|
||||
|
||||
*interval*: ++
|
||||
typeof: integer ++
|
||||
typeof: integer or float ++
|
||||
default: 10 ++
|
||||
The interval in which the information gets polled.
|
||||
|
||||
@ -160,4 +160,5 @@ Addressed by *temperature*
|
||||
# STYLE
|
||||
|
||||
- *#temperature*
|
||||
- *#temperature.warning*
|
||||
- *#temperature.critical*
|
||||
|
@ -437,4 +437,4 @@ A group may hide all but one element, showing them only on mouse hover. In order
|
||||
# SEE ALSO
|
||||
|
||||
*sway-output(5)*
|
||||
*waybar-styles(5)"
|
||||
*waybar-styles(5)*
|
||||
|
@ -69,7 +69,7 @@ is_openbsd = host_machine.system() == 'openbsd'
|
||||
|
||||
thread_dep = dependency('threads')
|
||||
fmt = dependency('fmt', version : ['>=8.1.1'], fallback : ['fmt', 'fmt_dep'])
|
||||
spdlog = dependency('spdlog', version : ['>=1.10.0'], fallback : ['spdlog', 'spdlog_dep'], default_options : ['external_fmt=enabled', 'std_format=disabled', 'tests=disabled'])
|
||||
spdlog = dependency('spdlog', version : ['>=1.15.2'], fallback : ['spdlog', 'spdlog_dep'], default_options : ['std_format=disabled', 'tests=disabled'])
|
||||
wayland_client = dependency('wayland-client')
|
||||
wayland_cursor = dependency('wayland-cursor')
|
||||
wayland_protos = dependency('wayland-protocols')
|
||||
@ -505,7 +505,7 @@ cava = dependency('cava',
|
||||
|
||||
if cava.found()
|
||||
add_project_arguments('-DHAVE_LIBCAVA', language: 'cpp')
|
||||
src_files += files('src/modules/cava.cpp')
|
||||
src_files += files('src/modules/cava/cava.cpp', 'src/modules/cava/cava_backend.cpp')
|
||||
man_files += files('man/waybar-cava.5.scd')
|
||||
endif
|
||||
|
||||
|
@ -30,13 +30,12 @@ in
|
||||
# nixpkgs checks version, no need when building locally
|
||||
nativeInstallCheckInputs = [ ];
|
||||
|
||||
buildInputs = (builtins.filter (p:
|
||||
p.pname != "wireplumber" &&
|
||||
p.pname != "gps"
|
||||
) oldAttrs.buildInputs) ++ [
|
||||
pkgs.wireplumber
|
||||
pkgs.gpsd
|
||||
];
|
||||
buildInputs =
|
||||
(builtins.filter (p: p.pname != "wireplumber" && p.pname != "gps") oldAttrs.buildInputs)
|
||||
++ [
|
||||
pkgs.wireplumber
|
||||
pkgs.gpsd
|
||||
];
|
||||
|
||||
postUnpack = ''
|
||||
pushd "$sourceRoot"
|
||||
|
@ -63,7 +63,7 @@ button:hover {
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
#workspaces button.focused {
|
||||
#workspaces button.focused, #workspaces button.active {
|
||||
background-color: #64727D;
|
||||
box-shadow: inset 0 -3px #ffffff;
|
||||
}
|
||||
|
@ -63,7 +63,8 @@ std::optional<std::string> getDesktopFilePath(const std::string& app_identifier,
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto data_dirs = Glib::get_system_data_dirs();
|
||||
auto data_dirs = Glib::get_system_data_dirs();
|
||||
data_dirs.insert(data_dirs.begin(), Glib::get_user_data_dir());
|
||||
for (const auto& data_dir : data_dirs) {
|
||||
const auto data_app_dir = data_dir + "/applications/";
|
||||
auto desktop_file_suffix = app_identifier + ".desktop";
|
||||
|
@ -36,11 +36,12 @@ AIconLabel::AIconLabel(const Json::Value &config, const std::string &name, const
|
||||
box_.set_spacing(spacing);
|
||||
|
||||
bool swap_icon_label = false;
|
||||
if (config_.isMember("swap-icon-label")) {
|
||||
if (!config_["swap-icon-label"].isBool())
|
||||
spdlog::warn("'swap-icon-label' must be a bool.");
|
||||
else
|
||||
swap_icon_label = config_["swap-icon-label"].asBool();
|
||||
if (config_["swap-icon-label"].isNull()) {
|
||||
} else if (config_["swap-icon-label"].isBool()) {
|
||||
swap_icon_label = config_["swap-icon-label"].asBool();
|
||||
} else {
|
||||
spdlog::warn("'swap-icon-label' must be a bool, found '{}'. Using default value (false).",
|
||||
config_["swap-icon-label"].asString());
|
||||
}
|
||||
|
||||
if ((rot == 0 || rot == 3) ^ swap_icon_label) {
|
||||
|
@ -17,10 +17,17 @@ ALabel::ALabel(const Json::Value& config, const std::string& name, const std::st
|
||||
config["format-alt"].isString() || config["menu"].isString() || enable_click,
|
||||
enable_scroll),
|
||||
format_(config_["format"].isString() ? config_["format"].asString() : format),
|
||||
|
||||
// Leave the default option outside of the std::max(1L, ...), because the zero value
|
||||
// (default) is used in modules/custom.cpp to make the difference between
|
||||
// two types of custom scripts. Fixes #4521.
|
||||
interval_(config_["interval"] == "once"
|
||||
? std::chrono::seconds::max()
|
||||
: std::chrono::seconds(
|
||||
config_["interval"].isUInt() ? config_["interval"].asUInt() : interval)),
|
||||
? std::chrono::milliseconds::max()
|
||||
: std::chrono::milliseconds(
|
||||
(config_["interval"].isNumeric()
|
||||
? std::max(1L, // Minimum 1ms due to millisecond precision
|
||||
static_cast<long>(config_["interval"].asDouble()) * 1000)
|
||||
: 1000 * (long)interval))),
|
||||
default_format_(format_) {
|
||||
label_.set_name(name);
|
||||
if (!id.empty()) {
|
||||
|
@ -172,6 +172,10 @@ bool AModule::handleUserEvent(GdkEventButton* const& e) {
|
||||
// Popup the menu
|
||||
gtk_widget_show_all(GTK_WIDGET(menu_));
|
||||
gtk_menu_popup_at_pointer(GTK_MENU(menu_), reinterpret_cast<GdkEvent*>(e));
|
||||
// Manually reset prelight to make sure the module doesn't stay in a hover state
|
||||
if (auto* module = event_box_.get_child(); module != nullptr) {
|
||||
module->unset_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Second call user scripts
|
||||
|
13
src/bar.cpp
13
src/bar.cpp
@ -433,7 +433,18 @@ void waybar::Bar::onMap(GdkEventAny* /*unused*/) {
|
||||
/*
|
||||
* Obtain a pointer to the custom layer surface for modules that require it (idle_inhibitor).
|
||||
*/
|
||||
auto* gdk_window = window.get_window()->gobj();
|
||||
auto gdk_window_ref = window.get_window();
|
||||
if (!gdk_window_ref) {
|
||||
spdlog::warn("Failed to get GDK window during onMap, deferring surface initialization");
|
||||
return;
|
||||
}
|
||||
|
||||
auto* gdk_window = gdk_window_ref->gobj();
|
||||
if (!gdk_window) {
|
||||
spdlog::warn("GDK window object is null during onMap, deferring surface initialization");
|
||||
return;
|
||||
}
|
||||
|
||||
surface = gdk_wayland_window_get_wl_surface(gdk_window);
|
||||
configureGlobalOffset(gdk_window_get_width(gdk_window), gdk_window_get_height(gdk_window));
|
||||
|
||||
|
@ -106,12 +106,24 @@ void Config::setupConfig(Json::Value &dst, const std::string &config_file, int d
|
||||
mergeConfig(dst, tmp_config);
|
||||
}
|
||||
|
||||
std::optional<std::string> Config::findIncludePath(const std::string &name) {
|
||||
std::vector<std::string> Config::findIncludePath(const std::string &name,
|
||||
const std::vector<std::string> &dirs) {
|
||||
auto match1 = tryExpandPath(name, "");
|
||||
if (!match1.empty()) {
|
||||
return match1.front();
|
||||
return match1;
|
||||
}
|
||||
return findConfigPath({name});
|
||||
if (const char *dir = std::getenv(Config::CONFIG_PATH_ENV)) {
|
||||
if (auto res = tryExpandPath(dir, name); !res.empty()) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
for (const auto &dir : dirs) {
|
||||
if (auto res = tryExpandPath(dir, name); !res.empty()) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void Config::resolveConfigIncludes(Json::Value &config, int depth) {
|
||||
@ -119,18 +131,22 @@ void Config::resolveConfigIncludes(Json::Value &config, int depth) {
|
||||
if (includes.isArray()) {
|
||||
for (const auto &include : includes) {
|
||||
spdlog::info("Including resource file: {}", include.asString());
|
||||
auto match = findIncludePath(include.asString());
|
||||
if (match.has_value()) {
|
||||
setupConfig(config, match.value(), depth + 1);
|
||||
auto matches = findIncludePath(include.asString());
|
||||
if (!matches.empty()) {
|
||||
for (const auto &match : matches) {
|
||||
setupConfig(config, match, depth + 1);
|
||||
}
|
||||
} else {
|
||||
spdlog::warn("Unable to find resource file: {}", include.asString());
|
||||
}
|
||||
}
|
||||
} else if (includes.isString()) {
|
||||
spdlog::info("Including resource file: {}", includes.asString());
|
||||
auto match = findIncludePath(includes.asString());
|
||||
if (match.has_value()) {
|
||||
setupConfig(config, match.value(), depth + 1);
|
||||
auto matches = findIncludePath(includes.asString());
|
||||
if (!matches.empty()) {
|
||||
for (const auto &match : matches) {
|
||||
setupConfig(config, match, depth + 1);
|
||||
}
|
||||
} else {
|
||||
spdlog::warn("Unable to find resource file: {}", includes.asString());
|
||||
}
|
||||
|
@ -109,7 +109,7 @@
|
||||
#include "modules/wireplumber.hpp"
|
||||
#endif
|
||||
#ifdef HAVE_LIBCAVA
|
||||
#include "modules/cava.hpp"
|
||||
#include "modules/cava/cava.hpp"
|
||||
#endif
|
||||
#ifdef HAVE_SYSTEMD_MONITOR
|
||||
#include "modules/systemd_failed_units.hpp"
|
||||
@ -343,7 +343,7 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name,
|
||||
#endif
|
||||
#ifdef HAVE_LIBCAVA
|
||||
if (ref == "cava") {
|
||||
return new waybar::modules::Cava(id, config_[name]);
|
||||
return new waybar::modules::cava::Cava(id, config_[name]);
|
||||
}
|
||||
#endif
|
||||
#ifdef HAVE_SYSTEMD_MONITOR
|
||||
|
@ -1,211 +0,0 @@
|
||||
#include "modules/cava.hpp"
|
||||
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
waybar::modules::Cava::Cava(const std::string& id, const Json::Value& config)
|
||||
: ALabel(config, "cava", id, "{}", 60, false, false, false) {
|
||||
// Load waybar module config
|
||||
char cfgPath[PATH_MAX];
|
||||
cfgPath[0] = '\0';
|
||||
|
||||
if (config_["cava_config"].isString()) strcpy(cfgPath, config_["cava_config"].asString().data());
|
||||
// Load cava config
|
||||
error_.length = 0;
|
||||
|
||||
if (!load_config(cfgPath, &prm_, false, &error_)) {
|
||||
spdlog::error("Error loading config. {0}", error_.message);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Override cava parameters by the user config
|
||||
prm_.inAtty = 0;
|
||||
prm_.output = cava::output_method::OUTPUT_RAW;
|
||||
strcpy(prm_.data_format, "ascii");
|
||||
strcpy(prm_.raw_target, "/dev/stdout");
|
||||
prm_.ascii_range = config_["format-icons"].size() - 1;
|
||||
|
||||
prm_.bar_width = 2;
|
||||
prm_.bar_spacing = 0;
|
||||
prm_.bar_height = 32;
|
||||
prm_.bar_width = 1;
|
||||
prm_.orientation = cava::ORIENT_TOP;
|
||||
prm_.xaxis = cava::xaxis_scale::NONE;
|
||||
prm_.mono_opt = cava::AVERAGE;
|
||||
prm_.autobars = 0;
|
||||
prm_.gravity = 0;
|
||||
prm_.integral = 1;
|
||||
|
||||
if (config_["framerate"].isInt()) prm_.framerate = config_["framerate"].asInt();
|
||||
if (config_["autosens"].isInt()) prm_.autosens = config_["autosens"].asInt();
|
||||
if (config_["sensitivity"].isInt()) prm_.sens = config_["sensitivity"].asInt();
|
||||
if (config_["bars"].isInt()) prm_.fixedbars = config_["bars"].asInt();
|
||||
if (config_["lower_cutoff_freq"].isNumeric())
|
||||
prm_.lower_cut_off = config_["lower_cutoff_freq"].asLargestInt();
|
||||
if (config_["higher_cutoff_freq"].isNumeric())
|
||||
prm_.upper_cut_off = config_["higher_cutoff_freq"].asLargestInt();
|
||||
if (config_["sleep_timer"].isInt()) prm_.sleep_timer = config_["sleep_timer"].asInt();
|
||||
if (config_["method"].isString())
|
||||
prm_.input = cava::input_method_by_name(config_["method"].asString().c_str());
|
||||
if (config_["source"].isString()) prm_.audio_source = config_["source"].asString().data();
|
||||
if (config_["sample_rate"].isNumeric()) prm_.samplerate = config_["sample_rate"].asLargestInt();
|
||||
if (config_["sample_bits"].isInt()) prm_.samplebits = config_["sample_bits"].asInt();
|
||||
if (config_["stereo"].isBool()) prm_.stereo = config_["stereo"].asBool();
|
||||
if (config_["reverse"].isBool()) prm_.reverse = config_["reverse"].asBool();
|
||||
if (config_["bar_delimiter"].isInt()) prm_.bar_delim = config_["bar_delimiter"].asInt();
|
||||
if (config_["monstercat"].isBool()) prm_.monstercat = config_["monstercat"].asBool();
|
||||
if (config_["waves"].isBool()) prm_.waves = config_["waves"].asBool();
|
||||
if (config_["noise_reduction"].isDouble())
|
||||
prm_.noise_reduction = config_["noise_reduction"].asDouble();
|
||||
if (config_["input_delay"].isInt())
|
||||
fetch_input_delay_ = std::chrono::seconds(config_["input_delay"].asInt());
|
||||
if (config_["hide_on_silence"].isBool()) hide_on_silence_ = config_["hide_on_silence"].asBool();
|
||||
if (config_["format_silent"].isString()) format_silent_ = config_["format_silent"].asString();
|
||||
// Make cava parameters configuration
|
||||
plan_ = new cava::cava_plan{};
|
||||
|
||||
audio_raw_.height = prm_.ascii_range;
|
||||
audio_data_.format = -1;
|
||||
audio_data_.source = new char[1 + strlen(prm_.audio_source)];
|
||||
audio_data_.source[0] = '\0';
|
||||
strcpy(audio_data_.source, prm_.audio_source);
|
||||
|
||||
audio_data_.rate = 0;
|
||||
audio_data_.samples_counter = 0;
|
||||
audio_data_.channels = 2;
|
||||
audio_data_.IEEE_FLOAT = 0;
|
||||
|
||||
audio_data_.input_buffer_size = BUFFER_SIZE * audio_data_.channels;
|
||||
audio_data_.cava_buffer_size = audio_data_.input_buffer_size * 8;
|
||||
|
||||
audio_data_.cava_in = new double[audio_data_.cava_buffer_size]{0.0};
|
||||
|
||||
audio_data_.terminate = 0;
|
||||
audio_data_.suspendFlag = false;
|
||||
input_source_ = get_input(&audio_data_, &prm_);
|
||||
|
||||
if (!input_source_) {
|
||||
spdlog::error("cava API didn't provide input audio source method");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
// Calculate delay for Update() thread
|
||||
frame_time_milsec_ = std::chrono::milliseconds((int)(1e3 / prm_.framerate));
|
||||
|
||||
// Init cava plan, audio_raw structure
|
||||
audio_raw_init(&audio_data_, &audio_raw_, &prm_, plan_);
|
||||
if (!plan_) spdlog::error("cava plan is not provided");
|
||||
audio_raw_.previous_frame[0] = -1; // For first Update() call need to rePaint text message
|
||||
// Read audio source trough cava API. Cava orginizes this process via infinity loop
|
||||
thread_fetch_input_ = [this] {
|
||||
thread_fetch_input_.sleep_for(fetch_input_delay_);
|
||||
input_source_(&audio_data_);
|
||||
};
|
||||
|
||||
thread_ = [this] {
|
||||
dp.emit();
|
||||
thread_.sleep_for(frame_time_milsec_);
|
||||
};
|
||||
}
|
||||
|
||||
waybar::modules::Cava::~Cava() {
|
||||
thread_fetch_input_.stop();
|
||||
thread_.stop();
|
||||
delete plan_;
|
||||
plan_ = nullptr;
|
||||
}
|
||||
|
||||
void upThreadDelay(std::chrono::milliseconds& delay, std::chrono::seconds& delta) {
|
||||
if (delta == std::chrono::seconds{0}) {
|
||||
delta += std::chrono::seconds{1};
|
||||
delay += delta;
|
||||
}
|
||||
}
|
||||
|
||||
void downThreadDelay(std::chrono::milliseconds& delay, std::chrono::seconds& delta) {
|
||||
if (delta > std::chrono::seconds{0}) {
|
||||
delay -= delta;
|
||||
delta -= std::chrono::seconds{1};
|
||||
}
|
||||
}
|
||||
|
||||
auto waybar::modules::Cava::update() -> void {
|
||||
if (audio_data_.suspendFlag) return;
|
||||
silence_ = true;
|
||||
|
||||
for (int i{0}; i < audio_data_.input_buffer_size; ++i) {
|
||||
if (audio_data_.cava_in[i]) {
|
||||
silence_ = false;
|
||||
sleep_counter_ = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (silence_ && prm_.sleep_timer != 0) {
|
||||
if (sleep_counter_ <=
|
||||
(int)(std::chrono::milliseconds(prm_.sleep_timer * 1s) / frame_time_milsec_)) {
|
||||
++sleep_counter_;
|
||||
silence_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!silence_ || prm_.sleep_timer == 0) {
|
||||
downThreadDelay(frame_time_milsec_, suspend_silence_delay_);
|
||||
// Process: execute cava
|
||||
pthread_mutex_lock(&audio_data_.lock);
|
||||
cava::cava_execute(audio_data_.cava_in, audio_data_.samples_counter, audio_raw_.cava_out,
|
||||
plan_);
|
||||
if (audio_data_.samples_counter > 0) audio_data_.samples_counter = 0;
|
||||
pthread_mutex_unlock(&audio_data_.lock);
|
||||
|
||||
// Do transformation under raw data
|
||||
audio_raw_fetch(&audio_raw_, &prm_, &rePaint_, plan_);
|
||||
|
||||
if (rePaint_ == 1) {
|
||||
text_.clear();
|
||||
|
||||
for (int i{0}; i < audio_raw_.number_of_bars; ++i) {
|
||||
audio_raw_.previous_frame[i] = audio_raw_.bars[i];
|
||||
text_.append(
|
||||
getIcon((audio_raw_.bars[i] > prm_.ascii_range) ? prm_.ascii_range : audio_raw_.bars[i],
|
||||
"", prm_.ascii_range + 1));
|
||||
if (prm_.bar_delim != 0) text_.push_back(prm_.bar_delim);
|
||||
}
|
||||
|
||||
label_.set_markup(text_);
|
||||
label_.show();
|
||||
ALabel::update();
|
||||
label_.get_style_context()->add_class("updated");
|
||||
}
|
||||
|
||||
label_.get_style_context()->remove_class("silent");
|
||||
} else {
|
||||
upThreadDelay(frame_time_milsec_, suspend_silence_delay_);
|
||||
if (hide_on_silence_)
|
||||
label_.hide();
|
||||
else if (config_["format_silent"].isString())
|
||||
label_.set_markup(format_silent_);
|
||||
|
||||
label_.get_style_context()->add_class("silent");
|
||||
label_.get_style_context()->remove_class("updated");
|
||||
}
|
||||
}
|
||||
|
||||
auto waybar::modules::Cava::doAction(const std::string& name) -> void {
|
||||
if ((actionMap_[name])) {
|
||||
(this->*actionMap_[name])();
|
||||
} else
|
||||
spdlog::error("Cava. Unsupported action \"{0}\"", name);
|
||||
}
|
||||
|
||||
// Cava actions
|
||||
void waybar::modules::Cava::pause_resume() {
|
||||
pthread_mutex_lock(&audio_data_.lock);
|
||||
if (audio_data_.suspendFlag) {
|
||||
audio_data_.suspendFlag = false;
|
||||
pthread_cond_broadcast(&audio_data_.resumeCond);
|
||||
downThreadDelay(frame_time_milsec_, suspend_silence_delay_);
|
||||
} else {
|
||||
audio_data_.suspendFlag = true;
|
||||
upThreadDelay(frame_time_milsec_, suspend_silence_delay_);
|
||||
}
|
||||
pthread_mutex_unlock(&audio_data_.lock);
|
||||
}
|
51
src/modules/cava/cava.cpp
Normal file
51
src/modules/cava/cava.cpp
Normal file
@ -0,0 +1,51 @@
|
||||
#include "modules/cava/cava.hpp"
|
||||
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
waybar::modules::cava::Cava::Cava(const std::string& id, const Json::Value& config)
|
||||
: ALabel(config, "cava", id, "{}", 60, false, false, false),
|
||||
backend_{waybar::modules::cava::CavaBackend::inst(config)} {
|
||||
if (config_["hide_on_silence"].isBool()) hide_on_silence_ = config_["hide_on_silence"].asBool();
|
||||
if (config_["format_silent"].isString()) format_silent_ = config_["format_silent"].asString();
|
||||
|
||||
ascii_range_ = backend_->getAsciiRange();
|
||||
backend_->signal_update().connect(sigc::mem_fun(*this, &Cava::onUpdate));
|
||||
backend_->signal_silence().connect(sigc::mem_fun(*this, &Cava::onSilence));
|
||||
backend_->Update();
|
||||
}
|
||||
|
||||
auto waybar::modules::cava::Cava::doAction(const std::string& name) -> void {
|
||||
if ((actionMap_[name])) {
|
||||
(this->*actionMap_[name])();
|
||||
} else
|
||||
spdlog::error("Cava. Unsupported action \"{0}\"", name);
|
||||
}
|
||||
|
||||
// Cava actions
|
||||
void waybar::modules::cava::Cava::pause_resume() { backend_->doPauseResume(); }
|
||||
auto waybar::modules::cava::Cava::onUpdate(const std::string& input) -> void {
|
||||
if (silence_) {
|
||||
label_.get_style_context()->remove_class("silent");
|
||||
label_.get_style_context()->add_class("updated");
|
||||
}
|
||||
label_text_.clear();
|
||||
for (auto& ch : input)
|
||||
label_text_.append(getIcon((ch > ascii_range_) ? ascii_range_ : ch, "", ascii_range_ + 1));
|
||||
|
||||
label_.set_markup(label_text_);
|
||||
label_.show();
|
||||
ALabel::update();
|
||||
silence_ = false;
|
||||
}
|
||||
auto waybar::modules::cava::Cava::onSilence() -> void {
|
||||
if (!silence_) {
|
||||
label_.get_style_context()->remove_class("updated");
|
||||
|
||||
if (hide_on_silence_)
|
||||
label_.hide();
|
||||
else if (config_["format_silent"].isString())
|
||||
label_.set_markup(format_silent_);
|
||||
silence_ = true;
|
||||
label_.get_style_context()->add_class("silent");
|
||||
}
|
||||
}
|
223
src/modules/cava/cava_backend.cpp
Normal file
223
src/modules/cava/cava_backend.cpp
Normal file
@ -0,0 +1,223 @@
|
||||
#include "modules/cava/cava_backend.hpp"
|
||||
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
std::shared_ptr<waybar::modules::cava::CavaBackend> waybar::modules::cava::CavaBackend::inst(
|
||||
const Json::Value& config) {
|
||||
static auto* backend = new CavaBackend(config);
|
||||
static std::shared_ptr<CavaBackend> backend_ptr{backend};
|
||||
return backend_ptr;
|
||||
}
|
||||
|
||||
waybar::modules::cava::CavaBackend::CavaBackend(const Json::Value& config) {
|
||||
// Load waybar module config
|
||||
char cfgPath[PATH_MAX];
|
||||
cfgPath[0] = '\0';
|
||||
|
||||
if (config["cava_config"].isString()) strcpy(cfgPath, config["cava_config"].asString().data());
|
||||
// Load cava config
|
||||
error_.length = 0;
|
||||
|
||||
if (!load_config(cfgPath, &prm_, false, &error_)) {
|
||||
spdlog::error("cava backend. Error loading config. {0}", error_.message);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Override cava parameters by the user config
|
||||
prm_.inAtty = 0;
|
||||
prm_.output = ::cava::output_method::OUTPUT_RAW;
|
||||
strcpy(prm_.data_format, "ascii");
|
||||
strcpy(prm_.raw_target, "/dev/stdout");
|
||||
prm_.ascii_range = config["format-icons"].size() - 1;
|
||||
|
||||
prm_.bar_width = 2;
|
||||
prm_.bar_spacing = 0;
|
||||
prm_.bar_height = 32;
|
||||
prm_.bar_width = 1;
|
||||
prm_.orientation = ::cava::ORIENT_TOP;
|
||||
prm_.xaxis = ::cava::xaxis_scale::NONE;
|
||||
prm_.mono_opt = ::cava::AVERAGE;
|
||||
prm_.autobars = 0;
|
||||
prm_.gravity = 0;
|
||||
prm_.integral = 1;
|
||||
|
||||
if (config["framerate"].isInt()) prm_.framerate = config["framerate"].asInt();
|
||||
// Calculate delay for Update() thread
|
||||
frame_time_milsec_ = std::chrono::milliseconds((int)(1e3 / prm_.framerate));
|
||||
if (config["autosens"].isInt()) prm_.autosens = config["autosens"].asInt();
|
||||
if (config["sensitivity"].isInt()) prm_.sens = config["sensitivity"].asInt();
|
||||
if (config["bars"].isInt()) prm_.fixedbars = config["bars"].asInt();
|
||||
if (config["lower_cutoff_freq"].isNumeric())
|
||||
prm_.lower_cut_off = config["lower_cutoff_freq"].asLargestInt();
|
||||
if (config["higher_cutoff_freq"].isNumeric())
|
||||
prm_.upper_cut_off = config["higher_cutoff_freq"].asLargestInt();
|
||||
if (config["sleep_timer"].isInt()) prm_.sleep_timer = config["sleep_timer"].asInt();
|
||||
if (config["method"].isString())
|
||||
prm_.input = ::cava::input_method_by_name(config["method"].asString().c_str());
|
||||
if (config["source"].isString()) prm_.audio_source = config["source"].asString().data();
|
||||
if (config["sample_rate"].isNumeric()) prm_.samplerate = config["sample_rate"].asLargestInt();
|
||||
if (config["sample_bits"].isInt()) prm_.samplebits = config["sample_bits"].asInt();
|
||||
if (config["stereo"].isBool()) prm_.stereo = config["stereo"].asBool();
|
||||
if (config["reverse"].isBool()) prm_.reverse = config["reverse"].asBool();
|
||||
if (config["bar_delimiter"].isInt()) prm_.bar_delim = config["bar_delimiter"].asInt();
|
||||
if (config["monstercat"].isBool()) prm_.monstercat = config["monstercat"].asBool();
|
||||
if (config["waves"].isBool()) prm_.waves = config["waves"].asBool();
|
||||
if (config["noise_reduction"].isDouble())
|
||||
prm_.noise_reduction = config["noise_reduction"].asDouble();
|
||||
if (config["input_delay"].isInt())
|
||||
fetch_input_delay_ = std::chrono::seconds(config["input_delay"].asInt());
|
||||
|
||||
// Make cava parameters configuration
|
||||
plan_ = new ::cava::cava_plan{};
|
||||
|
||||
audio_raw_.height = prm_.ascii_range;
|
||||
audio_data_.format = -1;
|
||||
audio_data_.source = new char[1 + strlen(prm_.audio_source)];
|
||||
audio_data_.source[0] = '\0';
|
||||
strcpy(audio_data_.source, prm_.audio_source);
|
||||
|
||||
audio_data_.rate = 0;
|
||||
audio_data_.samples_counter = 0;
|
||||
audio_data_.channels = 2;
|
||||
audio_data_.IEEE_FLOAT = 0;
|
||||
|
||||
audio_data_.input_buffer_size = BUFFER_SIZE * audio_data_.channels;
|
||||
audio_data_.cava_buffer_size = audio_data_.input_buffer_size * 8;
|
||||
|
||||
audio_data_.cava_in = new double[audio_data_.cava_buffer_size]{0.0};
|
||||
|
||||
audio_data_.terminate = 0;
|
||||
audio_data_.suspendFlag = false;
|
||||
input_source_ = get_input(&audio_data_, &prm_);
|
||||
|
||||
if (!input_source_) {
|
||||
spdlog::error("cava backend API didn't provide input audio source method");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Init cava plan, audio_raw structure
|
||||
audio_raw_init(&audio_data_, &audio_raw_, &prm_, plan_);
|
||||
if (!plan_) spdlog::error("cava backend plan is not provided");
|
||||
audio_raw_.previous_frame[0] = -1; // For first Update() call need to rePaint text message
|
||||
// Read audio source trough cava API. Cava orginizes this process via infinity loop
|
||||
read_thread_ = [this] {
|
||||
try {
|
||||
input_source_(&audio_data_);
|
||||
} catch (const std::runtime_error& e) {
|
||||
spdlog::warn("Cava backend. Read source error: {0}", e.what());
|
||||
}
|
||||
read_thread_.sleep_for(fetch_input_delay_);
|
||||
};
|
||||
|
||||
thread_ = [this] {
|
||||
doUpdate();
|
||||
thread_.sleep_for(frame_time_milsec_);
|
||||
};
|
||||
}
|
||||
|
||||
waybar::modules::cava::CavaBackend::~CavaBackend() {
|
||||
thread_.stop();
|
||||
read_thread_.stop();
|
||||
delete plan_;
|
||||
plan_ = nullptr;
|
||||
}
|
||||
|
||||
static void upThreadDelay(std::chrono::milliseconds& delay, std::chrono::seconds& delta) {
|
||||
if (delta == std::chrono::seconds{0}) {
|
||||
delta += std::chrono::seconds{1};
|
||||
delay += delta;
|
||||
}
|
||||
}
|
||||
|
||||
static void downThreadDelay(std::chrono::milliseconds& delay, std::chrono::seconds& delta) {
|
||||
if (delta > std::chrono::seconds{0}) {
|
||||
delay -= delta;
|
||||
delta -= std::chrono::seconds{1};
|
||||
}
|
||||
}
|
||||
|
||||
bool waybar::modules::cava::CavaBackend::isSilence() {
|
||||
for (int i{0}; i < audio_data_.input_buffer_size; ++i) {
|
||||
if (audio_data_.cava_in[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int waybar::modules::cava::CavaBackend::getAsciiRange() { return prm_.ascii_range; }
|
||||
|
||||
// Process: execute cava
|
||||
void waybar::modules::cava::CavaBackend::invoke() {
|
||||
pthread_mutex_lock(&audio_data_.lock);
|
||||
::cava::cava_execute(audio_data_.cava_in, audio_data_.samples_counter, audio_raw_.cava_out,
|
||||
plan_);
|
||||
if (audio_data_.samples_counter > 0) audio_data_.samples_counter = 0;
|
||||
pthread_mutex_unlock(&audio_data_.lock);
|
||||
}
|
||||
|
||||
// Do transformation under raw data
|
||||
void waybar::modules::cava::CavaBackend::execute() {
|
||||
invoke();
|
||||
audio_raw_fetch(&audio_raw_, &prm_, &re_paint_, plan_);
|
||||
|
||||
if (re_paint_ == 1) {
|
||||
output_.clear();
|
||||
for (int i{0}; i < audio_raw_.number_of_bars; ++i) {
|
||||
audio_raw_.previous_frame[i] = audio_raw_.bars[i];
|
||||
output_.push_back(audio_raw_.bars[i]);
|
||||
if (prm_.bar_delim != 0) output_.push_back(prm_.bar_delim);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void waybar::modules::cava::CavaBackend::doPauseResume() {
|
||||
pthread_mutex_lock(&audio_data_.lock);
|
||||
if (audio_data_.suspendFlag) {
|
||||
audio_data_.suspendFlag = false;
|
||||
pthread_cond_broadcast(&audio_data_.resumeCond);
|
||||
downThreadDelay(frame_time_milsec_, suspend_silence_delay_);
|
||||
} else {
|
||||
audio_data_.suspendFlag = true;
|
||||
upThreadDelay(frame_time_milsec_, suspend_silence_delay_);
|
||||
}
|
||||
pthread_mutex_unlock(&audio_data_.lock);
|
||||
}
|
||||
|
||||
waybar::modules::cava::CavaBackend::type_signal_update
|
||||
waybar::modules::cava::CavaBackend::signal_update() {
|
||||
return m_signal_update_;
|
||||
}
|
||||
|
||||
waybar::modules::cava::CavaBackend::type_signal_silence
|
||||
waybar::modules::cava::CavaBackend::signal_silence() {
|
||||
return m_signal_silence_;
|
||||
}
|
||||
|
||||
void waybar::modules::cava::CavaBackend::Update() { doUpdate(true); }
|
||||
|
||||
void waybar::modules::cava::CavaBackend::doUpdate(bool force) {
|
||||
if (audio_data_.suspendFlag && !force) return;
|
||||
|
||||
silence_ = isSilence();
|
||||
if (!silence_) sleep_counter_ = 0;
|
||||
|
||||
if (silence_ && prm_.sleep_timer != 0) {
|
||||
if (sleep_counter_ <=
|
||||
(int)(std::chrono::milliseconds(prm_.sleep_timer * 1s) / frame_time_milsec_)) {
|
||||
++sleep_counter_;
|
||||
silence_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!silence_ || prm_.sleep_timer == 0) {
|
||||
downThreadDelay(frame_time_milsec_, suspend_silence_delay_);
|
||||
execute();
|
||||
if (re_paint_ == 1 || force) m_signal_update_.emit(output_);
|
||||
} else {
|
||||
upThreadDelay(frame_time_milsec_, suspend_silence_delay_);
|
||||
if (silence_ != silence_prev_ || force) m_signal_silence_.emit();
|
||||
}
|
||||
silence_prev_ = silence_;
|
||||
}
|
@ -30,6 +30,9 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
|
||||
cldMonShift_{year(1900) / January},
|
||||
tzInTooltip_{m_tlpFmt_.find("{" + kTZPlaceholder + "}") != std::string::npos},
|
||||
tzCurrIdx_{0},
|
||||
tzTooltipFormat_{config_["timezone-tooltip-format"].isString()
|
||||
? config_["timezone-tooltip-format"].asString()
|
||||
: ""},
|
||||
ordInTooltip_{m_tlpFmt_.find("{" + kOrdPlaceholder + "}") != std::string::npos} {
|
||||
m_tlpText_ = m_tlpFmt_;
|
||||
|
||||
@ -63,8 +66,8 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
|
||||
if (cldInTooltip_) {
|
||||
if (config_[kCldPlaceholder]["mode"].isString()) {
|
||||
const std::string cfgMode{config_[kCldPlaceholder]["mode"].asString()};
|
||||
const std::map<std::string_view, const CldMode&> monthModes{{"month", CldMode::MONTH},
|
||||
{"year", CldMode::YEAR}};
|
||||
const std::map<std::string, CldMode> monthModes{{"month", CldMode::MONTH},
|
||||
{"year", CldMode::YEAR}};
|
||||
if (monthModes.find(cfgMode) != monthModes.end())
|
||||
cldMode_ = monthModes.at(cfgMode);
|
||||
else
|
||||
@ -73,6 +76,11 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
|
||||
"using instead",
|
||||
cfgMode);
|
||||
}
|
||||
|
||||
if (config_[kCldPlaceholder]["iso8601"].isBool()) {
|
||||
iso8601Calendar_ = config_[kCldPlaceholder]["iso8601"].asBool();
|
||||
}
|
||||
|
||||
if (config_[kCldPlaceholder]["weeks-pos"].isString()) {
|
||||
if (config_[kCldPlaceholder]["weeks-pos"].asString() == "left") cldWPos_ = WS::LEFT;
|
||||
if (config_[kCldPlaceholder]["weeks-pos"].asString() == "right") cldWPos_ = WS::RIGHT;
|
||||
@ -92,23 +100,25 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
|
||||
fmtMap_.insert({2, "{}"});
|
||||
if (config_[kCldPlaceholder]["format"]["today"].isString()) {
|
||||
fmtMap_.insert({3, config_[kCldPlaceholder]["format"]["today"].asString()});
|
||||
cldBaseDay_ =
|
||||
year_month_day{
|
||||
floor<days>(zoned_time{local_zone(), system_clock::now()}.get_local_time())}
|
||||
.day();
|
||||
auto local_time = zoned_time{local_zone(), system_clock::now()}.get_local_time();
|
||||
cldBaseDay_ = year_month_day{floor<days>(local_time)}.day();
|
||||
} else
|
||||
fmtMap_.insert({3, "{}"});
|
||||
if (config_[kCldPlaceholder]["format"]["weeks"].isString() && cldWPos_ != WS::HIDDEN) {
|
||||
const auto defaultFmt =
|
||||
iso8601Calendar_ ? "{:%V}" : ((first_day_of_week() == Monday) ? "{:%W}" : "{:%U}");
|
||||
fmtMap_.insert({4, std::regex_replace(config_[kCldPlaceholder]["format"]["weeks"].asString(),
|
||||
std::regex("\\{\\}"),
|
||||
(first_day_of_week() == Monday) ? "{:%W}" : "{:%U}")});
|
||||
std::regex("\\{\\}"), defaultFmt)});
|
||||
Glib::ustring tmp{std::regex_replace(fmtMap_[4], std::regex("</?[^>]+>|\\{.*\\}"), "")};
|
||||
cldWnLen_ += tmp.size();
|
||||
} else {
|
||||
if (cldWPos_ != WS::HIDDEN)
|
||||
fmtMap_.insert({4, (first_day_of_week() == Monday) ? "{:%W}" : "{:%U}"});
|
||||
else
|
||||
if (cldWPos_ != WS::HIDDEN) {
|
||||
const auto defaultFmt =
|
||||
iso8601Calendar_ ? "{:%V}" : ((first_day_of_week() == Monday) ? "{:%W}" : "{:%U}");
|
||||
fmtMap_.insert({4, defaultFmt});
|
||||
} else {
|
||||
cldWnLen_ = 0;
|
||||
}
|
||||
}
|
||||
if (config_[kCldPlaceholder]["mode-mon-col"].isInt()) {
|
||||
cldMonCols_ = config_[kCldPlaceholder]["mode-mon-col"].asInt();
|
||||
@ -188,11 +198,26 @@ auto waybar::modules::Clock::getTZtext(sys_seconds now) -> std::string {
|
||||
if (tzList_.size() == 1) return "";
|
||||
|
||||
std::stringstream os;
|
||||
bool first = true;
|
||||
for (size_t tz_idx{0}; tz_idx < tzList_.size(); ++tz_idx) {
|
||||
if (static_cast<int>(tz_idx) == tzCurrIdx_) continue;
|
||||
const auto* tz = tzList_[tz_idx] != nullptr ? tzList_[tz_idx] : local_zone();
|
||||
// Skip local timezone (nullptr) - never show it in tooltip
|
||||
if (tzList_[tz_idx] == nullptr) continue;
|
||||
|
||||
// Skip current timezone unless timezone-tooltip-format is specified
|
||||
if (static_cast<int>(tz_idx) == tzCurrIdx_ && tzTooltipFormat_.empty()) continue;
|
||||
|
||||
const auto* tz = tzList_[tz_idx];
|
||||
auto zt{zoned_time{tz, now}};
|
||||
os << fmt_lib::vformat(m_locale_, format_, fmt_lib::make_format_args(zt)) << '\n';
|
||||
|
||||
// Add newline before each entry except the first
|
||||
if (!first) {
|
||||
os << '\n';
|
||||
}
|
||||
first = false;
|
||||
|
||||
// Use timezone-tooltip-format if specified, otherwise use format_
|
||||
const std::string& fmt = tzTooltipFormat_.empty() ? format_ : tzTooltipFormat_;
|
||||
os << fmt_lib::vformat(m_locale_, fmt, fmt_lib::make_format_args(zt));
|
||||
}
|
||||
|
||||
return os.str();
|
||||
@ -204,9 +229,11 @@ const unsigned cldRowsInMonth(const year_month& ym, const weekday& firstdow) {
|
||||
|
||||
auto cldGetWeekForLine(const year_month& ym, const weekday& firstdow, const unsigned line)
|
||||
-> const year_month_weekday {
|
||||
unsigned index{line - 2};
|
||||
if (weekday{ym / 1} == firstdow) ++index;
|
||||
return ym / firstdow[index];
|
||||
const unsigned idx = line - 2;
|
||||
const std::chrono::weekday_indexed indexed_first_day_of_week =
|
||||
weekday{ym / 1} == firstdow ? firstdow[idx + 1] : firstdow[idx];
|
||||
|
||||
return ym / indexed_first_day_of_week;
|
||||
}
|
||||
|
||||
auto getCalendarLine(const year_month_day& currDate, const year_month ym, const unsigned line,
|
||||
@ -265,7 +292,7 @@ auto getCalendarLine(const year_month_day& currDate, const year_month ym, const
|
||||
}
|
||||
// Print non-first week
|
||||
default: {
|
||||
auto ymdTmp{cldGetWeekForLine(ym, firstdow, line)};
|
||||
const auto ymdTmp{cldGetWeekForLine(ym, firstdow, line)};
|
||||
if (ymdTmp.ok()) {
|
||||
auto d{year_month_day{ymdTmp}.day()};
|
||||
const auto dlast{(ym / last).day()};
|
||||
@ -356,8 +383,9 @@ auto waybar::modules::Clock::get_calendar(const year_month_day& today, const yea
|
||||
: static_cast<const zoned_seconds&&>(zoned_seconds{
|
||||
tz, local_days{cldGetWeekForLine(ymTmp, firstdow, line)}})))
|
||||
<< ' ';
|
||||
} else
|
||||
} else {
|
||||
os << pads;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -481,6 +509,9 @@ using deleting_unique_ptr = std::unique_ptr<T, deleter_from_fn<fn>>;
|
||||
|
||||
// Computations done similarly to Linux cal utility.
|
||||
auto waybar::modules::Clock::first_day_of_week() -> weekday {
|
||||
if (iso8601Calendar_) {
|
||||
return Monday;
|
||||
}
|
||||
#ifdef HAVE_LANGINFO_1STDAY
|
||||
deleting_unique_ptr<std::remove_pointer<locale_t>::type, freelocale> posix_locale{
|
||||
newlocale(LC_ALL, m_locale_.name().c_str(), nullptr)};
|
||||
|
@ -89,9 +89,11 @@ void waybar::modules::Custom::continuousWorker() {
|
||||
dp.emit();
|
||||
spdlog::error("{} stopped unexpectedly, is it endless?", name_);
|
||||
}
|
||||
if (config_["restart-interval"].isUInt()) {
|
||||
if (config_["restart-interval"].isNumeric()) {
|
||||
pid_ = -1;
|
||||
thread_.sleep_for(std::chrono::seconds(config_["restart-interval"].asUInt()));
|
||||
thread_.sleep_for(std::chrono::milliseconds(
|
||||
std::max(1L, // Minimum 1ms due to millisecond precision
|
||||
static_cast<long>(config_["restart-interval"].asDouble() * 1000))));
|
||||
fp_ = util::command::open(cmd, pid_, output_name_);
|
||||
if (!fp_) {
|
||||
throw std::runtime_error("Unable to open " + cmd);
|
||||
|
@ -349,11 +349,11 @@ Workspace::Workspace(const Json::Value &config, WorkspaceManager &manager,
|
||||
}
|
||||
const bool config_on_click_middle = config["on-click-middle"].isString();
|
||||
if (config_on_click_middle) {
|
||||
on_click_middle_action_ = config["on-click"].asString();
|
||||
on_click_middle_action_ = config["on-click-middle"].asString();
|
||||
}
|
||||
const bool config_on_click_right = config["on-click-right"].isString();
|
||||
if (config_on_click_right) {
|
||||
on_click_right_action_ = config["on-click"].asString();
|
||||
on_click_right_action_ = config["on-click-right"].asString();
|
||||
}
|
||||
|
||||
// setup UI
|
||||
@ -377,16 +377,19 @@ Workspace::~Workspace() {
|
||||
}
|
||||
|
||||
void Workspace::update() {
|
||||
if (!needs_updating_) {
|
||||
return;
|
||||
}
|
||||
const auto style_context = button_.get_style_context();
|
||||
|
||||
// update style and visibility
|
||||
|
||||
const auto style_context = button_.get_style_context();
|
||||
style_context->remove_class("active");
|
||||
style_context->remove_class("urgent");
|
||||
style_context->remove_class("hidden");
|
||||
if (!has_state(EXT_WORKSPACE_HANDLE_V1_STATE_ACTIVE)) {
|
||||
style_context->remove_class("active");
|
||||
}
|
||||
if (!has_state(EXT_WORKSPACE_HANDLE_V1_STATE_URGENT)) {
|
||||
style_context->remove_class("urgent");
|
||||
}
|
||||
if (!has_state(EXT_WORKSPACE_HANDLE_V1_STATE_HIDDEN)) {
|
||||
style_context->remove_class("hidden");
|
||||
}
|
||||
|
||||
if (has_state(EXT_WORKSPACE_HANDLE_V1_STATE_ACTIVE)) {
|
||||
button_.set_visible(true);
|
||||
@ -408,34 +411,26 @@ void Workspace::update() {
|
||||
label_.set_markup(fmt::format(fmt::runtime(format_), fmt::arg("name", name_),
|
||||
fmt::arg("id", workspace_id_),
|
||||
fmt::arg("icon", with_icon_ ? icon() : "")));
|
||||
|
||||
needs_updating_ = false;
|
||||
}
|
||||
|
||||
void Workspace::handle_id(const std::string &id) {
|
||||
spdlog::debug("[ext/workspaces]: ID for workspace {}: {}", id_, id);
|
||||
workspace_id_ = id;
|
||||
needs_updating_ = true;
|
||||
workspace_manager_.set_needs_sorting();
|
||||
}
|
||||
|
||||
void Workspace::handle_name(const std::string &name) {
|
||||
spdlog::debug("[ext/workspaces]: Name for workspace {}: {}", id_, name);
|
||||
name_ = name;
|
||||
needs_updating_ = true;
|
||||
workspace_manager_.set_needs_sorting();
|
||||
}
|
||||
|
||||
void Workspace::handle_coordinates(const std::vector<uint32_t> &coordinates) {
|
||||
coordinates_ = coordinates;
|
||||
needs_updating_ = true;
|
||||
workspace_manager_.set_needs_sorting();
|
||||
}
|
||||
|
||||
void Workspace::handle_state(uint32_t state) {
|
||||
state_ = state;
|
||||
needs_updating_ = true;
|
||||
}
|
||||
void Workspace::handle_state(uint32_t state) { state_ = state; }
|
||||
|
||||
void Workspace::handle_capabilities(uint32_t capabilities) {
|
||||
spdlog::debug("[ext/workspaces]: Capabilities for workspace {}:", id_);
|
||||
@ -451,7 +446,6 @@ void Workspace::handle_capabilities(uint32_t capabilities) {
|
||||
if ((capabilities & EXT_WORKSPACE_HANDLE_V1_WORKSPACE_CAPABILITIES_ASSIGN) == capabilities) {
|
||||
spdlog::debug("[ext/workspaces]: - assign");
|
||||
}
|
||||
needs_updating_ = true;
|
||||
}
|
||||
|
||||
void Workspace::handle_removed() {
|
||||
@ -475,6 +469,8 @@ bool Workspace::handle_clicked(const GdkEventButton *button) const {
|
||||
|
||||
if (action == "activate") {
|
||||
ext_workspace_handle_v1_activate(ext_handle_);
|
||||
} else if (action == "deactivate") {
|
||||
ext_workspace_handle_v1_deactivate(ext_handle_);
|
||||
} else if (action == "close") {
|
||||
ext_workspace_handle_v1_remove(ext_handle_);
|
||||
} else {
|
||||
|
@ -53,7 +53,6 @@ Gamemode::Gamemode(const std::string& id, const Json::Value& config)
|
||||
if (config_["icon-spacing"].isUInt()) {
|
||||
iconSpacing = config_["icon-spacing"].asUInt();
|
||||
}
|
||||
box_.set_spacing(iconSpacing);
|
||||
|
||||
// Whether to use icon or not
|
||||
if (config_["use-icon"].isBool()) {
|
||||
@ -64,7 +63,6 @@ Gamemode::Gamemode(const std::string& id, const Json::Value& config)
|
||||
if (config_["icon-size"].isUInt()) {
|
||||
iconSize = config_["icon-size"].asUInt();
|
||||
}
|
||||
icon_.set_pixel_size(iconSize);
|
||||
|
||||
// Format
|
||||
if (config_["format"].isString()) {
|
||||
@ -228,6 +226,11 @@ auto Gamemode::update() -> void {
|
||||
iconName = DEFAULT_ICON_NAME;
|
||||
}
|
||||
icon_.set_from_icon_name(iconName, Gtk::ICON_SIZE_INVALID);
|
||||
box_.set_spacing(iconSpacing);
|
||||
icon_.set_pixel_size(iconSize);
|
||||
} else {
|
||||
box_.set_spacing(0);
|
||||
icon_.set_pixel_size(0);
|
||||
}
|
||||
|
||||
// Call parent update
|
||||
|
@ -46,9 +46,14 @@ std::filesystem::path IPC::getSocketFolder(const char* instanceSig) {
|
||||
IPC::IPC() {
|
||||
// will start IPC and relay events to parseIPC
|
||||
ipcThread_ = std::thread([this]() { socketListener(); });
|
||||
socketOwnerPid_ = getpid();
|
||||
}
|
||||
|
||||
IPC::~IPC() {
|
||||
// Do no stop Hyprland IPC if a child process (with successful fork() but
|
||||
// failed exec()) exits.
|
||||
if (getpid() != socketOwnerPid_) return;
|
||||
|
||||
running_ = false;
|
||||
spdlog::info("Hyprland IPC stopping...");
|
||||
if (socketfd_ != -1) {
|
||||
|
@ -104,8 +104,25 @@ void Workspace::initializeWindowMap(const Json::Value &clients_data) {
|
||||
}
|
||||
|
||||
void Workspace::setActiveWindow(WindowAddress const &addr) {
|
||||
for (auto &window : m_windowMap) {
|
||||
window.setActive(window.address == addr);
|
||||
std::optional<long> activeIdx;
|
||||
for (size_t i = 0; i < m_windowMap.size(); ++i) {
|
||||
auto &window = m_windowMap[i];
|
||||
bool isActive = (window.address == addr);
|
||||
window.setActive(isActive);
|
||||
if (isActive) {
|
||||
activeIdx = i;
|
||||
}
|
||||
}
|
||||
|
||||
auto activeWindowPos = m_workspaceManager.activeWindowPosition();
|
||||
if (activeIdx.has_value() && activeWindowPos != Workspaces::ActiveWindowPosition::NONE) {
|
||||
auto window = std::move(m_windowMap[*activeIdx]);
|
||||
m_windowMap.erase(m_windowMap.begin() + *activeIdx);
|
||||
if (activeWindowPos == Workspaces::ActiveWindowPosition::FIRST) {
|
||||
m_windowMap.insert(m_windowMap.begin(), std::move(window));
|
||||
} else if (activeWindowPos == Workspaces::ActiveWindowPosition::LAST) {
|
||||
m_windowMap.emplace_back(std::move(window));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -251,6 +268,17 @@ void Workspace::update(const std::string &workspace_icon) {
|
||||
}
|
||||
}
|
||||
|
||||
bool Workspace::isEmpty() const {
|
||||
auto ignore_list = m_workspaceManager.getIgnoredWindows();
|
||||
if (ignore_list.empty()) {
|
||||
return m_windows == 0;
|
||||
}
|
||||
// If there are windows but they are all ignored, consider the workspace empty
|
||||
return std::all_of(
|
||||
m_windowMap.begin(), m_windowMap.end(),
|
||||
[this, &ignore_list](const auto &window_repr) { return shouldSkipWindow(window_repr); });
|
||||
}
|
||||
|
||||
void Workspace::updateTaskbar(const std::string &workspace_icon) {
|
||||
for (auto child : m_content.get_children()) {
|
||||
if (child != &m_labelBefore) {
|
||||
@ -259,9 +287,9 @@ void Workspace::updateTaskbar(const std::string &workspace_icon) {
|
||||
}
|
||||
|
||||
bool isFirst = true;
|
||||
for (const auto &window_repr : m_windowMap) {
|
||||
auto processWindow = [&](const WindowRepr &window_repr) {
|
||||
if (shouldSkipWindow(window_repr)) {
|
||||
continue;
|
||||
return; // skip
|
||||
}
|
||||
if (isFirst) {
|
||||
isFirst = false;
|
||||
@ -270,6 +298,7 @@ void Workspace::updateTaskbar(const std::string &workspace_icon) {
|
||||
m_content.pack_start(*windowSeparator, false, false);
|
||||
windowSeparator->show();
|
||||
}
|
||||
|
||||
auto window_box = Gtk::make_managed<Gtk::Box>(Gtk::ORIENTATION_HORIZONTAL);
|
||||
window_box->set_tooltip_text(window_repr.window_title);
|
||||
window_box->get_style_context()->add_class("taskbar-window");
|
||||
@ -307,6 +336,16 @@ void Workspace::updateTaskbar(const std::string &workspace_icon) {
|
||||
|
||||
m_content.pack_start(*event_box, true, false);
|
||||
event_box->show_all();
|
||||
};
|
||||
|
||||
if (m_workspaceManager.taskbarReverseDirection()) {
|
||||
for (auto it = m_windowMap.rbegin(); it != m_windowMap.rend(); ++it) {
|
||||
processWindow(*it);
|
||||
}
|
||||
} else {
|
||||
for (const auto &window_repr : m_windowMap) {
|
||||
processWindow(window_repr);
|
||||
}
|
||||
}
|
||||
|
||||
auto formatAfter = m_workspaceManager.formatAfter();
|
||||
|
@ -727,6 +727,7 @@ auto Workspaces::populateWorkspaceTaskbarConfig(const Json::Value &config) -> vo
|
||||
|
||||
populateBoolConfig(workspaceTaskbar, "enable", m_enableTaskbar);
|
||||
populateBoolConfig(workspaceTaskbar, "update-active-window", m_updateActiveWindow);
|
||||
populateBoolConfig(workspaceTaskbar, "reverse-direction", m_taskbarReverseDirection);
|
||||
|
||||
if (workspaceTaskbar["format"].isString()) {
|
||||
/* The user defined a format string, use it */
|
||||
@ -774,6 +775,18 @@ auto Workspaces::populateWorkspaceTaskbarConfig(const Json::Value &config) -> vo
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (workspaceTaskbar["active-window-position"].isString()) {
|
||||
auto posStr = workspaceTaskbar["active-window-position"].asString();
|
||||
try {
|
||||
m_activeWindowPosition =
|
||||
m_activeWindowEnumParser.parseStringToEnum(posStr, m_activeWindowPositionMap);
|
||||
} catch (const std::invalid_argument &e) {
|
||||
spdlog::warn(
|
||||
"Invalid string representation for active-window-position. Falling back to 'none'.");
|
||||
m_activeWindowPosition = ActiveWindowPosition::NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Workspaces::registerOrphanWindow(WindowCreationPayload create_window_payload) {
|
||||
@ -1128,9 +1141,9 @@ std::optional<int> Workspaces::parseWorkspaceId(std::string const &workspaceIdSt
|
||||
try {
|
||||
return workspaceIdStr == "special" ? -99 : std::stoi(workspaceIdStr);
|
||||
} catch (std::exception const &e) {
|
||||
spdlog::error("Failed to parse workspace ID: {}", e.what());
|
||||
spdlog::debug("Workspace \"{}\" is not bound to an id: {}", workspaceIdStr, e.what());
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace waybar::modules::hyprland
|
||||
} // namespace waybar::modules::hyprland
|
||||
|
@ -14,14 +14,20 @@ waybar::modules::Image::Image(const std::string& id, const Json::Value& config)
|
||||
|
||||
size_ = config["size"].asInt();
|
||||
|
||||
interval_ = config_["interval"].asInt();
|
||||
interval_ = config_["interval"] == "once"
|
||||
? std::chrono::milliseconds::max()
|
||||
: std::chrono::milliseconds(std::max(
|
||||
1L, // Minimum 1ms due to millisecond precision
|
||||
static_cast<long>(
|
||||
(config_["interval"].isNumeric() ? config_["interval"].asDouble() : 0) *
|
||||
1000)));
|
||||
|
||||
if (size_ == 0) {
|
||||
size_ = 16;
|
||||
}
|
||||
|
||||
if (interval_ == 0) {
|
||||
interval_ = INT_MAX;
|
||||
if (interval_.count() == 0) {
|
||||
interval_ = std::chrono::milliseconds::max();
|
||||
}
|
||||
|
||||
delayWorker();
|
||||
@ -30,8 +36,7 @@ waybar::modules::Image::Image(const std::string& id, const Json::Value& config)
|
||||
void waybar::modules::Image::delayWorker() {
|
||||
thread_ = [this] {
|
||||
dp.emit();
|
||||
auto interval = std::chrono::seconds(interval_);
|
||||
thread_.sleep_for(interval);
|
||||
thread_.sleep_for(interval_);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -333,18 +333,23 @@ auto waybar::modules::Network::update() -> void {
|
||||
fmt::arg("ipaddr", final_ipaddr_), fmt::arg("gwaddr", gwaddr_), fmt::arg("cidr", cidr_),
|
||||
fmt::arg("cidr6", cidr6_), fmt::arg("frequency", fmt::format("{:.1f}", frequency_)),
|
||||
fmt::arg("icon", getIcon(signal_strength_, state_)),
|
||||
fmt::arg("bandwidthDownBits", pow_format(bandwidth_down * 8ull / interval_.count(), "b/s")),
|
||||
fmt::arg("bandwidthUpBits", pow_format(bandwidth_up * 8ull / interval_.count(), "b/s")),
|
||||
fmt::arg("bandwidthTotalBits",
|
||||
pow_format((bandwidth_up + bandwidth_down) * 8ull / interval_.count(), "b/s")),
|
||||
fmt::arg("bandwidthDownOctets", pow_format(bandwidth_down / interval_.count(), "o/s")),
|
||||
fmt::arg("bandwidthUpOctets", pow_format(bandwidth_up / interval_.count(), "o/s")),
|
||||
fmt::arg("bandwidthDownBits",
|
||||
pow_format(bandwidth_down * 8ull / (interval_.count() / 1000.0), "b/s")),
|
||||
fmt::arg("bandwidthUpBits",
|
||||
pow_format(bandwidth_up * 8ull / (interval_.count() / 1000.0), "b/s")),
|
||||
fmt::arg(
|
||||
"bandwidthTotalBits",
|
||||
pow_format((bandwidth_up + bandwidth_down) * 8ull / (interval_.count() / 1000.0), "b/s")),
|
||||
fmt::arg("bandwidthDownOctets",
|
||||
pow_format(bandwidth_down / (interval_.count() / 1000.0), "o/s")),
|
||||
fmt::arg("bandwidthUpOctets", pow_format(bandwidth_up / (interval_.count() / 1000.0), "o/s")),
|
||||
fmt::arg("bandwidthTotalOctets",
|
||||
pow_format((bandwidth_up + bandwidth_down) / interval_.count(), "o/s")),
|
||||
fmt::arg("bandwidthDownBytes", pow_format(bandwidth_down / interval_.count(), "B/s")),
|
||||
fmt::arg("bandwidthUpBytes", pow_format(bandwidth_up / interval_.count(), "B/s")),
|
||||
pow_format((bandwidth_up + bandwidth_down) / (interval_.count() / 1000.0), "o/s")),
|
||||
fmt::arg("bandwidthDownBytes",
|
||||
pow_format(bandwidth_down / (interval_.count() / 1000.0), "B/s")),
|
||||
fmt::arg("bandwidthUpBytes", pow_format(bandwidth_up / (interval_.count() / 1000.0), "B/s")),
|
||||
fmt::arg("bandwidthTotalBytes",
|
||||
pow_format((bandwidth_up + bandwidth_down) / interval_.count(), "B/s")));
|
||||
pow_format((bandwidth_up + bandwidth_down) / (interval_.count() / 1000.0), "B/s")));
|
||||
if (text.compare(label_.get_label()) != 0) {
|
||||
label_.set_markup(text);
|
||||
if (text.empty()) {
|
||||
|
@ -58,6 +58,16 @@ void Language::doUpdate() {
|
||||
spdlog::debug("niri language update with short description {}", layout.short_description);
|
||||
spdlog::debug("niri language update with variant {}", layout.variant);
|
||||
|
||||
if (!last_short_name_.empty()) {
|
||||
label_.get_style_context()->remove_class(last_short_name_);
|
||||
}
|
||||
if (!layout.short_name.empty()) {
|
||||
label_.get_style_context()->add_class(layout.short_name);
|
||||
last_short_name_ = layout.short_name;
|
||||
} else {
|
||||
last_short_name_.clear();
|
||||
}
|
||||
|
||||
std::string layoutName = std::string{};
|
||||
if (config_.isMember("format-" + layout.short_description + "-" + layout.variant)) {
|
||||
const auto propName = "format-" + layout.short_description + "-" + layout.variant;
|
||||
|
@ -150,11 +150,7 @@ Tags::Tags(const std::string &id, const waybar::Bar &bar, const Json::Value &con
|
||||
button.show();
|
||||
}
|
||||
|
||||
struct wl_output *output = gdk_wayland_monitor_get_wl_output(bar_.output->monitor->gobj());
|
||||
output_status_ = zriver_status_manager_v1_get_river_output_status(status_manager_, output);
|
||||
zriver_output_status_v1_add_listener(output_status_, &output_status_listener_impl, this);
|
||||
|
||||
zriver_status_manager_v1_destroy(status_manager_);
|
||||
box_.signal_show().connect(sigc::mem_fun(*this, &Tags::handle_show));
|
||||
}
|
||||
|
||||
Tags::~Tags() {
|
||||
@ -165,6 +161,19 @@ Tags::~Tags() {
|
||||
if (control_) {
|
||||
zriver_control_v1_destroy(control_);
|
||||
}
|
||||
|
||||
if (status_manager_) {
|
||||
zriver_status_manager_v1_destroy(status_manager_);
|
||||
}
|
||||
}
|
||||
|
||||
void Tags::handle_show() {
|
||||
struct wl_output *output = gdk_wayland_monitor_get_wl_output(bar_.output->monitor->gobj());
|
||||
output_status_ = zriver_status_manager_v1_get_river_output_status(status_manager_, output);
|
||||
zriver_output_status_v1_add_listener(output_status_, &output_status_listener_impl, this);
|
||||
|
||||
zriver_status_manager_v1_destroy(status_manager_);
|
||||
status_manager_ = nullptr;
|
||||
}
|
||||
|
||||
void Tags::handle_primary_clicked(uint32_t tag) {
|
||||
|
@ -74,6 +74,13 @@ Item::Item(const std::string& bn, const std::string& op, const Json::Value& conf
|
||||
cancellable_, interface);
|
||||
}
|
||||
|
||||
Item::~Item() {
|
||||
if (this->gtk_menu != nullptr) {
|
||||
this->gtk_menu->popdown();
|
||||
this->gtk_menu->detach();
|
||||
}
|
||||
}
|
||||
|
||||
bool Item::handleMouseEnter(GdkEventCrossing* const& e) {
|
||||
event_box.set_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT);
|
||||
return false;
|
||||
@ -443,6 +450,9 @@ void Item::makeMenu() {
|
||||
gtk_menu->attach_to_widget(event_box);
|
||||
}
|
||||
}
|
||||
// Manually reset prelight to make sure the tray item doesn't stay in a hover state even though
|
||||
// the menu is focused
|
||||
event_box.unset_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT);
|
||||
}
|
||||
|
||||
bool Item::handleClick(GdkEventButton* const& ev) {
|
||||
|
@ -74,12 +74,14 @@ auto waybar::modules::Temperature::update() -> void {
|
||||
if (critical) {
|
||||
format = config_["format-critical"].isString() ? config_["format-critical"].asString() : format;
|
||||
label_.get_style_context()->add_class("critical");
|
||||
} else if (warning) {
|
||||
format = config_["format-warning"].isString() ? config_["format-warning"].asString() : format;
|
||||
label_.get_style_context()->add_class("warning");
|
||||
} else {
|
||||
label_.get_style_context()->remove_class("critical");
|
||||
label_.get_style_context()->remove_class("warning");
|
||||
if (warning) {
|
||||
format = config_["format-warning"].isString() ? config_["format-warning"].asString() : format;
|
||||
label_.get_style_context()->add_class("warning");
|
||||
} else {
|
||||
label_.get_style_context()->remove_class("warning");
|
||||
}
|
||||
}
|
||||
|
||||
if (format.empty()) {
|
||||
|
@ -41,6 +41,7 @@ EnumType EnumParser<EnumType>::parseStringToEnum(const std::string& str,
|
||||
// Explicit instantiations for specific EnumType types you intend to use
|
||||
// Add explicit instantiations for all relevant EnumType types
|
||||
template struct EnumParser<modules::hyprland::Workspaces::SortMethod>;
|
||||
template struct EnumParser<modules::hyprland::Workspaces::ActiveWindowPosition>;
|
||||
template struct EnumParser<util::KillSignalAction>;
|
||||
|
||||
} // namespace waybar::util
|
||||
|
@ -1,13 +1,13 @@
|
||||
[wrap-file]
|
||||
directory = spdlog-1.14.1
|
||||
source_url = https://github.com/gabime/spdlog/archive/refs/tags/v1.14.1.tar.gz
|
||||
source_filename = spdlog-1.14.1.tar.gz
|
||||
source_hash = 1586508029a7d0670dfcb2d97575dcdc242d3868a259742b69f100801ab4e16b
|
||||
patch_filename = spdlog_1.14.1-1_patch.zip
|
||||
patch_url = https://wrapdb.mesonbuild.com/v2/spdlog_1.14.1-1/get_patch
|
||||
patch_hash = ae878e732330ea1048f90d7e117c40c0cd2a6fb8ae5492c7955818ce3aaade6c
|
||||
source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/spdlog_1.14.1-1/spdlog-1.14.1.tar.gz
|
||||
wrapdb_version = 1.14.1-1
|
||||
directory = spdlog-1.15.2
|
||||
source_url = https://github.com/gabime/spdlog/archive/refs/tags/v1.15.2.tar.gz
|
||||
source_filename = spdlog-1.15.2.tar.gz
|
||||
source_hash = 7a80896357f3e8e920e85e92633b14ba0f229c506e6f978578bdc35ba09e9a5d
|
||||
patch_filename = spdlog_1.15.2-3_patch.zip
|
||||
patch_url = https://wrapdb.mesonbuild.com/v2/spdlog_1.15.2-3/get_patch
|
||||
patch_hash = d5ab078661f571ef5113a8e4bc5c4121e16c044e7772a24b44b1ca8f3ee7c6cb
|
||||
source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/spdlog_1.15.2-3/spdlog-1.15.2.tar.gz
|
||||
wrapdb_version = 1.15.2-3
|
||||
|
||||
[provide]
|
||||
spdlog = spdlog_dep
|
||||
|
@ -84,6 +84,33 @@ TEST_CASE("Load simple config with include", "[config]") {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Load simple config with wildcard include", "[config]") {
|
||||
waybar::Config conf;
|
||||
conf.load("test/config/include-wildcard.json");
|
||||
|
||||
auto& data = conf.getConfig();
|
||||
SECTION("validate cpu include file") { REQUIRE(data["cpu"]["format"].asString() == "goo"); }
|
||||
SECTION("validate memory include file") { REQUIRE(data["memory"]["format"].asString() == "foo"); }
|
||||
}
|
||||
|
||||
TEST_CASE("Load config using relative paths and wildcards", "[config]") {
|
||||
waybar::Config conf;
|
||||
|
||||
const char* old_config_path = std::getenv(waybar::Config::CONFIG_PATH_ENV);
|
||||
setenv(waybar::Config::CONFIG_PATH_ENV, "test/config", 1);
|
||||
|
||||
conf.load("test/config/include-relative-path.json");
|
||||
|
||||
auto& data = conf.getConfig();
|
||||
SECTION("validate cpu include file") { REQUIRE(data["cpu"]["format"].asString() == "goo"); }
|
||||
SECTION("validate memory include file") { REQUIRE(data["memory"]["format"].asString() == "foo"); }
|
||||
|
||||
if (old_config_path)
|
||||
setenv(waybar::Config::CONFIG_PATH_ENV, old_config_path, 1);
|
||||
else
|
||||
unsetenv(waybar::Config::CONFIG_PATH_ENV);
|
||||
}
|
||||
|
||||
TEST_CASE("Load multiple bar config with include", "[config]") {
|
||||
waybar::Config conf;
|
||||
conf.load("test/config/include-multi.json");
|
||||
|
5
test/config/include-relative-path.json
Normal file
5
test/config/include-relative-path.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"include": ["modules/*.jsonc"],
|
||||
"position": "top",
|
||||
"nullOption": null
|
||||
}
|
5
test/config/include-wildcard.json
Normal file
5
test/config/include-wildcard.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"include": ["test/config/modules/*.jsonc"],
|
||||
"position": "top",
|
||||
"nullOption": null
|
||||
}
|
6
test/config/modules/cpu.jsonc
Normal file
6
test/config/modules/cpu.jsonc
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"cpu": {
|
||||
"interval": 2,
|
||||
"format": "goo"
|
||||
}
|
||||
}
|
6
test/config/modules/memory.jsonc
Normal file
6
test/config/modules/memory.jsonc
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"memory": {
|
||||
"interval": 2,
|
||||
"format": "foo",
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user