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
|
./build/waybar --log-level debug
|
||||||
|
|
||||||
test:
|
test:
|
||||||
meson test -C build --no-rebuild --verbose --suite waybar
|
meson test -C build --verbose --suite waybar
|
||||||
.PHONY: test
|
.PHONY: test
|
||||||
|
|
||||||
|
test-detailed:
|
||||||
|
meson test -C build --verbose --print-errorlogs --test-args='--reporter console -s'
|
||||||
|
.PHONY: test-detailed
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf build
|
rm -rf build
|
||||||
|
6
flake.lock
generated
6
flake.lock
generated
@ -18,11 +18,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1753694789,
|
"lastModified": 1759036355,
|
||||||
"narHash": "sha256-cKgvtz6fKuK1Xr5LQW/zOUiAC0oSQoA9nOISB0pJZqM=",
|
"narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "dc9637876d0dcc8c9e5e22986b857632effeb727",
|
"rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
42
flake.nix
42
flake.nix
@ -65,7 +65,7 @@
|
|||||||
nativeBuildInputs =
|
nativeBuildInputs =
|
||||||
pkgs.waybar.nativeBuildInputs
|
pkgs.waybar.nativeBuildInputs
|
||||||
++ (with pkgs; [
|
++ (with pkgs; [
|
||||||
nixfmt-rfc-style
|
nixfmt
|
||||||
clang-tools
|
clang-tools
|
||||||
gdb
|
gdb
|
||||||
]);
|
]);
|
||||||
@ -75,28 +75,26 @@
|
|||||||
formatter = genSystems (
|
formatter = genSystems (
|
||||||
pkgs:
|
pkgs:
|
||||||
pkgs.treefmt.withConfig {
|
pkgs.treefmt.withConfig {
|
||||||
settings = [
|
settings = {
|
||||||
{
|
formatter = {
|
||||||
formatter = {
|
clang-format = {
|
||||||
clang-format = {
|
options = [ "-i" ];
|
||||||
options = [ "-i" ];
|
command = lib.getExe' pkgs.clang-tools "clang-format";
|
||||||
command = lib.getExe' pkgs.clang-tools "clang-format";
|
excludes = [ ];
|
||||||
excludes = [ ];
|
includes = [
|
||||||
includes = [
|
"*.c"
|
||||||
"*.c"
|
"*.cpp"
|
||||||
"*.cpp"
|
"*.h"
|
||||||
"*.h"
|
"*.hpp"
|
||||||
"*.hpp"
|
];
|
||||||
];
|
|
||||||
};
|
|
||||||
nixfmt = {
|
|
||||||
command = lib.getExe pkgs.nixfmt-rfc-style;
|
|
||||||
includes = [ "*.nix" ];
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
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:
|
protected:
|
||||||
Gtk::Label label_;
|
Gtk::Label label_;
|
||||||
std::string format_;
|
std::string format_;
|
||||||
const std::chrono::seconds interval_;
|
const std::chrono::milliseconds interval_;
|
||||||
bool alt_ = false;
|
bool alt_ = false;
|
||||||
std::string default_format_;
|
std::string default_format_;
|
||||||
|
|
||||||
|
@ -35,7 +35,8 @@ class Config {
|
|||||||
void setupConfig(Json::Value &dst, const std::string &config_file, int depth);
|
void setupConfig(Json::Value &dst, const std::string &config_file, int depth);
|
||||||
void resolveConfigIncludes(Json::Value &config, int depth);
|
void resolveConfigIncludes(Json::Value &config, int depth);
|
||||||
void mergeConfig(Json::Value &a_config_, Json::Value &b_config_);
|
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_;
|
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
|
std::string cldYearCached_; // calendar Year mode. Cached calendar
|
||||||
date::year_month cldMonShift_; // calendar Month mode. Cached ym
|
date::year_month cldMonShift_; // calendar Month mode. Cached ym
|
||||||
std::string cldMonCached_; // calendar Month mode. Cached calendar
|
std::string cldMonCached_; // calendar Month mode. Cached calendar
|
||||||
date::day cldBaseDay_{0}; // calendar Cached day. Is used when today is changing(midnight)
|
date::day cldBaseDay_{0}; // calendar Cached day. Is used when today is changing(midnight)
|
||||||
std::string cldText_{""}; // calendar text to print
|
std::string cldText_{""}; // calendar text to print
|
||||||
|
bool iso8601Calendar_{false}; // whether the calendar is in ISO8601
|
||||||
CldMode cldMode_{CldMode::MONTH};
|
CldMode cldMode_{CldMode::MONTH};
|
||||||
auto get_calendar(const date::year_month_day& today, const date::year_month_day& ymd,
|
auto get_calendar(const date::year_month_day& today, const date::year_month_day& ymd,
|
||||||
const date::time_zone* tz) -> const std::string;
|
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
|
std::vector<const date::time_zone*> tzList_; // time zones list
|
||||||
int tzCurrIdx_; // current time zone index for tzList_
|
int tzCurrIdx_; // current time zone index for tzList_
|
||||||
std::string tzText_{""}; // time zones text to print
|
std::string tzText_{""}; // time zones text to print
|
||||||
|
std::string tzTooltipFormat_{""}; // optional timezone tooltip format
|
||||||
util::SleeperThread thread_;
|
util::SleeperThread thread_;
|
||||||
|
|
||||||
// ordinal date in tooltip
|
// ordinal date in tooltip
|
||||||
|
@ -136,8 +136,6 @@ class Workspace {
|
|||||||
Gtk::Button button_;
|
Gtk::Button button_;
|
||||||
Gtk::Box content_;
|
Gtk::Box content_;
|
||||||
Gtk::Label label_;
|
Gtk::Label label_;
|
||||||
|
|
||||||
bool needs_updating_ = false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace waybar::modules::ext
|
} // namespace waybar::modules::ext
|
||||||
|
@ -46,6 +46,7 @@ class IPC {
|
|||||||
util::JsonParser parser_;
|
util::JsonParser parser_;
|
||||||
std::list<std::pair<std::string, EventHandler*>> callbacks_;
|
std::list<std::pair<std::string, EventHandler*>> callbacks_;
|
||||||
int socketfd_; // the hyprland socket file descriptor
|
int socketfd_; // the hyprland socket file descriptor
|
||||||
|
pid_t socketOwnerPid_;
|
||||||
bool running_ = true; // the ipcThread will stop running when this is false
|
bool running_ = true; // the ipcThread will stop running when this is false
|
||||||
};
|
};
|
||||||
}; // namespace waybar::modules::hyprland
|
}; // namespace waybar::modules::hyprland
|
||||||
|
@ -42,7 +42,6 @@ class Workspace {
|
|||||||
bool isPersistentConfig() const { return m_isPersistentConfig; };
|
bool isPersistentConfig() const { return m_isPersistentConfig; };
|
||||||
bool isPersistentRule() const { return m_isPersistentRule; };
|
bool isPersistentRule() const { return m_isPersistentRule; };
|
||||||
bool isVisible() const { return m_isVisible; };
|
bool isVisible() const { return m_isVisible; };
|
||||||
bool isEmpty() const { return m_windows == 0; };
|
|
||||||
bool isUrgent() const { return m_isUrgent; };
|
bool isUrgent() const { return m_isUrgent; };
|
||||||
|
|
||||||
bool handleClicked(GdkEventButton* bt) const;
|
bool handleClicked(GdkEventButton* bt) const;
|
||||||
@ -88,6 +87,7 @@ class Workspace {
|
|||||||
Gtk::Label m_labelBefore;
|
Gtk::Label m_labelBefore;
|
||||||
Gtk::Label m_labelAfter;
|
Gtk::Label m_labelAfter;
|
||||||
|
|
||||||
|
bool isEmpty() const;
|
||||||
void updateTaskbar(const std::string& workspace_icon);
|
void updateTaskbar(const std::string& workspace_icon);
|
||||||
bool handleClick(const GdkEventButton* event_button, WindowAddress const& addr) const;
|
bool handleClick(const GdkEventButton* event_button, WindowAddress const& addr) const;
|
||||||
bool shouldSkipWindow(const WindowRepr& window_repr) 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 taskbarFormatAfter() const -> std::string { return m_taskbarFormatAfter; }
|
||||||
auto taskbarIconSize() const -> int { return m_taskbarIconSize; }
|
auto taskbarIconSize() const -> int { return m_taskbarIconSize; }
|
||||||
auto taskbarOrientation() const -> Gtk::Orientation { return m_taskbarOrientation; }
|
auto taskbarOrientation() const -> Gtk::Orientation { return m_taskbarOrientation; }
|
||||||
|
auto taskbarReverseDirection() const -> bool { return m_taskbarReverseDirection; }
|
||||||
auto onClickWindow() const -> std::string { return m_onClickWindow; }
|
auto onClickWindow() const -> std::string { return m_onClickWindow; }
|
||||||
auto getIgnoredWindows() const -> std::vector<std::regex> { return m_ignoreWindows; }
|
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 getRewrite(std::string window_class, std::string window_title);
|
||||||
std::string& getWindowSeparator() { return m_formatWindowSeparator; }
|
std::string& getWindowSeparator() { return m_formatWindowSeparator; }
|
||||||
bool isWorkspaceIgnored(std::string const& workspace_name);
|
bool isWorkspaceIgnored(std::string const& workspace_name);
|
||||||
@ -183,6 +187,14 @@ class Workspaces : public AModule, public EventHandler {
|
|||||||
std::string m_taskbarFormatAfter;
|
std::string m_taskbarFormatAfter;
|
||||||
int m_taskbarIconSize = 16;
|
int m_taskbarIconSize = 16;
|
||||||
Gtk::Orientation m_taskbarOrientation = Gtk::ORIENTATION_HORIZONTAL;
|
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_onClickWindow;
|
||||||
std::string m_currentActiveWindowAddress;
|
std::string m_currentActiveWindowAddress;
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ class Image : public AModule {
|
|||||||
std::string path_;
|
std::string path_;
|
||||||
std::string tooltip_;
|
std::string tooltip_;
|
||||||
int size_;
|
int size_;
|
||||||
int interval_;
|
std::chrono::milliseconds interval_;
|
||||||
util::command::res output_;
|
util::command::res output_;
|
||||||
|
|
||||||
util::SleeperThread thread_;
|
util::SleeperThread thread_;
|
||||||
|
@ -33,6 +33,7 @@ class Language : public ALabel, public EventHandler {
|
|||||||
|
|
||||||
std::vector<Layout> layouts_;
|
std::vector<Layout> layouts_;
|
||||||
unsigned current_idx_;
|
unsigned current_idx_;
|
||||||
|
std::string last_short_name_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace waybar::modules::niri
|
} // namespace waybar::modules::niri
|
||||||
|
@ -21,6 +21,7 @@ class Tags : public waybar::AModule {
|
|||||||
void handle_view_tags(struct wl_array *tags);
|
void handle_view_tags(struct wl_array *tags);
|
||||||
void handle_urgent_tags(uint32_t tags);
|
void handle_urgent_tags(uint32_t tags);
|
||||||
|
|
||||||
|
void handle_show();
|
||||||
void handle_primary_clicked(uint32_t tag);
|
void handle_primary_clicked(uint32_t tag);
|
||||||
bool handle_button_press(GdkEventButton *event_button, uint32_t tag);
|
bool handle_button_press(GdkEventButton *event_button, uint32_t tag);
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ struct ToolTip {
|
|||||||
class Item : public sigc::trackable {
|
class Item : public sigc::trackable {
|
||||||
public:
|
public:
|
||||||
Item(const std::string&, const std::string&, const Json::Value&, const Bar&);
|
Item(const std::string&, const std::string&, const Json::Value&, const Bar&);
|
||||||
~Item() = default;
|
~Item();
|
||||||
|
|
||||||
std::string bus_name;
|
std::string bus_name;
|
||||||
std::string object_path;
|
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
|
:[ 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.
|
the scroll wheel. Do not specify *timezone* option when *timezones* is specified.
|
||||||
"" represents the system's local timezone
|
"" 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*
|
|[ *locale*
|
||||||
:[ string
|
:[ string
|
||||||
:[
|
:[
|
||||||
@ -126,6 +132,12 @@ View all valid format options in *strftime(3)* or have a look https://en.cpprefe
|
|||||||
:[ 1
|
:[ 1
|
||||||
:[ Value to scroll months/years forward/backward. Can be negative. Is
|
:[ Value to scroll months/years forward/backward. Can be negative. Is
|
||||||
configured under *on-scroll* option
|
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*
|
3. Addressed by *clock: calendar: format*
|
||||||
[- *Option*
|
[- *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
|
# STYLE
|
||||||
|
|
||||||
- *#clock*
|
- *#clock*
|
||||||
|
@ -11,9 +11,10 @@ The *cpu* module displays the current CPU utilization.
|
|||||||
# CONFIGURATION
|
# CONFIGURATION
|
||||||
|
|
||||||
*interval*: ++
|
*interval*: ++
|
||||||
typeof: integer ++
|
typeof: integer or float ++
|
||||||
default: 10 ++
|
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*: ++
|
*format*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
|
@ -35,15 +35,17 @@ Addressed by *custom/<name>*
|
|||||||
See *return-type*
|
See *return-type*
|
||||||
|
|
||||||
*interval*: ++
|
*interval*: ++
|
||||||
typeof: integer ++
|
typeof: integer or float ++
|
||||||
The interval (in seconds) in which the information gets polled. ++
|
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. ++
|
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. ++
|
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.
|
If a *signal* is defined then the script will run once on startup and will only update with a signal.
|
||||||
|
|
||||||
*restart-interval*: ++
|
*restart-interval*: ++
|
||||||
typeof: integer ++
|
typeof: integer or float ++
|
||||||
The restart interval (in seconds). ++
|
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. ++
|
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*.
|
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.
|
*activate*: Switch to workspace.
|
||||||
|
|
||||||
|
*deactivate*: Deactivate the workspace.
|
||||||
|
|
||||||
*close*: Close the workspace.
|
*close*: Close the workspace.
|
||||||
|
|
||||||
# ICONS
|
# ICONS
|
||||||
|
@ -50,6 +50,21 @@ This setting is ignored if *workspace-taskbar.enable* is set to true.
|
|||||||
default: false ++
|
default: false ++
|
||||||
Enables the workspace taskbar mode.
|
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*: ++
|
*format*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
default: {icon} ++
|
default: {icon} ++
|
||||||
@ -70,6 +85,19 @@ This setting is ignored if *workspace-taskbar.enable* is set to true.
|
|||||||
default: horizontal ++
|
default: horizontal ++
|
||||||
Direction in which the workspace taskbar is displayed.
|
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*: ++
|
*show-special*: ++
|
||||||
typeof: bool ++
|
typeof: bool ++
|
||||||
default: false ++
|
default: false ++
|
||||||
@ -216,4 +244,6 @@ Additional to workspace name matching, the following *format-icons* can be set.
|
|||||||
- *#workspaces button.special*
|
- *#workspaces button.special*
|
||||||
- *#workspaces button.urgent*
|
- *#workspaces button.urgent*
|
||||||
- *#workspaces button.hosting-monitor* (gets applied if workspace-monitor == waybar-monitor)
|
- *#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.
|
The width/height to render the image.
|
||||||
|
|
||||||
*interval*: ++
|
*interval*: ++
|
||||||
typeof: integer ++
|
typeof: integer or float ++
|
||||||
The interval (in seconds) to re-render the image. ++
|
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. ++
|
This is useful if the contents of *path* changes. ++
|
||||||
If no *interval* is defined, the image will only be rendered once.
|
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.
|
The format of information displayed in the tooltip.
|
||||||
|
|
||||||
*interval*: ++
|
*interval*: ++
|
||||||
typeof: integer ++
|
typeof: integer or float ++
|
||||||
default: 1 ++
|
default: 1 ++
|
||||||
The interval in which the information gets polled.
|
The interval in which the information gets polled.
|
||||||
|
|
||||||
|
@ -87,15 +87,15 @@ Module config :
|
|||||||
```
|
```
|
||||||
"custom/power": {
|
"custom/power": {
|
||||||
"format" : "⏻ ",
|
"format" : "⏻ ",
|
||||||
"tooltip": false,
|
"tooltip": false,
|
||||||
"menu": "on-click",
|
"menu": "on-click",
|
||||||
"menu-file": "~/.config/waybar/power_menu.xml",
|
"menu-file": "~/.config/waybar/power_menu.xml",
|
||||||
"menu-actions": {
|
"menu-actions": {
|
||||||
"shutdown": "shutdown",
|
"shutdown": "shutdown",
|
||||||
"reboot": "reboot",
|
"reboot": "reboot",
|
||||||
"suspend": "systemctl suspend",
|
"suspend": "systemctl suspend",
|
||||||
"hibernate": "systemctl hibernate",
|
"hibernate": "systemctl hibernate",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -104,28 +104,28 @@ Module config :
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<interface>
|
<interface>
|
||||||
<object class="GtkMenu" id="menu">
|
<object class="GtkMenu" id="menu">
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkMenuItem" id="suspend">
|
<object class="GtkMenuItem" id="suspend">
|
||||||
<property name="label">Suspend</property>
|
<property name="label">Suspend</property>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkMenuItem" id="hibernat">
|
<object class="GtkMenuItem" id="hibernate">
|
||||||
<property name="label">Hibernate</property>
|
<property name="label">Hibernate</property>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkMenuItem" id="shutdown">
|
<object class="GtkMenuItem" id="shutdown">
|
||||||
<property name="label">Shutdown</property>
|
<property name="label">Shutdown</property>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkSeparatorMenuItem" id="delimiter1"/>
|
<object class="GtkSeparatorMenuItem" id="delimiter1"/>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkMenuItem" id="reboot">
|
<object class="GtkMenuItem" id="reboot">
|
||||||
<property name="label">Reboot</property>
|
<property name="label">Reboot</property>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</interface>
|
</interface>
|
||||||
|
@ -52,8 +52,8 @@ Addressed by *niri/language*
|
|||||||
|
|
||||||
```
|
```
|
||||||
"niri/language": {
|
"niri/language": {
|
||||||
"format": "Lang: {long}"
|
"format": "Lang: {long}",
|
||||||
"format-en": "AMERICA, HELL YEAH!"
|
"format-en": "AMERICA, HELL YEAH!",
|
||||||
"format-tr": "As bayrakları"
|
"format-tr": "As bayrakları"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -61,3 +61,12 @@ Addressed by *niri/language*
|
|||||||
# STYLE
|
# STYLE
|
||||||
|
|
||||||
- *#language*
|
- *#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).
|
The threshold before it is considered critical (Celsius).
|
||||||
|
|
||||||
*interval*: ++
|
*interval*: ++
|
||||||
typeof: integer ++
|
typeof: integer or float ++
|
||||||
default: 10 ++
|
default: 10 ++
|
||||||
The interval in which the information gets polled.
|
The interval in which the information gets polled.
|
||||||
|
|
||||||
@ -160,4 +160,5 @@ Addressed by *temperature*
|
|||||||
# STYLE
|
# STYLE
|
||||||
|
|
||||||
- *#temperature*
|
- *#temperature*
|
||||||
|
- *#temperature.warning*
|
||||||
- *#temperature.critical*
|
- *#temperature.critical*
|
||||||
|
@ -437,4 +437,4 @@ A group may hide all but one element, showing them only on mouse hover. In order
|
|||||||
# SEE ALSO
|
# SEE ALSO
|
||||||
|
|
||||||
*sway-output(5)*
|
*sway-output(5)*
|
||||||
*waybar-styles(5)"
|
*waybar-styles(5)*
|
||||||
|
@ -69,7 +69,7 @@ is_openbsd = host_machine.system() == 'openbsd'
|
|||||||
|
|
||||||
thread_dep = dependency('threads')
|
thread_dep = dependency('threads')
|
||||||
fmt = dependency('fmt', version : ['>=8.1.1'], fallback : ['fmt', 'fmt_dep'])
|
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_client = dependency('wayland-client')
|
||||||
wayland_cursor = dependency('wayland-cursor')
|
wayland_cursor = dependency('wayland-cursor')
|
||||||
wayland_protos = dependency('wayland-protocols')
|
wayland_protos = dependency('wayland-protocols')
|
||||||
@ -505,7 +505,7 @@ cava = dependency('cava',
|
|||||||
|
|
||||||
if cava.found()
|
if cava.found()
|
||||||
add_project_arguments('-DHAVE_LIBCAVA', language: 'cpp')
|
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')
|
man_files += files('man/waybar-cava.5.scd')
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
@ -30,13 +30,12 @@ in
|
|||||||
# nixpkgs checks version, no need when building locally
|
# nixpkgs checks version, no need when building locally
|
||||||
nativeInstallCheckInputs = [ ];
|
nativeInstallCheckInputs = [ ];
|
||||||
|
|
||||||
buildInputs = (builtins.filter (p:
|
buildInputs =
|
||||||
p.pname != "wireplumber" &&
|
(builtins.filter (p: p.pname != "wireplumber" && p.pname != "gps") oldAttrs.buildInputs)
|
||||||
p.pname != "gps"
|
++ [
|
||||||
) oldAttrs.buildInputs) ++ [
|
pkgs.wireplumber
|
||||||
pkgs.wireplumber
|
pkgs.gpsd
|
||||||
pkgs.gpsd
|
];
|
||||||
];
|
|
||||||
|
|
||||||
postUnpack = ''
|
postUnpack = ''
|
||||||
pushd "$sourceRoot"
|
pushd "$sourceRoot"
|
||||||
|
@ -63,7 +63,7 @@ button:hover {
|
|||||||
background: rgba(0, 0, 0, 0.2);
|
background: rgba(0, 0, 0, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#workspaces button.focused {
|
#workspaces button.focused, #workspaces button.active {
|
||||||
background-color: #64727D;
|
background-color: #64727D;
|
||||||
box-shadow: inset 0 -3px #ffffff;
|
box-shadow: inset 0 -3px #ffffff;
|
||||||
}
|
}
|
||||||
|
@ -63,7 +63,8 @@ std::optional<std::string> getDesktopFilePath(const std::string& app_identifier,
|
|||||||
return {};
|
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) {
|
for (const auto& data_dir : data_dirs) {
|
||||||
const auto data_app_dir = data_dir + "/applications/";
|
const auto data_app_dir = data_dir + "/applications/";
|
||||||
auto desktop_file_suffix = app_identifier + ".desktop";
|
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);
|
box_.set_spacing(spacing);
|
||||||
|
|
||||||
bool swap_icon_label = false;
|
bool swap_icon_label = false;
|
||||||
if (config_.isMember("swap-icon-label")) {
|
if (config_["swap-icon-label"].isNull()) {
|
||||||
if (!config_["swap-icon-label"].isBool())
|
} else if (config_["swap-icon-label"].isBool()) {
|
||||||
spdlog::warn("'swap-icon-label' must be a bool.");
|
swap_icon_label = config_["swap-icon-label"].asBool();
|
||||||
else
|
} else {
|
||||||
swap_icon_label = config_["swap-icon-label"].asBool();
|
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) {
|
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,
|
config["format-alt"].isString() || config["menu"].isString() || enable_click,
|
||||||
enable_scroll),
|
enable_scroll),
|
||||||
format_(config_["format"].isString() ? config_["format"].asString() : format),
|
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"
|
interval_(config_["interval"] == "once"
|
||||||
? std::chrono::seconds::max()
|
? std::chrono::milliseconds::max()
|
||||||
: std::chrono::seconds(
|
: std::chrono::milliseconds(
|
||||||
config_["interval"].isUInt() ? config_["interval"].asUInt() : interval)),
|
(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_) {
|
default_format_(format_) {
|
||||||
label_.set_name(name);
|
label_.set_name(name);
|
||||||
if (!id.empty()) {
|
if (!id.empty()) {
|
||||||
|
@ -172,6 +172,10 @@ bool AModule::handleUserEvent(GdkEventButton* const& e) {
|
|||||||
// Popup the menu
|
// Popup the menu
|
||||||
gtk_widget_show_all(GTK_WIDGET(menu_));
|
gtk_widget_show_all(GTK_WIDGET(menu_));
|
||||||
gtk_menu_popup_at_pointer(GTK_MENU(menu_), reinterpret_cast<GdkEvent*>(e));
|
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
|
// 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).
|
* 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);
|
surface = gdk_wayland_window_get_wl_surface(gdk_window);
|
||||||
configureGlobalOffset(gdk_window_get_width(gdk_window), gdk_window_get_height(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);
|
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, "");
|
auto match1 = tryExpandPath(name, "");
|
||||||
if (!match1.empty()) {
|
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) {
|
void Config::resolveConfigIncludes(Json::Value &config, int depth) {
|
||||||
@ -119,18 +131,22 @@ void Config::resolveConfigIncludes(Json::Value &config, int depth) {
|
|||||||
if (includes.isArray()) {
|
if (includes.isArray()) {
|
||||||
for (const auto &include : includes) {
|
for (const auto &include : includes) {
|
||||||
spdlog::info("Including resource file: {}", include.asString());
|
spdlog::info("Including resource file: {}", include.asString());
|
||||||
auto match = findIncludePath(include.asString());
|
auto matches = findIncludePath(include.asString());
|
||||||
if (match.has_value()) {
|
if (!matches.empty()) {
|
||||||
setupConfig(config, match.value(), depth + 1);
|
for (const auto &match : matches) {
|
||||||
|
setupConfig(config, match, depth + 1);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
spdlog::warn("Unable to find resource file: {}", include.asString());
|
spdlog::warn("Unable to find resource file: {}", include.asString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (includes.isString()) {
|
} else if (includes.isString()) {
|
||||||
spdlog::info("Including resource file: {}", includes.asString());
|
spdlog::info("Including resource file: {}", includes.asString());
|
||||||
auto match = findIncludePath(includes.asString());
|
auto matches = findIncludePath(includes.asString());
|
||||||
if (match.has_value()) {
|
if (!matches.empty()) {
|
||||||
setupConfig(config, match.value(), depth + 1);
|
for (const auto &match : matches) {
|
||||||
|
setupConfig(config, match, depth + 1);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
spdlog::warn("Unable to find resource file: {}", includes.asString());
|
spdlog::warn("Unable to find resource file: {}", includes.asString());
|
||||||
}
|
}
|
||||||
|
@ -109,7 +109,7 @@
|
|||||||
#include "modules/wireplumber.hpp"
|
#include "modules/wireplumber.hpp"
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_LIBCAVA
|
#ifdef HAVE_LIBCAVA
|
||||||
#include "modules/cava.hpp"
|
#include "modules/cava/cava.hpp"
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_SYSTEMD_MONITOR
|
#ifdef HAVE_SYSTEMD_MONITOR
|
||||||
#include "modules/systemd_failed_units.hpp"
|
#include "modules/systemd_failed_units.hpp"
|
||||||
@ -343,7 +343,7 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name,
|
|||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_LIBCAVA
|
#ifdef HAVE_LIBCAVA
|
||||||
if (ref == "cava") {
|
if (ref == "cava") {
|
||||||
return new waybar::modules::Cava(id, config_[name]);
|
return new waybar::modules::cava::Cava(id, config_[name]);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_SYSTEMD_MONITOR
|
#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},
|
cldMonShift_{year(1900) / January},
|
||||||
tzInTooltip_{m_tlpFmt_.find("{" + kTZPlaceholder + "}") != std::string::npos},
|
tzInTooltip_{m_tlpFmt_.find("{" + kTZPlaceholder + "}") != std::string::npos},
|
||||||
tzCurrIdx_{0},
|
tzCurrIdx_{0},
|
||||||
|
tzTooltipFormat_{config_["timezone-tooltip-format"].isString()
|
||||||
|
? config_["timezone-tooltip-format"].asString()
|
||||||
|
: ""},
|
||||||
ordInTooltip_{m_tlpFmt_.find("{" + kOrdPlaceholder + "}") != std::string::npos} {
|
ordInTooltip_{m_tlpFmt_.find("{" + kOrdPlaceholder + "}") != std::string::npos} {
|
||||||
m_tlpText_ = m_tlpFmt_;
|
m_tlpText_ = m_tlpFmt_;
|
||||||
|
|
||||||
@ -63,8 +66,8 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
|
|||||||
if (cldInTooltip_) {
|
if (cldInTooltip_) {
|
||||||
if (config_[kCldPlaceholder]["mode"].isString()) {
|
if (config_[kCldPlaceholder]["mode"].isString()) {
|
||||||
const std::string cfgMode{config_[kCldPlaceholder]["mode"].asString()};
|
const std::string cfgMode{config_[kCldPlaceholder]["mode"].asString()};
|
||||||
const std::map<std::string_view, const CldMode&> monthModes{{"month", CldMode::MONTH},
|
const std::map<std::string, CldMode> monthModes{{"month", CldMode::MONTH},
|
||||||
{"year", CldMode::YEAR}};
|
{"year", CldMode::YEAR}};
|
||||||
if (monthModes.find(cfgMode) != monthModes.end())
|
if (monthModes.find(cfgMode) != monthModes.end())
|
||||||
cldMode_ = monthModes.at(cfgMode);
|
cldMode_ = monthModes.at(cfgMode);
|
||||||
else
|
else
|
||||||
@ -73,6 +76,11 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
|
|||||||
"using instead",
|
"using instead",
|
||||||
cfgMode);
|
cfgMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (config_[kCldPlaceholder]["iso8601"].isBool()) {
|
||||||
|
iso8601Calendar_ = config_[kCldPlaceholder]["iso8601"].asBool();
|
||||||
|
}
|
||||||
|
|
||||||
if (config_[kCldPlaceholder]["weeks-pos"].isString()) {
|
if (config_[kCldPlaceholder]["weeks-pos"].isString()) {
|
||||||
if (config_[kCldPlaceholder]["weeks-pos"].asString() == "left") cldWPos_ = WS::LEFT;
|
if (config_[kCldPlaceholder]["weeks-pos"].asString() == "left") cldWPos_ = WS::LEFT;
|
||||||
if (config_[kCldPlaceholder]["weeks-pos"].asString() == "right") cldWPos_ = WS::RIGHT;
|
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, "{}"});
|
fmtMap_.insert({2, "{}"});
|
||||||
if (config_[kCldPlaceholder]["format"]["today"].isString()) {
|
if (config_[kCldPlaceholder]["format"]["today"].isString()) {
|
||||||
fmtMap_.insert({3, config_[kCldPlaceholder]["format"]["today"].asString()});
|
fmtMap_.insert({3, config_[kCldPlaceholder]["format"]["today"].asString()});
|
||||||
cldBaseDay_ =
|
auto local_time = zoned_time{local_zone(), system_clock::now()}.get_local_time();
|
||||||
year_month_day{
|
cldBaseDay_ = year_month_day{floor<days>(local_time)}.day();
|
||||||
floor<days>(zoned_time{local_zone(), system_clock::now()}.get_local_time())}
|
|
||||||
.day();
|
|
||||||
} else
|
} else
|
||||||
fmtMap_.insert({3, "{}"});
|
fmtMap_.insert({3, "{}"});
|
||||||
if (config_[kCldPlaceholder]["format"]["weeks"].isString() && cldWPos_ != WS::HIDDEN) {
|
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(),
|
fmtMap_.insert({4, std::regex_replace(config_[kCldPlaceholder]["format"]["weeks"].asString(),
|
||||||
std::regex("\\{\\}"),
|
std::regex("\\{\\}"), defaultFmt)});
|
||||||
(first_day_of_week() == Monday) ? "{:%W}" : "{:%U}")});
|
|
||||||
Glib::ustring tmp{std::regex_replace(fmtMap_[4], std::regex("</?[^>]+>|\\{.*\\}"), "")};
|
Glib::ustring tmp{std::regex_replace(fmtMap_[4], std::regex("</?[^>]+>|\\{.*\\}"), "")};
|
||||||
cldWnLen_ += tmp.size();
|
cldWnLen_ += tmp.size();
|
||||||
} else {
|
} else {
|
||||||
if (cldWPos_ != WS::HIDDEN)
|
if (cldWPos_ != WS::HIDDEN) {
|
||||||
fmtMap_.insert({4, (first_day_of_week() == Monday) ? "{:%W}" : "{:%U}"});
|
const auto defaultFmt =
|
||||||
else
|
iso8601Calendar_ ? "{:%V}" : ((first_day_of_week() == Monday) ? "{:%W}" : "{:%U}");
|
||||||
|
fmtMap_.insert({4, defaultFmt});
|
||||||
|
} else {
|
||||||
cldWnLen_ = 0;
|
cldWnLen_ = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (config_[kCldPlaceholder]["mode-mon-col"].isInt()) {
|
if (config_[kCldPlaceholder]["mode-mon-col"].isInt()) {
|
||||||
cldMonCols_ = config_[kCldPlaceholder]["mode-mon-col"].asInt();
|
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 "";
|
if (tzList_.size() == 1) return "";
|
||||||
|
|
||||||
std::stringstream os;
|
std::stringstream os;
|
||||||
|
bool first = true;
|
||||||
for (size_t tz_idx{0}; tz_idx < tzList_.size(); ++tz_idx) {
|
for (size_t tz_idx{0}; tz_idx < tzList_.size(); ++tz_idx) {
|
||||||
if (static_cast<int>(tz_idx) == tzCurrIdx_) continue;
|
// Skip local timezone (nullptr) - never show it in tooltip
|
||||||
const auto* tz = tzList_[tz_idx] != nullptr ? tzList_[tz_idx] : local_zone();
|
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}};
|
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();
|
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)
|
auto cldGetWeekForLine(const year_month& ym, const weekday& firstdow, const unsigned line)
|
||||||
-> const year_month_weekday {
|
-> const year_month_weekday {
|
||||||
unsigned index{line - 2};
|
const unsigned idx = line - 2;
|
||||||
if (weekday{ym / 1} == firstdow) ++index;
|
const std::chrono::weekday_indexed indexed_first_day_of_week =
|
||||||
return ym / firstdow[index];
|
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,
|
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
|
// Print non-first week
|
||||||
default: {
|
default: {
|
||||||
auto ymdTmp{cldGetWeekForLine(ym, firstdow, line)};
|
const auto ymdTmp{cldGetWeekForLine(ym, firstdow, line)};
|
||||||
if (ymdTmp.ok()) {
|
if (ymdTmp.ok()) {
|
||||||
auto d{year_month_day{ymdTmp}.day()};
|
auto d{year_month_day{ymdTmp}.day()};
|
||||||
const auto dlast{(ym / last).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{
|
: static_cast<const zoned_seconds&&>(zoned_seconds{
|
||||||
tz, local_days{cldGetWeekForLine(ymTmp, firstdow, line)}})))
|
tz, local_days{cldGetWeekForLine(ymTmp, firstdow, line)}})))
|
||||||
<< ' ';
|
<< ' ';
|
||||||
} else
|
} else {
|
||||||
os << pads;
|
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.
|
// Computations done similarly to Linux cal utility.
|
||||||
auto waybar::modules::Clock::first_day_of_week() -> weekday {
|
auto waybar::modules::Clock::first_day_of_week() -> weekday {
|
||||||
|
if (iso8601Calendar_) {
|
||||||
|
return Monday;
|
||||||
|
}
|
||||||
#ifdef HAVE_LANGINFO_1STDAY
|
#ifdef HAVE_LANGINFO_1STDAY
|
||||||
deleting_unique_ptr<std::remove_pointer<locale_t>::type, freelocale> posix_locale{
|
deleting_unique_ptr<std::remove_pointer<locale_t>::type, freelocale> posix_locale{
|
||||||
newlocale(LC_ALL, m_locale_.name().c_str(), nullptr)};
|
newlocale(LC_ALL, m_locale_.name().c_str(), nullptr)};
|
||||||
|
@ -89,9 +89,11 @@ void waybar::modules::Custom::continuousWorker() {
|
|||||||
dp.emit();
|
dp.emit();
|
||||||
spdlog::error("{} stopped unexpectedly, is it endless?", name_);
|
spdlog::error("{} stopped unexpectedly, is it endless?", name_);
|
||||||
}
|
}
|
||||||
if (config_["restart-interval"].isUInt()) {
|
if (config_["restart-interval"].isNumeric()) {
|
||||||
pid_ = -1;
|
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_);
|
fp_ = util::command::open(cmd, pid_, output_name_);
|
||||||
if (!fp_) {
|
if (!fp_) {
|
||||||
throw std::runtime_error("Unable to open " + cmd);
|
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();
|
const bool config_on_click_middle = config["on-click-middle"].isString();
|
||||||
if (config_on_click_middle) {
|
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();
|
const bool config_on_click_right = config["on-click-right"].isString();
|
||||||
if (config_on_click_right) {
|
if (config_on_click_right) {
|
||||||
on_click_right_action_ = config["on-click"].asString();
|
on_click_right_action_ = config["on-click-right"].asString();
|
||||||
}
|
}
|
||||||
|
|
||||||
// setup UI
|
// setup UI
|
||||||
@ -377,16 +377,19 @@ Workspace::~Workspace() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Workspace::update() {
|
void Workspace::update() {
|
||||||
if (!needs_updating_) {
|
const auto style_context = button_.get_style_context();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// update style and visibility
|
// update style and visibility
|
||||||
|
|
||||||
const auto style_context = button_.get_style_context();
|
if (!has_state(EXT_WORKSPACE_HANDLE_V1_STATE_ACTIVE)) {
|
||||||
style_context->remove_class("active");
|
style_context->remove_class("active");
|
||||||
style_context->remove_class("urgent");
|
}
|
||||||
style_context->remove_class("hidden");
|
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)) {
|
if (has_state(EXT_WORKSPACE_HANDLE_V1_STATE_ACTIVE)) {
|
||||||
button_.set_visible(true);
|
button_.set_visible(true);
|
||||||
@ -408,34 +411,26 @@ void Workspace::update() {
|
|||||||
label_.set_markup(fmt::format(fmt::runtime(format_), fmt::arg("name", name_),
|
label_.set_markup(fmt::format(fmt::runtime(format_), fmt::arg("name", name_),
|
||||||
fmt::arg("id", workspace_id_),
|
fmt::arg("id", workspace_id_),
|
||||||
fmt::arg("icon", with_icon_ ? icon() : "")));
|
fmt::arg("icon", with_icon_ ? icon() : "")));
|
||||||
|
|
||||||
needs_updating_ = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Workspace::handle_id(const std::string &id) {
|
void Workspace::handle_id(const std::string &id) {
|
||||||
spdlog::debug("[ext/workspaces]: ID for workspace {}: {}", id_, id);
|
spdlog::debug("[ext/workspaces]: ID for workspace {}: {}", id_, id);
|
||||||
workspace_id_ = id;
|
workspace_id_ = id;
|
||||||
needs_updating_ = true;
|
|
||||||
workspace_manager_.set_needs_sorting();
|
workspace_manager_.set_needs_sorting();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Workspace::handle_name(const std::string &name) {
|
void Workspace::handle_name(const std::string &name) {
|
||||||
spdlog::debug("[ext/workspaces]: Name for workspace {}: {}", id_, name);
|
spdlog::debug("[ext/workspaces]: Name for workspace {}: {}", id_, name);
|
||||||
name_ = name;
|
name_ = name;
|
||||||
needs_updating_ = true;
|
|
||||||
workspace_manager_.set_needs_sorting();
|
workspace_manager_.set_needs_sorting();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Workspace::handle_coordinates(const std::vector<uint32_t> &coordinates) {
|
void Workspace::handle_coordinates(const std::vector<uint32_t> &coordinates) {
|
||||||
coordinates_ = coordinates;
|
coordinates_ = coordinates;
|
||||||
needs_updating_ = true;
|
|
||||||
workspace_manager_.set_needs_sorting();
|
workspace_manager_.set_needs_sorting();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Workspace::handle_state(uint32_t state) {
|
void Workspace::handle_state(uint32_t state) { state_ = state; }
|
||||||
state_ = state;
|
|
||||||
needs_updating_ = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Workspace::handle_capabilities(uint32_t capabilities) {
|
void Workspace::handle_capabilities(uint32_t capabilities) {
|
||||||
spdlog::debug("[ext/workspaces]: Capabilities for workspace {}:", id_);
|
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) {
|
if ((capabilities & EXT_WORKSPACE_HANDLE_V1_WORKSPACE_CAPABILITIES_ASSIGN) == capabilities) {
|
||||||
spdlog::debug("[ext/workspaces]: - assign");
|
spdlog::debug("[ext/workspaces]: - assign");
|
||||||
}
|
}
|
||||||
needs_updating_ = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Workspace::handle_removed() {
|
void Workspace::handle_removed() {
|
||||||
@ -475,6 +469,8 @@ bool Workspace::handle_clicked(const GdkEventButton *button) const {
|
|||||||
|
|
||||||
if (action == "activate") {
|
if (action == "activate") {
|
||||||
ext_workspace_handle_v1_activate(ext_handle_);
|
ext_workspace_handle_v1_activate(ext_handle_);
|
||||||
|
} else if (action == "deactivate") {
|
||||||
|
ext_workspace_handle_v1_deactivate(ext_handle_);
|
||||||
} else if (action == "close") {
|
} else if (action == "close") {
|
||||||
ext_workspace_handle_v1_remove(ext_handle_);
|
ext_workspace_handle_v1_remove(ext_handle_);
|
||||||
} else {
|
} else {
|
||||||
|
@ -53,7 +53,6 @@ Gamemode::Gamemode(const std::string& id, const Json::Value& config)
|
|||||||
if (config_["icon-spacing"].isUInt()) {
|
if (config_["icon-spacing"].isUInt()) {
|
||||||
iconSpacing = config_["icon-spacing"].asUInt();
|
iconSpacing = config_["icon-spacing"].asUInt();
|
||||||
}
|
}
|
||||||
box_.set_spacing(iconSpacing);
|
|
||||||
|
|
||||||
// Whether to use icon or not
|
// Whether to use icon or not
|
||||||
if (config_["use-icon"].isBool()) {
|
if (config_["use-icon"].isBool()) {
|
||||||
@ -64,7 +63,6 @@ Gamemode::Gamemode(const std::string& id, const Json::Value& config)
|
|||||||
if (config_["icon-size"].isUInt()) {
|
if (config_["icon-size"].isUInt()) {
|
||||||
iconSize = config_["icon-size"].asUInt();
|
iconSize = config_["icon-size"].asUInt();
|
||||||
}
|
}
|
||||||
icon_.set_pixel_size(iconSize);
|
|
||||||
|
|
||||||
// Format
|
// Format
|
||||||
if (config_["format"].isString()) {
|
if (config_["format"].isString()) {
|
||||||
@ -228,6 +226,11 @@ auto Gamemode::update() -> void {
|
|||||||
iconName = DEFAULT_ICON_NAME;
|
iconName = DEFAULT_ICON_NAME;
|
||||||
}
|
}
|
||||||
icon_.set_from_icon_name(iconName, Gtk::ICON_SIZE_INVALID);
|
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
|
// Call parent update
|
||||||
|
@ -46,9 +46,14 @@ std::filesystem::path IPC::getSocketFolder(const char* instanceSig) {
|
|||||||
IPC::IPC() {
|
IPC::IPC() {
|
||||||
// will start IPC and relay events to parseIPC
|
// will start IPC and relay events to parseIPC
|
||||||
ipcThread_ = std::thread([this]() { socketListener(); });
|
ipcThread_ = std::thread([this]() { socketListener(); });
|
||||||
|
socketOwnerPid_ = getpid();
|
||||||
}
|
}
|
||||||
|
|
||||||
IPC::~IPC() {
|
IPC::~IPC() {
|
||||||
|
// Do no stop Hyprland IPC if a child process (with successful fork() but
|
||||||
|
// failed exec()) exits.
|
||||||
|
if (getpid() != socketOwnerPid_) return;
|
||||||
|
|
||||||
running_ = false;
|
running_ = false;
|
||||||
spdlog::info("Hyprland IPC stopping...");
|
spdlog::info("Hyprland IPC stopping...");
|
||||||
if (socketfd_ != -1) {
|
if (socketfd_ != -1) {
|
||||||
|
@ -104,8 +104,25 @@ void Workspace::initializeWindowMap(const Json::Value &clients_data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Workspace::setActiveWindow(WindowAddress const &addr) {
|
void Workspace::setActiveWindow(WindowAddress const &addr) {
|
||||||
for (auto &window : m_windowMap) {
|
std::optional<long> activeIdx;
|
||||||
window.setActive(window.address == addr);
|
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) {
|
void Workspace::updateTaskbar(const std::string &workspace_icon) {
|
||||||
for (auto child : m_content.get_children()) {
|
for (auto child : m_content.get_children()) {
|
||||||
if (child != &m_labelBefore) {
|
if (child != &m_labelBefore) {
|
||||||
@ -259,9 +287,9 @@ void Workspace::updateTaskbar(const std::string &workspace_icon) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool isFirst = true;
|
bool isFirst = true;
|
||||||
for (const auto &window_repr : m_windowMap) {
|
auto processWindow = [&](const WindowRepr &window_repr) {
|
||||||
if (shouldSkipWindow(window_repr)) {
|
if (shouldSkipWindow(window_repr)) {
|
||||||
continue;
|
return; // skip
|
||||||
}
|
}
|
||||||
if (isFirst) {
|
if (isFirst) {
|
||||||
isFirst = false;
|
isFirst = false;
|
||||||
@ -270,6 +298,7 @@ void Workspace::updateTaskbar(const std::string &workspace_icon) {
|
|||||||
m_content.pack_start(*windowSeparator, false, false);
|
m_content.pack_start(*windowSeparator, false, false);
|
||||||
windowSeparator->show();
|
windowSeparator->show();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto window_box = Gtk::make_managed<Gtk::Box>(Gtk::ORIENTATION_HORIZONTAL);
|
auto window_box = Gtk::make_managed<Gtk::Box>(Gtk::ORIENTATION_HORIZONTAL);
|
||||||
window_box->set_tooltip_text(window_repr.window_title);
|
window_box->set_tooltip_text(window_repr.window_title);
|
||||||
window_box->get_style_context()->add_class("taskbar-window");
|
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);
|
m_content.pack_start(*event_box, true, false);
|
||||||
event_box->show_all();
|
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();
|
auto formatAfter = m_workspaceManager.formatAfter();
|
||||||
|
@ -727,6 +727,7 @@ auto Workspaces::populateWorkspaceTaskbarConfig(const Json::Value &config) -> vo
|
|||||||
|
|
||||||
populateBoolConfig(workspaceTaskbar, "enable", m_enableTaskbar);
|
populateBoolConfig(workspaceTaskbar, "enable", m_enableTaskbar);
|
||||||
populateBoolConfig(workspaceTaskbar, "update-active-window", m_updateActiveWindow);
|
populateBoolConfig(workspaceTaskbar, "update-active-window", m_updateActiveWindow);
|
||||||
|
populateBoolConfig(workspaceTaskbar, "reverse-direction", m_taskbarReverseDirection);
|
||||||
|
|
||||||
if (workspaceTaskbar["format"].isString()) {
|
if (workspaceTaskbar["format"].isString()) {
|
||||||
/* The user defined a format string, use it */
|
/* 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) {
|
void Workspaces::registerOrphanWindow(WindowCreationPayload create_window_payload) {
|
||||||
@ -1128,7 +1141,7 @@ std::optional<int> Workspaces::parseWorkspaceId(std::string const &workspaceIdSt
|
|||||||
try {
|
try {
|
||||||
return workspaceIdStr == "special" ? -99 : std::stoi(workspaceIdStr);
|
return workspaceIdStr == "special" ? -99 : std::stoi(workspaceIdStr);
|
||||||
} catch (std::exception const &e) {
|
} 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;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,14 +14,20 @@ waybar::modules::Image::Image(const std::string& id, const Json::Value& config)
|
|||||||
|
|
||||||
size_ = config["size"].asInt();
|
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) {
|
if (size_ == 0) {
|
||||||
size_ = 16;
|
size_ = 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (interval_ == 0) {
|
if (interval_.count() == 0) {
|
||||||
interval_ = INT_MAX;
|
interval_ = std::chrono::milliseconds::max();
|
||||||
}
|
}
|
||||||
|
|
||||||
delayWorker();
|
delayWorker();
|
||||||
@ -30,8 +36,7 @@ waybar::modules::Image::Image(const std::string& id, const Json::Value& config)
|
|||||||
void waybar::modules::Image::delayWorker() {
|
void waybar::modules::Image::delayWorker() {
|
||||||
thread_ = [this] {
|
thread_ = [this] {
|
||||||
dp.emit();
|
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("ipaddr", final_ipaddr_), fmt::arg("gwaddr", gwaddr_), fmt::arg("cidr", cidr_),
|
||||||
fmt::arg("cidr6", cidr6_), fmt::arg("frequency", fmt::format("{:.1f}", frequency_)),
|
fmt::arg("cidr6", cidr6_), fmt::arg("frequency", fmt::format("{:.1f}", frequency_)),
|
||||||
fmt::arg("icon", getIcon(signal_strength_, state_)),
|
fmt::arg("icon", getIcon(signal_strength_, state_)),
|
||||||
fmt::arg("bandwidthDownBits", pow_format(bandwidth_down * 8ull / interval_.count(), "b/s")),
|
fmt::arg("bandwidthDownBits",
|
||||||
fmt::arg("bandwidthUpBits", pow_format(bandwidth_up * 8ull / interval_.count(), "b/s")),
|
pow_format(bandwidth_down * 8ull / (interval_.count() / 1000.0), "b/s")),
|
||||||
fmt::arg("bandwidthTotalBits",
|
fmt::arg("bandwidthUpBits",
|
||||||
pow_format((bandwidth_up + bandwidth_down) * 8ull / interval_.count(), "b/s")),
|
pow_format(bandwidth_up * 8ull / (interval_.count() / 1000.0), "b/s")),
|
||||||
fmt::arg("bandwidthDownOctets", pow_format(bandwidth_down / interval_.count(), "o/s")),
|
fmt::arg(
|
||||||
fmt::arg("bandwidthUpOctets", pow_format(bandwidth_up / interval_.count(), "o/s")),
|
"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",
|
fmt::arg("bandwidthTotalOctets",
|
||||||
pow_format((bandwidth_up + bandwidth_down) / interval_.count(), "o/s")),
|
pow_format((bandwidth_up + bandwidth_down) / (interval_.count() / 1000.0), "o/s")),
|
||||||
fmt::arg("bandwidthDownBytes", pow_format(bandwidth_down / interval_.count(), "B/s")),
|
fmt::arg("bandwidthDownBytes",
|
||||||
fmt::arg("bandwidthUpBytes", pow_format(bandwidth_up / interval_.count(), "B/s")),
|
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",
|
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) {
|
if (text.compare(label_.get_label()) != 0) {
|
||||||
label_.set_markup(text);
|
label_.set_markup(text);
|
||||||
if (text.empty()) {
|
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 short description {}", layout.short_description);
|
||||||
spdlog::debug("niri language update with variant {}", layout.variant);
|
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{};
|
std::string layoutName = std::string{};
|
||||||
if (config_.isMember("format-" + layout.short_description + "-" + layout.variant)) {
|
if (config_.isMember("format-" + layout.short_description + "-" + layout.variant)) {
|
||||||
const auto propName = "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();
|
button.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
struct wl_output *output = gdk_wayland_monitor_get_wl_output(bar_.output->monitor->gobj());
|
box_.signal_show().connect(sigc::mem_fun(*this, &Tags::handle_show));
|
||||||
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_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Tags::~Tags() {
|
Tags::~Tags() {
|
||||||
@ -165,6 +161,19 @@ Tags::~Tags() {
|
|||||||
if (control_) {
|
if (control_) {
|
||||||
zriver_control_v1_destroy(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) {
|
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);
|
cancellable_, interface);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Item::~Item() {
|
||||||
|
if (this->gtk_menu != nullptr) {
|
||||||
|
this->gtk_menu->popdown();
|
||||||
|
this->gtk_menu->detach();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool Item::handleMouseEnter(GdkEventCrossing* const& e) {
|
bool Item::handleMouseEnter(GdkEventCrossing* const& e) {
|
||||||
event_box.set_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT);
|
event_box.set_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT);
|
||||||
return false;
|
return false;
|
||||||
@ -443,6 +450,9 @@ void Item::makeMenu() {
|
|||||||
gtk_menu->attach_to_widget(event_box);
|
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) {
|
bool Item::handleClick(GdkEventButton* const& ev) {
|
||||||
|
@ -74,12 +74,14 @@ auto waybar::modules::Temperature::update() -> void {
|
|||||||
if (critical) {
|
if (critical) {
|
||||||
format = config_["format-critical"].isString() ? config_["format-critical"].asString() : format;
|
format = config_["format-critical"].isString() ? config_["format-critical"].asString() : format;
|
||||||
label_.get_style_context()->add_class("critical");
|
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 {
|
} else {
|
||||||
label_.get_style_context()->remove_class("critical");
|
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()) {
|
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
|
// Explicit instantiations for specific EnumType types you intend to use
|
||||||
// Add explicit instantiations for all relevant EnumType types
|
// Add explicit instantiations for all relevant EnumType types
|
||||||
template struct EnumParser<modules::hyprland::Workspaces::SortMethod>;
|
template struct EnumParser<modules::hyprland::Workspaces::SortMethod>;
|
||||||
|
template struct EnumParser<modules::hyprland::Workspaces::ActiveWindowPosition>;
|
||||||
template struct EnumParser<util::KillSignalAction>;
|
template struct EnumParser<util::KillSignalAction>;
|
||||||
|
|
||||||
} // namespace waybar::util
|
} // namespace waybar::util
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
[wrap-file]
|
[wrap-file]
|
||||||
directory = spdlog-1.14.1
|
directory = spdlog-1.15.2
|
||||||
source_url = https://github.com/gabime/spdlog/archive/refs/tags/v1.14.1.tar.gz
|
source_url = https://github.com/gabime/spdlog/archive/refs/tags/v1.15.2.tar.gz
|
||||||
source_filename = spdlog-1.14.1.tar.gz
|
source_filename = spdlog-1.15.2.tar.gz
|
||||||
source_hash = 1586508029a7d0670dfcb2d97575dcdc242d3868a259742b69f100801ab4e16b
|
source_hash = 7a80896357f3e8e920e85e92633b14ba0f229c506e6f978578bdc35ba09e9a5d
|
||||||
patch_filename = spdlog_1.14.1-1_patch.zip
|
patch_filename = spdlog_1.15.2-3_patch.zip
|
||||||
patch_url = https://wrapdb.mesonbuild.com/v2/spdlog_1.14.1-1/get_patch
|
patch_url = https://wrapdb.mesonbuild.com/v2/spdlog_1.15.2-3/get_patch
|
||||||
patch_hash = ae878e732330ea1048f90d7e117c40c0cd2a6fb8ae5492c7955818ce3aaade6c
|
patch_hash = d5ab078661f571ef5113a8e4bc5c4121e16c044e7772a24b44b1ca8f3ee7c6cb
|
||||||
source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/spdlog_1.14.1-1/spdlog-1.14.1.tar.gz
|
source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/spdlog_1.15.2-3/spdlog-1.15.2.tar.gz
|
||||||
wrapdb_version = 1.14.1-1
|
wrapdb_version = 1.15.2-3
|
||||||
|
|
||||||
[provide]
|
[provide]
|
||||||
spdlog = spdlog_dep
|
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]") {
|
TEST_CASE("Load multiple bar config with include", "[config]") {
|
||||||
waybar::Config conf;
|
waybar::Config conf;
|
||||||
conf.load("test/config/include-multi.json");
|
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