Merge branch 'master' into niri-language-hide
This commit is contained in:
@@ -10,13 +10,9 @@ jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
# TODO: bump to clang 19 release
|
||||
# - uses: DoozyX/clang-format-lint-action@v0.18.2
|
||||
- uses: DoozyX/clang-format-lint-action@558090054b3f39e3d6af24f0cd73b319535da809
|
||||
- uses: actions/checkout@v6
|
||||
- uses: RafikFarhad/clang-format-github-action@v6
|
||||
name: clang-format
|
||||
with:
|
||||
source: "."
|
||||
extensions: "hpp,h,cpp,c"
|
||||
style: "file:.clang-format"
|
||||
clangFormatVersion: 19
|
||||
sources: "src/**/*.hpp,src/**/*.cpp"
|
||||
style: "file"
|
||||
|
||||
@@ -12,7 +12,7 @@ jobs:
|
||||
container:
|
||||
image: alexays/waybar:debian
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v6
|
||||
- name: configure
|
||||
run: |
|
||||
meson -Dcpp_std=c++20 build # necessary to generate compile_commands.json
|
||||
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
|
||||
@@ -12,7 +12,7 @@ jobs:
|
||||
# https://github.com/actions/runner/issues/385 - for FreeBSD runner support
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v6
|
||||
- name: Test in FreeBSD VM
|
||||
uses: cross-platform-actions/action@v0.28.0
|
||||
timeout-minutes: 180
|
||||
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
image: alexays/waybar:${{ matrix.distro }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v6
|
||||
- name: configure
|
||||
run: meson setup -Dman-pages=enabled -Dcpp_std=${{matrix.cpp_std}} build
|
||||
- name: build
|
||||
|
||||
@@ -2,12 +2,15 @@ name: "Nix-Tests"
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-nix-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
jobs:
|
||||
nix-flake-check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: cachix/install-nix-action@v27
|
||||
- uses: actions/checkout@v6
|
||||
- uses: cachix/install-nix-action@v31
|
||||
with:
|
||||
extra_nix_config: |
|
||||
experimental-features = nix-command flakes
|
||||
|
||||
@@ -12,11 +12,11 @@ jobs:
|
||||
if: github.event_name != 'schedule' || github.repository == 'Alexays/Waybar'
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- name: Install Nix
|
||||
uses: cachix/install-nix-action@v27
|
||||
uses: cachix/install-nix-action@v31
|
||||
with:
|
||||
extra_nix_config: |
|
||||
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Update flake.lock
|
||||
uses: DeterminateSystems/update-flake-lock@v21
|
||||
uses: DeterminateSystems/update-flake-lock@v28
|
||||
|
||||
@@ -7,6 +7,7 @@ vgcore.*
|
||||
*.swp
|
||||
packagecache
|
||||
/subprojects/**/
|
||||
/subprojects/.wraplock
|
||||
/build*
|
||||
/dist
|
||||
/meson.egg-info
|
||||
@@ -50,3 +51,5 @@ result
|
||||
result-*
|
||||
|
||||
.ccls-cache
|
||||
_codeql_detected_source_root
|
||||
heaptrack*
|
||||
|
||||
@@ -119,7 +119,7 @@ sudo apt install \
|
||||
On Arch, you can use this command:
|
||||
|
||||
```
|
||||
pacman -S \
|
||||
pacman -S --asdeps \
|
||||
gtkmm3 \
|
||||
jsoncpp \
|
||||
libsigc++ \
|
||||
@@ -151,6 +151,10 @@ Contributions welcome!<br>
|
||||
Have fun :)<br>
|
||||
The style guidelines are [Google's](https://google.github.io/styleguide/cppguide.html)
|
||||
|
||||
> [!CAUTION]
|
||||
> Distributions of Waybar are only released on the [official GitHub page](https://github.com/Alexays/Waybar).<br/>
|
||||
> Waybar does **not** have an official website. Do not trust any sites that claim to be official.
|
||||
|
||||
## License
|
||||
|
||||
Waybar is licensed under the MIT license. [See LICENSE for more information](https://github.com/Alexays/Waybar/blob/master/LICENSE).
|
||||
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
.
|
||||
@@ -0,0 +1,5 @@
|
||||
# Suppress common address sanitizer issues in dependencies, these are often non-fixable or not an issue.
|
||||
# Use it like this (when in repo root): ASAN_OPTIONS="suppressions=./asan.supp" ./build/waybar
|
||||
leak:libpangoft2-1.0.so.0
|
||||
leak:libgtk-3.so.0
|
||||
leak:libfontconfig.so.1
|
||||
Generated
+6
-6
@@ -3,11 +3,11 @@
|
||||
"flake-compat": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1747046372,
|
||||
"narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=",
|
||||
"lastModified": 1767039857,
|
||||
"narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=",
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885",
|
||||
"rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -18,11 +18,11 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1759036355,
|
||||
"narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=",
|
||||
"lastModified": 1769461804,
|
||||
"narHash": "sha256-msG8SU5WsBUfVVa/9RPLaymvi5bI8edTavbIq3vRlhI=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127",
|
||||
"rev": "bfc1b8a4574108ceef22f02bafcf6611380c100d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
+8
-1
@@ -25,6 +25,10 @@ class AModule : public IModule {
|
||||
|
||||
bool expandEnabled() const;
|
||||
|
||||
virtual void suspend() {};
|
||||
virtual void resume() {};
|
||||
bool shouldSuspend() const { return disable_on_sleep_; }
|
||||
|
||||
protected:
|
||||
// Don't need to make an object directly
|
||||
// Derived classes are able to use it
|
||||
@@ -48,7 +52,9 @@ class AModule : public IModule {
|
||||
virtual bool handleMouseLeave(GdkEventCrossing* const& ev);
|
||||
virtual bool handleScroll(GdkEventScroll*);
|
||||
virtual bool handleRelease(GdkEventButton* const& ev);
|
||||
GObject *menu_;
|
||||
|
||||
bool disable_on_sleep_{false};
|
||||
GObject* menu_ = nullptr;
|
||||
|
||||
private:
|
||||
bool handleUserEvent(GdkEventButton* const& ev);
|
||||
@@ -57,6 +63,7 @@ class AModule : public IModule {
|
||||
bool hasUserEvents_;
|
||||
gdouble distance_scrolled_y_;
|
||||
gdouble distance_scrolled_x_;
|
||||
sigc::connection cursor_timeout_conn_;
|
||||
std::map<std::string, std::string> eventActionMap_;
|
||||
static const inline std::map<std::pair<uint, GdkEventType>, std::string> eventMap_{
|
||||
{std::make_pair(1, GdkEventType::GDK_BUTTON_PRESS), "on-click"},
|
||||
|
||||
@@ -75,6 +75,8 @@ class Bar : public sigc::trackable {
|
||||
util::KillSignalAction getOnSigusr1Action();
|
||||
util::KillSignalAction getOnSigusr2Action();
|
||||
|
||||
void toggleSuspend(bool suspend);
|
||||
|
||||
struct waybar_output* output;
|
||||
Json::Value config;
|
||||
struct wl_surface* surface;
|
||||
|
||||
@@ -44,6 +44,7 @@ class Client {
|
||||
const char* interface, uint32_t version);
|
||||
static void handleGlobalRemove(void* data, struct wl_registry* registry, uint32_t name);
|
||||
static void handleOutputDone(void*, struct zxdg_output_v1*);
|
||||
void createBarsBatch();
|
||||
static void handleOutputName(void*, struct zxdg_output_v1*, const char*);
|
||||
static void handleOutputDescription(void*, struct zxdg_output_v1*, const char*);
|
||||
void handleMonitorAdded(Glib::RefPtr<Gdk::Monitor> monitor);
|
||||
@@ -56,6 +57,10 @@ class Client {
|
||||
std::list<struct waybar_output> outputs_;
|
||||
std::unique_ptr<CssReloadHelper> m_cssReloadHelper;
|
||||
std::string m_cssFile;
|
||||
sigc::connection monitor_added_connection_;
|
||||
sigc::connection monitor_removed_connection_;
|
||||
std::list<waybar_output*> pending_outputs_;
|
||||
bool bars_scheduled_ = false;
|
||||
};
|
||||
|
||||
} // namespace waybar
|
||||
|
||||
@@ -30,6 +30,7 @@ class Group : public AModule {
|
||||
bool handleMouseEnter(GdkEventCrossing* const& ev) override;
|
||||
bool handleMouseLeave(GdkEventCrossing* const& ev) override;
|
||||
bool handleToggle(GdkEventButton* const& ev) override;
|
||||
bool handleScroll(GdkEventScroll* e) override;
|
||||
void show_group();
|
||||
void hide_group();
|
||||
};
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#if defined(__linux__)
|
||||
#include <sys/inotify.h>
|
||||
#endif
|
||||
#include <poll.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
@@ -15,6 +16,7 @@
|
||||
#include "ALabel.hpp"
|
||||
#include "bar.hpp"
|
||||
#include "util/sleeper_thread.hpp"
|
||||
#include "util/udev_deleter.hpp"
|
||||
|
||||
namespace waybar::modules {
|
||||
|
||||
@@ -37,15 +39,17 @@ class Battery : public ALabel {
|
||||
void setBarClass(std::string&);
|
||||
void processEvents(std::string& state, std::string& status, uint8_t capacity);
|
||||
|
||||
int global_watch;
|
||||
std::map<fs::path, int> batteries_;
|
||||
std::unique_ptr<udev, util::UdevDeleter> udev_;
|
||||
std::array<pollfd, 1> poll_fds_;
|
||||
std::unique_ptr<udev_monitor, util::UdevMonitorDeleter> mon_;
|
||||
fs::path adapter_;
|
||||
int battery_watch_fd_;
|
||||
int global_watch_fd_;
|
||||
std::mutex battery_list_mutex_;
|
||||
std::string old_status_;
|
||||
std::string last_event_;
|
||||
bool warnFirstTime_{true};
|
||||
bool weightedAverage_{true};
|
||||
const Bar& bar_;
|
||||
|
||||
util::SleeperThread thread_;
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include <epoxy/gl.h>
|
||||
|
||||
#include "AModule.hpp"
|
||||
#include "cava_backend.hpp"
|
||||
|
||||
namespace waybar::modules::cava {
|
||||
|
||||
class CavaGLSL final : public AModule, public Gtk::GLArea {
|
||||
public:
|
||||
CavaGLSL(const std::string&, const Json::Value&);
|
||||
~CavaGLSL() = default;
|
||||
|
||||
private:
|
||||
std::shared_ptr<CavaBackend> backend_;
|
||||
struct ::cava::config_params prm_;
|
||||
int frame_counter{0};
|
||||
bool silence_{false};
|
||||
bool hide_on_silence_{false};
|
||||
// Cava method
|
||||
auto onUpdate(const ::cava::audio_raw& input) -> void;
|
||||
auto onSilence() -> void;
|
||||
// Member variable to store the shared pointer
|
||||
std::shared_ptr<::cava::audio_raw> m_data_;
|
||||
GLuint shaderProgram_;
|
||||
// OpenGL variables
|
||||
GLuint fbo_;
|
||||
GLuint texture_;
|
||||
GLint uniform_bars_;
|
||||
GLint uniform_previous_bars_;
|
||||
GLint uniform_bars_count_;
|
||||
GLint uniform_time_;
|
||||
// Methods
|
||||
void onRealize();
|
||||
bool onRender(const Glib::RefPtr<Gdk::GLContext>& context);
|
||||
|
||||
void initShaders();
|
||||
void initSurface();
|
||||
void initGLSL();
|
||||
GLuint loadShader(const std::string& fileName, GLenum type);
|
||||
};
|
||||
} // namespace waybar::modules::cava
|
||||
@@ -9,20 +9,20 @@ 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_{""};
|
||||
Glib::ustring label_text_{""};
|
||||
bool silence_{false};
|
||||
bool hide_on_silence_{false};
|
||||
std::string format_silent_{""};
|
||||
int ascii_range_{0};
|
||||
bool silence_{false};
|
||||
// Cava method
|
||||
void pause_resume();
|
||||
auto onUpdate(const std::string& input) -> void;
|
||||
auto onSilence() -> void;
|
||||
// ModuleActionMap
|
||||
static inline std::map<const std::string, void (waybar::modules::cava::Cava::* const)()>
|
||||
actionMap_{{"mode", &waybar::modules::cava::Cava::pause_resume}};
|
||||
@@ -32,29 +32,36 @@ class CavaBackend final {
|
||||
int getAsciiRange();
|
||||
void doPauseResume();
|
||||
void Update();
|
||||
const struct ::cava::config_params* getPrm();
|
||||
std::chrono::milliseconds getFrameTimeMilsec();
|
||||
|
||||
// Signal accessor
|
||||
using type_signal_update = sigc::signal<void(const std::string&)>;
|
||||
type_signal_update signal_update();
|
||||
using type_signal_audio_raw_update = sigc::signal<void(const ::cava::audio_raw&)>;
|
||||
type_signal_audio_raw_update signal_audio_raw_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_;
|
||||
util::SleeperThread out_thread_;
|
||||
|
||||
// Cava API to read audio source
|
||||
::cava::ptr input_source_;
|
||||
::cava::ptr input_source_{NULL};
|
||||
|
||||
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{}};
|
||||
struct ::cava::cava_plan* plan_{NULL}; //{new cava_plan{}};
|
||||
|
||||
std::chrono::seconds fetch_input_delay_{4};
|
||||
// Delay to handle audio source
|
||||
std::chrono::milliseconds frame_time_milsec_{1s};
|
||||
|
||||
const Json::Value& config_;
|
||||
int re_paint_{0};
|
||||
bool silence_{false};
|
||||
bool silence_prev_{false};
|
||||
@@ -66,9 +73,12 @@ class CavaBackend final {
|
||||
void execute();
|
||||
bool isSilence();
|
||||
void doUpdate(bool force = false);
|
||||
void loadConfig();
|
||||
void freeBackend();
|
||||
|
||||
// Signal
|
||||
type_signal_update m_signal_update_;
|
||||
type_signal_audio_raw_update m_signal_audio_raw_;
|
||||
type_signal_silence m_signal_silence_;
|
||||
};
|
||||
} // namespace waybar::modules::cava
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef HAVE_LIBCAVA
|
||||
#include "cavaRaw.hpp"
|
||||
#include "cava_backend.hpp"
|
||||
#ifdef HAVE_LIBCAVAGLSL
|
||||
#include "cavaGLSL.hpp"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace waybar::modules::cava {
|
||||
AModule* getModule(const std::string& id, const Json::Value& config) {
|
||||
#ifdef HAVE_LIBCAVA
|
||||
const std::shared_ptr<CavaBackend> backend_{waybar::modules::cava::CavaBackend::inst(config)};
|
||||
switch (backend_->getPrm()->output) {
|
||||
#ifdef HAVE_LIBCAVAGLSL
|
||||
case ::cava::output_method::OUTPUT_SDL_GLSL:
|
||||
return new waybar::modules::cava::CavaGLSL(id, config);
|
||||
#endif
|
||||
default:
|
||||
return new waybar::modules::cava::Cava(id, config);
|
||||
}
|
||||
#else
|
||||
throw std::runtime_error("Unknown module");
|
||||
#endif
|
||||
};
|
||||
} // namespace waybar::modules::cava
|
||||
@@ -35,6 +35,7 @@ class Custom : public ALabel {
|
||||
std::string id_;
|
||||
std::string alt_;
|
||||
std::string tooltip_;
|
||||
std::string last_tooltip_markup_;
|
||||
const bool tooltip_format_enabled_;
|
||||
std::vector<std::string> class_;
|
||||
int percentage_;
|
||||
|
||||
@@ -22,7 +22,7 @@ class Disk : public ALabel {
|
||||
std::string path_;
|
||||
std::string unit_;
|
||||
|
||||
float calc_specific_divisor(const std::string divisor);
|
||||
float calc_specific_divisor(const std::string& divisor);
|
||||
};
|
||||
|
||||
} // namespace waybar::modules
|
||||
|
||||
@@ -26,7 +26,7 @@ class Gamemode : public AModule {
|
||||
const std::string DEFAULT_FORMAT = "{glyph}";
|
||||
const std::string DEFAULT_FORMAT_ALT = "{glyph} {count}";
|
||||
const std::string DEFAULT_TOOLTIP_FORMAT = "Games running: {count}";
|
||||
const std::string DEFAULT_GLYPH = "";
|
||||
const std::string DEFAULT_GLYPH = "";
|
||||
|
||||
void appear(const Glib::RefPtr<Gio::DBus::Connection>& connection, const Glib::ustring& name,
|
||||
const Glib::ustring& name_owner);
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <filesystem>
|
||||
#include <list>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
@@ -17,9 +19,13 @@ class EventHandler {
|
||||
virtual ~EventHandler() = default;
|
||||
};
|
||||
|
||||
/// If you want to use the Hyprland IPC, simply use IPC::inst() to get the singleton instance.
|
||||
/// Do not create multiple instances.
|
||||
class IPC {
|
||||
protected:
|
||||
IPC(); // use IPC::inst() instead.
|
||||
|
||||
public:
|
||||
IPC();
|
||||
~IPC();
|
||||
static IPC& inst();
|
||||
|
||||
@@ -30,22 +36,33 @@ class IPC {
|
||||
Json::Value getSocket1JsonReply(const std::string& rq);
|
||||
static std::filesystem::path getSocketFolder(const char* instanceSig);
|
||||
|
||||
/// Dispatch a Hyprland command. Automatically uses the correct protocol
|
||||
/// (legacy text or Lua-based) depending on the running Hyprland version.
|
||||
static std::string dispatch(const std::string& dispatcher, const std::string& arg);
|
||||
|
||||
/// Build a Lua-format dispatch command string.
|
||||
static std::string buildLuaDispatch(const std::string& dispatcher, const std::string& arg);
|
||||
|
||||
protected:
|
||||
static std::filesystem::path socketFolder_;
|
||||
|
||||
/// Detect whether the running Hyprland uses the Lua-based IPC protocol.
|
||||
/// Returns true for Hyprland >= 0.54 (Lua config), false for older versions.
|
||||
static bool isLuaProtocol();
|
||||
|
||||
static std::optional<bool> s_luaProtocolDetected_; // cached detection result
|
||||
|
||||
private:
|
||||
void socketListener();
|
||||
void parseIPC(const std::string&);
|
||||
|
||||
std::thread ipcThread_;
|
||||
std::mutex callbackMutex_;
|
||||
std::mutex socketMutex_;
|
||||
util::JsonParser parser_;
|
||||
std::list<std::pair<std::string, EventHandler*>> callbacks_;
|
||||
int socketfd_; // the hyprland socket file descriptor
|
||||
pid_t socketOwnerPid_;
|
||||
bool running_ = true;
|
||||
int socketfd_ = -1; // the hyprland socket file descriptor
|
||||
pid_t socketOwnerPid_ = -1;
|
||||
std::atomic<bool> running_ = true; // the ipcThread will stop running when this is false
|
||||
};
|
||||
|
||||
inline bool modulesReady = false;
|
||||
inline std::unique_ptr<IPC> gIPC;
|
||||
}; // namespace waybar::modules::hyprland
|
||||
|
||||
@@ -26,6 +26,7 @@ class Submap : public waybar::ALabel, public EventHandler {
|
||||
const Bar& bar_;
|
||||
util::JsonParser parser_;
|
||||
std::string submap_;
|
||||
std::string prev_submap_;
|
||||
bool always_on_ = false;
|
||||
std::string default_submap_ = "Default";
|
||||
|
||||
|
||||
@@ -20,8 +20,8 @@ class Window : public waybar::AAppIconLabel, public EventHandler {
|
||||
|
||||
private:
|
||||
struct Workspace {
|
||||
int id;
|
||||
int windows;
|
||||
int id = 0;
|
||||
int windows = 0;
|
||||
std::string last_window;
|
||||
std::string last_window_title;
|
||||
|
||||
@@ -29,14 +29,14 @@ class Window : public waybar::AAppIconLabel, public EventHandler {
|
||||
};
|
||||
|
||||
struct WindowData {
|
||||
bool floating;
|
||||
bool floating = false;
|
||||
int monitor = -1;
|
||||
std::string class_name;
|
||||
std::string initial_class_name;
|
||||
std::string title;
|
||||
std::string initial_title;
|
||||
bool fullscreen;
|
||||
bool grouped;
|
||||
bool fullscreen = false;
|
||||
bool grouped = false;
|
||||
|
||||
static auto parse(const Json::Value&) -> WindowData;
|
||||
};
|
||||
@@ -47,7 +47,7 @@ class Window : public waybar::AAppIconLabel, public EventHandler {
|
||||
void queryActiveWorkspace();
|
||||
void setClass(const std::string&, bool enable);
|
||||
|
||||
bool separateOutputs_;
|
||||
bool separateOutputs_ = false;
|
||||
std::mutex mutex_;
|
||||
const Bar& bar_;
|
||||
util::JsonParser parser_;
|
||||
@@ -55,11 +55,11 @@ class Window : public waybar::AAppIconLabel, public EventHandler {
|
||||
Workspace workspace_;
|
||||
std::string soloClass_;
|
||||
std::string lastSoloClass_;
|
||||
bool solo_;
|
||||
bool allFloating_;
|
||||
bool swallowing_;
|
||||
bool fullscreen_;
|
||||
bool focused_;
|
||||
bool solo_ = false;
|
||||
bool allFloating_ = false;
|
||||
bool swallowing_ = false;
|
||||
bool fullscreen_ = false;
|
||||
bool focused_ = false;
|
||||
|
||||
IPC& m_ipc;
|
||||
};
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include "AAppIconLabel.hpp"
|
||||
#include "bar.hpp"
|
||||
#include "modules/hyprland/backend.hpp"
|
||||
#include "util/json.hpp"
|
||||
|
||||
namespace waybar::modules::hyprland {
|
||||
|
||||
@@ -26,8 +25,8 @@ class WindowCount : public waybar::AAppIconLabel, public EventHandler {
|
||||
static auto parse(const Json::Value& value) -> Workspace;
|
||||
};
|
||||
|
||||
static auto getActiveWorkspace(const std::string&) -> Workspace;
|
||||
static auto getActiveWorkspace() -> Workspace;
|
||||
auto getActiveWorkspace(const std::string&) -> Workspace;
|
||||
auto getActiveWorkspace() -> Workspace;
|
||||
void onEvent(const std::string& ev) override;
|
||||
void queryActiveWorkspace();
|
||||
void setClass(const std::string&, bool enable);
|
||||
@@ -36,6 +35,7 @@ class WindowCount : public waybar::AAppIconLabel, public EventHandler {
|
||||
std::mutex mutex_;
|
||||
const Bar& bar_;
|
||||
Workspace workspace_;
|
||||
IPC& m_ipc;
|
||||
};
|
||||
|
||||
} // namespace waybar::modules::hyprland
|
||||
|
||||
@@ -40,10 +40,11 @@ struct WindowRepr {
|
||||
|
||||
class WindowCreationPayload {
|
||||
public:
|
||||
WindowCreationPayload(std::string workspace_name, WindowAddress window_address,
|
||||
WindowCreationPayload(const std::string& workspace_name, WindowAddress window_address,
|
||||
WindowRepr window_repr);
|
||||
WindowCreationPayload(std::string workspace_name, WindowAddress window_address,
|
||||
std::string window_class, std::string window_title, bool is_active);
|
||||
WindowCreationPayload(const std::string& workspace_name, WindowAddress window_address,
|
||||
const std::string& window_class, const std::string& window_title,
|
||||
bool is_active);
|
||||
WindowCreationPayload(Json::Value const& client_data);
|
||||
|
||||
int incrementTimeSpentUncreated();
|
||||
|
||||
@@ -30,6 +30,7 @@ class Workspace {
|
||||
public:
|
||||
explicit Workspace(const Json::Value& workspace_data, Workspaces& workspace_manager,
|
||||
const Json::Value& clients_data = Json::Value::nullRef);
|
||||
~Workspace();
|
||||
std::string& selectIcon(std::map<std::string, std::string>& icons_map);
|
||||
Gtk::Button& button() { return m_button; };
|
||||
|
||||
@@ -45,6 +46,15 @@ class Workspace {
|
||||
bool isUrgent() const { return m_isUrgent; };
|
||||
|
||||
bool handleClicked(GdkEventButton* bt) const;
|
||||
|
||||
bool handleEnter(GdkEventCrossing* event);
|
||||
bool handleLeave(GdkEventCrossing* event);
|
||||
|
||||
void startHoverCheck();
|
||||
void stopHoverCheck();
|
||||
bool syncHoverClass();
|
||||
bool pointerInsideButton();
|
||||
|
||||
void setActive(bool value = true) { m_isActive = value; };
|
||||
void setPersistentRule(bool value = true) { m_isPersistentRule = value; };
|
||||
void setPersistentConfig(bool value = true) { m_isPersistentConfig = value; };
|
||||
@@ -80,6 +90,8 @@ class Workspace {
|
||||
bool m_isUrgent = false;
|
||||
bool m_isVisible = false;
|
||||
|
||||
sigc::connection m_hoverCheckConnection;
|
||||
|
||||
std::vector<WindowRepr> m_windowMap;
|
||||
|
||||
Gtk::Button m_button;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <gtkmm/enums.h>
|
||||
#include <gtkmm/label.h>
|
||||
#include <json/value.h>
|
||||
#include <sigc++/connection.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
@@ -43,6 +44,7 @@ class Workspaces : public AModule, public EventHandler {
|
||||
auto moveToMonitor() const -> bool { return m_moveToMonitor; }
|
||||
auto enableTaskbar() const -> bool { return m_enableTaskbar; }
|
||||
auto taskbarWithIcon() const -> bool { return m_taskbarWithIcon; }
|
||||
auto barScroll() const -> bool { return m_barScroll; }
|
||||
|
||||
auto getBarOutput() const -> std::string { return m_bar.output->name; }
|
||||
auto formatBefore() const -> std::string { return m_formatBefore; }
|
||||
@@ -58,7 +60,7 @@ class Workspaces : public AModule, public EventHandler {
|
||||
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(const std::string& window_class, const std::string& window_title);
|
||||
std::string& getWindowSeparator() { return m_formatWindowSeparator; }
|
||||
bool isWorkspaceIgnored(std::string const& workspace_name);
|
||||
|
||||
@@ -122,6 +124,8 @@ class Workspaces : public AModule, public EventHandler {
|
||||
static std::pair<std::string, std::string> splitDoublePayload(std::string const& payload);
|
||||
static std::tuple<std::string, std::string, std::string> splitTriplePayload(
|
||||
std::string const& payload);
|
||||
// scroll events
|
||||
bool handleScroll(GdkEventScroll* e) override;
|
||||
|
||||
// Update methods
|
||||
void doUpdate();
|
||||
@@ -145,6 +149,7 @@ class Workspaces : public AModule, public EventHandler {
|
||||
bool m_specialVisibleOnly = false;
|
||||
bool m_persistentOnly = false;
|
||||
bool m_moveToMonitor = false;
|
||||
bool m_barScroll = false;
|
||||
Json::Value m_persistentWorkspaceConfig;
|
||||
|
||||
// Map for windows stored in workspaces not present in the current bar.
|
||||
@@ -204,6 +209,7 @@ class Workspaces : public AModule, public EventHandler {
|
||||
std::mutex m_mutex;
|
||||
const Bar& m_bar;
|
||||
Gtk::Box m_box;
|
||||
sigc::connection m_scrollEventConnection_;
|
||||
IPC& m_ipc;
|
||||
};
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <fmt/chrono.h>
|
||||
#include <gtkmm/label.h>
|
||||
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
|
||||
@@ -41,6 +42,7 @@ class KeyboardState : public AModule {
|
||||
|
||||
struct libinput* libinput_;
|
||||
std::unordered_map<std::string, struct libinput_device*> libinput_devices_;
|
||||
std::mutex devices_mutex_; // protects libinput_devices_
|
||||
std::set<int> binding_keys;
|
||||
|
||||
util::SleeperThread libinput_thread_, hotplug_thread_;
|
||||
|
||||
@@ -22,6 +22,8 @@ class Memory : public ALabel {
|
||||
std::unordered_map<std::string, unsigned long> meminfo_;
|
||||
|
||||
util::SleeperThread thread_;
|
||||
|
||||
std::string unit_;
|
||||
};
|
||||
|
||||
} // namespace waybar::modules
|
||||
|
||||
@@ -33,6 +33,7 @@ class MPD : public ALabel {
|
||||
detail::unique_status status_;
|
||||
mpd_state state_;
|
||||
detail::unique_song song_;
|
||||
std::string ellipsis_;
|
||||
|
||||
public:
|
||||
MPD(const std::string&, const Json::Value&);
|
||||
@@ -44,7 +45,11 @@ class MPD : public ALabel {
|
||||
std::string getFilename() const;
|
||||
void setLabel();
|
||||
std::string getStateIcon() const;
|
||||
std::string getOptionIcon(std::string optionName, bool activated) const;
|
||||
std::string getOptionIcon(const std::string& optionName, bool activated) const;
|
||||
std::string getArtistStr(bool truncated) const;
|
||||
std::string getAlbumArtistStr(bool truncated) const;
|
||||
std::string getAlbumStr(bool truncated) const;
|
||||
std::string getTitleStr(bool truncated) const;
|
||||
|
||||
// GUI-side methods
|
||||
bool handlePlayPause(GdkEventButton* const&);
|
||||
@@ -54,7 +59,6 @@ class MPD : public ALabel {
|
||||
void tryConnect();
|
||||
void checkErrors(mpd_connection* conn);
|
||||
void fetchState();
|
||||
void queryMPD();
|
||||
|
||||
inline bool stopped() const { return connection_ && state_ == MPD_STATE_STOP; }
|
||||
inline bool playing() const { return connection_ && state_ == MPD_STATE_PLAY; }
|
||||
|
||||
@@ -197,7 +197,6 @@ class Context {
|
||||
void tryConnect() const;
|
||||
void checkErrors(mpd_connection*) const;
|
||||
void do_update();
|
||||
void queryMPD() const;
|
||||
void fetchState() const;
|
||||
constexpr mpd_state state() const;
|
||||
void emit() const;
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
namespace detail {
|
||||
using namespace std::literals::chrono_literals;
|
||||
|
||||
inline bool Context::is_connected() const { return mpd_module_->connection_ != nullptr; }
|
||||
inline bool Context::is_playing() const { return mpd_module_->playing(); }
|
||||
inline bool Context::is_paused() const { return mpd_module_->paused(); }
|
||||
inline bool Context::is_stopped() const { return mpd_module_->stopped(); }
|
||||
|
||||
constexpr inline std::size_t Context::interval() const { return mpd_module_->interval_.count(); }
|
||||
constexpr inline std::size_t Context::interval() const { return mpd_module_->interval_ / 1s; }
|
||||
inline void Context::tryConnect() const { mpd_module_->tryConnect(); }
|
||||
inline unique_connection& Context::connection() { return mpd_module_->connection_; }
|
||||
constexpr inline mpd_state Context::state() const { return mpd_module_->state_; }
|
||||
@@ -15,7 +16,6 @@ constexpr inline mpd_state Context::state() const { return mpd_module_->state_;
|
||||
inline void Context::do_update() { mpd_module_->setLabel(); }
|
||||
|
||||
inline void Context::checkErrors(mpd_connection* conn) const { mpd_module_->checkErrors(conn); }
|
||||
inline void Context::queryMPD() const { mpd_module_->queryMPD(); }
|
||||
inline void Context::fetchState() const { mpd_module_->fetchState(); }
|
||||
inline void Context::emit() const { mpd_module_->emit(); }
|
||||
|
||||
|
||||
@@ -78,6 +78,7 @@ class Mpris : public ALabel {
|
||||
|
||||
PlayerctlPlayerManager* manager;
|
||||
PlayerctlPlayer* player;
|
||||
PlayerctlPlayer* last_active_player_ = nullptr;
|
||||
std::string lastStatus;
|
||||
std::string lastPlayer;
|
||||
|
||||
|
||||
@@ -70,6 +70,9 @@ class Network : public ALabel {
|
||||
|
||||
unsigned long long bandwidth_down_total_{0};
|
||||
unsigned long long bandwidth_up_total_{0};
|
||||
unsigned long long bandwidth_down_prev_{0};
|
||||
unsigned long long bandwidth_up_prev_{0};
|
||||
std::chrono::steady_clock::time_point bandwidth_last_sample_time_;
|
||||
|
||||
std::string state_;
|
||||
std::string essid_;
|
||||
|
||||
@@ -17,7 +17,7 @@ class EventHandler {
|
||||
|
||||
class IPC {
|
||||
public:
|
||||
IPC() { startIPC(); }
|
||||
IPC();
|
||||
|
||||
void registerForIPC(const std::string& ev, EventHandler* ev_handler);
|
||||
void unregisterForIPC(EventHandler* handler);
|
||||
|
||||
@@ -28,6 +28,7 @@ class Privacy : public AModule {
|
||||
|
||||
// Config
|
||||
Gtk::Box box_;
|
||||
std::vector<PrivacyItem*> modules_;
|
||||
uint iconSpacing = 4;
|
||||
uint iconSize = 20;
|
||||
uint transition_duration = 250;
|
||||
|
||||
@@ -16,7 +16,7 @@ class Host {
|
||||
public:
|
||||
Host(const std::size_t id, const Json::Value&, const Bar&,
|
||||
const std::function<void(std::unique_ptr<Item>&)>&,
|
||||
const std::function<void(std::unique_ptr<Item>&)>&);
|
||||
const std::function<void(std::unique_ptr<Item>&)>&, const std::function<void()>&);
|
||||
~Host();
|
||||
|
||||
private:
|
||||
@@ -28,9 +28,13 @@ class Host {
|
||||
static void registerHost(GObject*, GAsyncResult*, gpointer);
|
||||
static void itemRegistered(SnWatcher*, const gchar*, gpointer);
|
||||
static void itemUnregistered(SnWatcher*, const gchar*, gpointer);
|
||||
void itemReady(Item&);
|
||||
void itemInvalidated(Item&);
|
||||
void removeItem(std::vector<std::unique_ptr<Item>>::iterator);
|
||||
void clearItems();
|
||||
|
||||
std::tuple<std::string, std::string> getBusNameAndObjectPath(const std::string);
|
||||
void addRegisteredItem(std::string service);
|
||||
void addRegisteredItem(const std::string& service);
|
||||
|
||||
std::vector<std::unique_ptr<Item>> items_;
|
||||
const std::string bus_name_;
|
||||
@@ -39,10 +43,13 @@ class Host {
|
||||
std::size_t watcher_id_;
|
||||
GCancellable* cancellable_ = nullptr;
|
||||
SnWatcher* watcher_ = nullptr;
|
||||
sigc::connection retry_connection_;
|
||||
unsigned retry_count_ = 0;
|
||||
const Json::Value& config_;
|
||||
const Bar& bar_;
|
||||
const std::function<void(std::unique_ptr<Item>&)> on_add_;
|
||||
const std::function<void(std::unique_ptr<Item>&)> on_remove_;
|
||||
const std::function<void()> on_update_;
|
||||
};
|
||||
|
||||
} // namespace waybar::modules::SNI
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <libdbusmenu-gtk/dbusmenu-gtk.h>
|
||||
#include <sigc++/trackable.h>
|
||||
|
||||
#include <functional>
|
||||
#include <set>
|
||||
#include <string_view>
|
||||
|
||||
@@ -25,9 +26,13 @@ struct ToolTip {
|
||||
|
||||
class Item : public sigc::trackable {
|
||||
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&,
|
||||
const std::function<void(Item&)>&, const std::function<void(Item&)>&,
|
||||
const std::function<void()>&);
|
||||
~Item();
|
||||
|
||||
bool isReady() const;
|
||||
|
||||
std::string bus_name;
|
||||
std::string object_path;
|
||||
|
||||
@@ -41,9 +46,12 @@ class Item : public sigc::trackable {
|
||||
std::string title;
|
||||
std::string icon_name;
|
||||
Glib::RefPtr<Gdk::Pixbuf> icon_pixmap;
|
||||
bool has_custom_icon_ = false;
|
||||
Glib::RefPtr<Gtk::IconTheme> icon_theme;
|
||||
std::string overlay_icon_name;
|
||||
Glib::RefPtr<Gdk::Pixbuf> overlay_icon_pixmap;
|
||||
std::string attention_icon_name;
|
||||
Glib::RefPtr<Gdk::Pixbuf> attention_icon_pixmap;
|
||||
std::string attention_movie_name;
|
||||
std::string icon_theme_path;
|
||||
std::string menu;
|
||||
@@ -62,6 +70,8 @@ class Item : public sigc::trackable {
|
||||
void proxyReady(Glib::RefPtr<Gio::AsyncResult>& result);
|
||||
void setProperty(const Glib::ustring& name, Glib::VariantBase& value);
|
||||
void setStatus(const Glib::ustring& value);
|
||||
void setReady();
|
||||
void invalidate();
|
||||
void setCustomIcon(const std::string& id);
|
||||
void getUpdatedProperties();
|
||||
void processUpdatedProperties(Glib::RefPtr<Gio::AsyncResult>& result);
|
||||
@@ -69,8 +79,13 @@ class Item : public sigc::trackable {
|
||||
const Glib::VariantContainerBase& arguments);
|
||||
|
||||
void updateImage();
|
||||
Glib::RefPtr<Gdk::Pixbuf> extractPixBuf(GVariant* variant);
|
||||
static Glib::RefPtr<Gdk::Pixbuf> extractPixBuf(GVariant* variant);
|
||||
Glib::RefPtr<Gdk::Pixbuf> getIconPixbuf();
|
||||
Glib::RefPtr<Gdk::Pixbuf> getAttentionIconPixbuf();
|
||||
Glib::RefPtr<Gdk::Pixbuf> getOverlayIconPixbuf();
|
||||
Glib::RefPtr<Gdk::Pixbuf> loadIconFromNameOrFile(const std::string& name, bool log_failure);
|
||||
static Glib::RefPtr<Gdk::Pixbuf> overlayPixbufs(const Glib::RefPtr<Gdk::Pixbuf>&,
|
||||
const Glib::RefPtr<Gdk::Pixbuf>&);
|
||||
Glib::RefPtr<Gdk::Pixbuf> getIconByName(const std::string& name, int size);
|
||||
double getScaledIconSize();
|
||||
static void onMenuDestroyed(Item* self, GObject* old_menu_pointer);
|
||||
@@ -86,8 +101,13 @@ class Item : public sigc::trackable {
|
||||
gdouble distance_scrolled_y_ = 0;
|
||||
// visibility of items with Status == Passive
|
||||
bool show_passive_ = false;
|
||||
bool ready_ = false;
|
||||
Glib::ustring status_ = "active";
|
||||
|
||||
const Bar& bar_;
|
||||
const std::function<void(Item&)> on_ready_;
|
||||
const std::function<void(Item&)> on_invalidate_;
|
||||
const std::function<void()> on_updated_;
|
||||
|
||||
Glib::RefPtr<Gio::DBus::Proxy> proxy_;
|
||||
Glib::RefPtr<Gio::Cancellable> cancellable_;
|
||||
|
||||
@@ -19,9 +19,9 @@ class Tray : public AModule {
|
||||
private:
|
||||
void onAdd(std::unique_ptr<Item>& item);
|
||||
void onRemove(std::unique_ptr<Item>& item);
|
||||
void queueUpdate();
|
||||
|
||||
static inline std::size_t nb_hosts_ = 0;
|
||||
bool show_passive_ = false;
|
||||
Gtk::Box box_;
|
||||
SNI::Watcher::singleton watcher_;
|
||||
SNI::Host host_;
|
||||
|
||||
@@ -37,7 +37,7 @@ class BarIpcClient {
|
||||
void onModeUpdate(bool visible_by_modifier);
|
||||
void onUrgencyUpdate(bool visible_by_urgency);
|
||||
void update();
|
||||
bool isModuleEnabled(std::string name);
|
||||
bool isModuleEnabled(const std::string& name);
|
||||
|
||||
Bar& bar_;
|
||||
util::JsonParser parser_;
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
#include <string>
|
||||
|
||||
#include "ipc.hpp"
|
||||
#include "util/SafeSignal.hpp"
|
||||
#include "util/scoped_fd.hpp"
|
||||
#include "util/sleeper_thread.hpp"
|
||||
|
||||
namespace waybar::modules::sway {
|
||||
@@ -27,8 +29,8 @@ class Ipc {
|
||||
std::string payload;
|
||||
};
|
||||
|
||||
sigc::signal<void, const struct ipc_response &> signal_event;
|
||||
sigc::signal<void, const struct ipc_response &> signal_cmd;
|
||||
::waybar::SafeSignal<const struct ipc_response&> signal_event;
|
||||
::waybar::SafeSignal<const struct ipc_response&> signal_cmd;
|
||||
|
||||
void sendCmd(uint32_t type, const std::string& payload = "");
|
||||
void subscribe(const std::string& payload);
|
||||
@@ -44,8 +46,8 @@ class Ipc {
|
||||
struct ipc_response send(int fd, uint32_t type, const std::string& payload = "");
|
||||
struct ipc_response recv(int fd);
|
||||
|
||||
int fd_;
|
||||
int fd_event_;
|
||||
util::ScopedFd fd_;
|
||||
util::ScopedFd fd_event_;
|
||||
std::mutex mutex_;
|
||||
util::SleeperThread thread_;
|
||||
};
|
||||
|
||||
@@ -47,7 +47,7 @@ class Language : public ALabel, public sigc::trackable {
|
||||
void onEvent(const struct Ipc::ipc_response&);
|
||||
void onCmd(const struct Ipc::ipc_response&);
|
||||
|
||||
auto set_current_layout(std::string current_layout) -> void;
|
||||
auto set_current_layout(const std::string& current_layout) -> void;
|
||||
auto init_layouts_map(const std::vector<std::string>& used_layouts) -> void;
|
||||
|
||||
const static std::string XKB_LAYOUT_NAMES_KEY;
|
||||
|
||||
@@ -24,10 +24,11 @@ class Workspaces : public AModule, public sigc::trackable {
|
||||
|
||||
private:
|
||||
static constexpr std::string_view workspace_switch_cmd_ = "workspace {} \"{}\"";
|
||||
static constexpr std::string_view workspace_switch_number_cmd_ = "workspace {} number {}";
|
||||
static constexpr std::string_view persistent_workspace_switch_cmd_ =
|
||||
R"(workspace {} "{}"; move workspace to output "{}"; workspace {} "{}")";
|
||||
|
||||
static int convertWorkspaceNameToNum(std::string name);
|
||||
static int convertWorkspaceNameToNum(const std::string& name);
|
||||
static int windowRewritePriorityFunction(std::string const& window_rule);
|
||||
|
||||
void onCmd(const struct Ipc::ipc_response&);
|
||||
@@ -40,7 +41,7 @@ class Workspaces : public AModule, public sigc::trackable {
|
||||
std::string getIcon(const std::string&, const Json::Value&);
|
||||
std::string getCycleWorkspace(std::vector<Json::Value>::iterator, bool prev) const;
|
||||
uint16_t getWorkspaceIndex(const std::string& name) const;
|
||||
static std::string trimWorkspaceName(std::string);
|
||||
static std::string trimWorkspaceName(const std::string&);
|
||||
bool handleScroll(GdkEventScroll* /*unused*/) override;
|
||||
|
||||
const Bar& bar_;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <giomm/dbusproxy.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "ALabel.hpp"
|
||||
|
||||
@@ -11,23 +12,42 @@ namespace waybar::modules {
|
||||
class SystemdFailedUnits : public ALabel {
|
||||
public:
|
||||
SystemdFailedUnits(const std::string&, const Json::Value&);
|
||||
virtual ~SystemdFailedUnits();
|
||||
virtual ~SystemdFailedUnits() = default;
|
||||
auto update() -> void override;
|
||||
|
||||
private:
|
||||
bool hide_on_ok;
|
||||
std::string format_ok;
|
||||
struct FailedUnit {
|
||||
std::string name;
|
||||
std::string description;
|
||||
std::string load_state;
|
||||
std::string active_state;
|
||||
std::string sub_state;
|
||||
std::string scope;
|
||||
};
|
||||
|
||||
bool update_pending;
|
||||
std::string system_state, user_state, overall_state;
|
||||
uint32_t nr_failed_system, nr_failed_user, nr_failed;
|
||||
std::string last_status;
|
||||
Glib::RefPtr<Gio::DBus::Proxy> system_proxy, user_proxy;
|
||||
bool hide_on_ok_;
|
||||
std::string format_ok_;
|
||||
std::string tooltip_format_;
|
||||
std::string tooltip_format_ok_;
|
||||
std::string tooltip_unit_format_;
|
||||
|
||||
bool update_pending_;
|
||||
std::string system_state_, user_state_, overall_state_;
|
||||
uint32_t nr_failed_system_, nr_failed_user_, nr_failed_;
|
||||
std::string last_status_;
|
||||
Glib::RefPtr<Gio::DBus::Proxy> system_props_proxy_, user_props_proxy_;
|
||||
Glib::RefPtr<Gio::DBus::Proxy> system_manager_proxy_, user_manager_proxy_;
|
||||
std::vector<FailedUnit> failed_units_;
|
||||
|
||||
void notify_cb(const Glib::ustring& sender_name, const Glib::ustring& signal_name,
|
||||
const Glib::VariantContainerBase& arguments);
|
||||
void RequestFailedUnits();
|
||||
void RequestFailedUnitsList();
|
||||
void RequestSystemState();
|
||||
std::vector<FailedUnit> LoadFailedUnitsList(const char* kind,
|
||||
Glib::RefPtr<Gio::DBus::Proxy>& proxy,
|
||||
const std::string& scope);
|
||||
std::string BuildTooltipFailedList() const;
|
||||
void updateData();
|
||||
};
|
||||
|
||||
|
||||
@@ -14,6 +14,8 @@ class Temperature : public ALabel {
|
||||
Temperature(const std::string&, const Json::Value&);
|
||||
virtual ~Temperature() = default;
|
||||
auto update() -> void override;
|
||||
void suspend() override;
|
||||
void resume() override;
|
||||
|
||||
private:
|
||||
float getTemperature();
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
#include "util/scoped_fd.hpp"
|
||||
|
||||
namespace waybar::modules::wayfire {
|
||||
|
||||
using EventHandler = std::function<void(const std::string& event)>;
|
||||
@@ -71,25 +73,9 @@ struct State {
|
||||
auto update_view(const Json::Value& view) -> void;
|
||||
};
|
||||
|
||||
struct Sock {
|
||||
int fd;
|
||||
using Sock = util::ScopedFd;
|
||||
|
||||
Sock(int fd) : fd{fd} {}
|
||||
~Sock() { close(fd); }
|
||||
Sock(const Sock&) = delete;
|
||||
auto operator=(const Sock&) = delete;
|
||||
Sock(Sock&& rhs) noexcept {
|
||||
fd = rhs.fd;
|
||||
rhs.fd = -1;
|
||||
}
|
||||
auto& operator=(Sock&& rhs) noexcept {
|
||||
fd = rhs.fd;
|
||||
rhs.fd = -1;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
class IPC {
|
||||
class IPC : public std::enable_shared_from_this<IPC> {
|
||||
static std::weak_ptr<IPC> instance;
|
||||
Json::CharReaderBuilder reader_builder;
|
||||
Json::StreamWriterBuilder writer_builder;
|
||||
@@ -98,7 +84,7 @@ class IPC {
|
||||
State state;
|
||||
std::mutex state_mutex;
|
||||
|
||||
IPC() { start(); }
|
||||
IPC() = default;
|
||||
|
||||
static auto connect() -> Sock;
|
||||
auto receive(Sock& sock) -> Json::Value;
|
||||
|
||||
@@ -33,8 +33,8 @@ class Taskbar;
|
||||
|
||||
class Task {
|
||||
public:
|
||||
Task(const waybar::Bar &, const Json::Value &, Taskbar *,
|
||||
struct zwlr_foreign_toplevel_handle_v1 *, struct wl_seat *);
|
||||
Task(const waybar::Bar&, const Json::Value&, Taskbar*, struct zwlr_foreign_toplevel_handle_v1*,
|
||||
struct wl_seat*);
|
||||
~Task();
|
||||
|
||||
public:
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <glibmm/dispatcher.h>
|
||||
#include <sigc++/signal.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
@@ -11,11 +12,6 @@
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#ifdef __OpenBSD__
|
||||
#define SIGRTMIN SIGUSR1 - 1
|
||||
#define SIGRTMAX SIGUSR1 + 1
|
||||
#endif
|
||||
|
||||
namespace waybar {
|
||||
|
||||
/**
|
||||
@@ -27,6 +23,12 @@ struct SafeSignal : sigc::signal<void(std::decay_t<Args>...)> {
|
||||
public:
|
||||
SafeSignal() { dp_.connect(sigc::mem_fun(*this, &SafeSignal::handle_event)); }
|
||||
|
||||
void set_max_queued_events(std::size_t max_queued_events) {
|
||||
std::unique_lock lock(mutex_);
|
||||
max_queued_events_ = max_queued_events;
|
||||
trim_queue_locked();
|
||||
}
|
||||
|
||||
template <typename... EmitArgs>
|
||||
void emit(EmitArgs&&... args) {
|
||||
if (main_tid_ == std::this_thread::get_id()) {
|
||||
@@ -41,6 +43,9 @@ struct SafeSignal : sigc::signal<void(std::decay_t<Args>...)> {
|
||||
} else {
|
||||
{
|
||||
std::unique_lock lock(mutex_);
|
||||
if (max_queued_events_ != 0 && queue_.size() >= max_queued_events_) {
|
||||
queue_.pop();
|
||||
}
|
||||
queue_.emplace(std::forward<EmitArgs>(args)...);
|
||||
}
|
||||
dp_.emit();
|
||||
@@ -60,6 +65,15 @@ struct SafeSignal : sigc::signal<void(std::decay_t<Args>...)> {
|
||||
using signal_t::emit_reverse;
|
||||
using signal_t::make_slot;
|
||||
|
||||
void trim_queue_locked() {
|
||||
if (max_queued_events_ == 0) {
|
||||
return;
|
||||
}
|
||||
while (queue_.size() > max_queued_events_) {
|
||||
queue_.pop();
|
||||
}
|
||||
}
|
||||
|
||||
void handle_event() {
|
||||
for (std::unique_lock lock(mutex_); !queue_.empty(); lock.lock()) {
|
||||
auto args = queue_.front();
|
||||
@@ -72,6 +86,7 @@ struct SafeSignal : sigc::signal<void(std::decay_t<Args>...)> {
|
||||
Glib::Dispatcher dp_;
|
||||
std::mutex mutex_;
|
||||
std::queue<arg_tuple_t> queue_;
|
||||
std::size_t max_queued_events_ = 4096;
|
||||
const std::thread::id main_tid_ = std::this_thread::get_id();
|
||||
// cache functor for signal emission to avoid recreating it on each event
|
||||
const slot_t cached_fn_ = make_slot();
|
||||
|
||||
@@ -20,6 +20,8 @@ extern std::list<pid_t> reap;
|
||||
|
||||
namespace waybar::util::command {
|
||||
|
||||
constexpr int kExecFailureExitCode = 127;
|
||||
|
||||
struct res {
|
||||
int exit_code;
|
||||
std::string out;
|
||||
@@ -59,6 +61,7 @@ inline int close(FILE* fp, pid_t pid) {
|
||||
spdlog::debug("Cmd continued");
|
||||
} else if (ret == -1) {
|
||||
spdlog::debug("waitpid failed: {}", strerror(errno));
|
||||
break;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
@@ -109,11 +112,13 @@ inline FILE* open(const std::string& cmd, int& pid, const std::string& output_na
|
||||
::close(fd[0]);
|
||||
dup2(fd[1], 1);
|
||||
setpgid(child_pid, child_pid);
|
||||
if (output_name != "") {
|
||||
if (!output_name.empty()) {
|
||||
setenv("WAYBAR_OUTPUT_NAME", output_name.c_str(), 1);
|
||||
}
|
||||
execlp("/bin/sh", "sh", "-c", cmd.c_str(), (char*)0);
|
||||
exit(0);
|
||||
const int saved_errno = errno;
|
||||
spdlog::error("execlp(/bin/sh) failed in open: {}", strerror(saved_errno));
|
||||
_exit(kExecFailureExitCode);
|
||||
} else {
|
||||
::close(fd[1]);
|
||||
}
|
||||
@@ -138,7 +143,7 @@ inline struct res execNoRead(const std::string& cmd) {
|
||||
return {WEXITSTATUS(stat), ""};
|
||||
}
|
||||
|
||||
inline int32_t forkExec(const std::string& cmd) {
|
||||
inline int32_t forkExec(const std::string& cmd, const std::string& output_name) {
|
||||
if (cmd == "") return -1;
|
||||
|
||||
pid_t pid = fork();
|
||||
@@ -157,8 +162,13 @@ inline int32_t forkExec(const std::string& cmd) {
|
||||
err = pthread_sigmask(SIG_UNBLOCK, &mask, nullptr);
|
||||
if (err != 0) spdlog::error("pthread_sigmask in forkExec failed: {}", strerror(err));
|
||||
setpgid(pid, pid);
|
||||
if (!output_name.empty()) {
|
||||
setenv("WAYBAR_OUTPUT_NAME", output_name.c_str(), 1);
|
||||
}
|
||||
execl("/bin/sh", "sh", "-c", cmd.c_str(), (char*)0);
|
||||
exit(0);
|
||||
const int saved_errno = errno;
|
||||
spdlog::error("execl(/bin/sh) failed in forkExec: {}", strerror(saved_errno));
|
||||
_exit(kExecFailureExitCode);
|
||||
} else {
|
||||
reap_mtx.lock();
|
||||
reap.push_back(pid);
|
||||
@@ -169,4 +179,6 @@ inline int32_t forkExec(const std::string& cmd) {
|
||||
return pid;
|
||||
}
|
||||
|
||||
inline int32_t forkExec(const std::string& cmd) { return forkExec(cmd, ""); }
|
||||
|
||||
} // namespace waybar::util::command
|
||||
|
||||
@@ -14,12 +14,14 @@ struct pollfd;
|
||||
namespace waybar {
|
||||
class CssReloadHelper {
|
||||
public:
|
||||
CssReloadHelper(std::string cssFile, std::function<void()> callback);
|
||||
CssReloadHelper(std::string cssFile, std::function<void(const std::string&)> callback);
|
||||
|
||||
virtual ~CssReloadHelper() = default;
|
||||
|
||||
virtual void monitorChanges();
|
||||
|
||||
virtual void changeCssFile(const std::string& newCssFile);
|
||||
|
||||
protected:
|
||||
std::vector<std::string> parseImports(const std::string& cssFile);
|
||||
|
||||
@@ -42,7 +44,7 @@ class CssReloadHelper {
|
||||
private:
|
||||
std::string m_cssFile;
|
||||
|
||||
std::function<void()> m_callback;
|
||||
std::function<void(const std::string&)> m_callback;
|
||||
|
||||
std::vector<std::tuple<Glib::RefPtr<Gio::FileMonitor>>> m_fileMonitors;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
/**
|
||||
* Result of transforming 8-bit hex codes to rgba().
|
||||
*/
|
||||
struct TransformResult {
|
||||
std::string css;
|
||||
bool was_transformed;
|
||||
};
|
||||
|
||||
/**
|
||||
* Reads a CSS file, searches for 8-bit hex codes (#RRGGBBAA),
|
||||
* and transforms them into GTK-compatible rgba() syntax.
|
||||
*/
|
||||
TransformResult transform_8bit_to_hex(const std::string& file_path);
|
||||
+12
-5
@@ -7,6 +7,7 @@
|
||||
#include <codecvt>
|
||||
#include <iostream>
|
||||
#include <locale>
|
||||
#include <memory>
|
||||
#include <regex>
|
||||
|
||||
#if (FMT_VERSION >= 90000)
|
||||
@@ -26,19 +27,25 @@ class JsonParser {
|
||||
Json::Value root;
|
||||
|
||||
// replace all occurrences of "\x" with "\u00", because JSON doesn't allow "\x" escape sequences
|
||||
std::string modifiedJsonStr = replaceHexadecimalEscape(jsonStr);
|
||||
std::string modifiedJsonStr;
|
||||
const std::string* json = &jsonStr;
|
||||
if (jsonStr.find("\\x") != std::string::npos) {
|
||||
modifiedJsonStr = replaceHexadecimalEscape(jsonStr);
|
||||
json = &modifiedJsonStr;
|
||||
}
|
||||
|
||||
std::istringstream jsonStream(modifiedJsonStr);
|
||||
std::string errs;
|
||||
if (!Json::parseFromStream(m_readerBuilder, jsonStream, &root, &errs)) {
|
||||
// Use local CharReaderBuilder for thread safety - the IPC singleton's
|
||||
// parser can be called concurrently from multiple module threads
|
||||
Json::CharReaderBuilder readerBuilder;
|
||||
auto reader = std::unique_ptr<Json::CharReader>(readerBuilder.newCharReader());
|
||||
if (!reader->parse(json->data(), json->data() + json->size(), &root, &errs)) {
|
||||
throw std::runtime_error("Error parsing JSON: " + errs);
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
private:
|
||||
Json::CharReaderBuilder m_readerBuilder;
|
||||
|
||||
static std::string replaceHexadecimalEscape(const std::string& str) {
|
||||
static std::regex re("\\\\x");
|
||||
return std::regex_replace(str, re, "\\u00");
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
namespace waybar::util {
|
||||
|
||||
class ScopedFd {
|
||||
public:
|
||||
explicit ScopedFd(int fd = -1) : fd_(fd) {}
|
||||
~ScopedFd() {
|
||||
if (fd_ != -1) {
|
||||
close(fd_);
|
||||
}
|
||||
}
|
||||
|
||||
// ScopedFd is non-copyable
|
||||
ScopedFd(const ScopedFd&) = delete;
|
||||
ScopedFd& operator=(const ScopedFd&) = delete;
|
||||
|
||||
// ScopedFd is moveable
|
||||
ScopedFd(ScopedFd&& other) noexcept : fd_(other.fd_) { other.fd_ = -1; }
|
||||
ScopedFd& operator=(ScopedFd&& other) noexcept {
|
||||
if (this != &other) {
|
||||
if (fd_ != -1) {
|
||||
close(fd_);
|
||||
}
|
||||
fd_ = other.fd_;
|
||||
other.fd_ = -1;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
int get() const { return fd_; }
|
||||
|
||||
operator int() const { return fd_; }
|
||||
|
||||
void reset(int fd = -1) {
|
||||
if (fd_ != -1) {
|
||||
close(fd_);
|
||||
}
|
||||
fd_ = fd;
|
||||
}
|
||||
|
||||
int release() {
|
||||
int fd = fd_;
|
||||
fd_ = -1;
|
||||
return fd;
|
||||
}
|
||||
|
||||
private:
|
||||
int fd_;
|
||||
};
|
||||
|
||||
} // namespace waybar::util
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <ctime>
|
||||
@@ -31,8 +32,8 @@ class SleeperThread {
|
||||
|
||||
SleeperThread(std::function<void()> func)
|
||||
: thread_{[this, func] {
|
||||
while (do_run_) {
|
||||
signal_ = false;
|
||||
while (do_run_.load(std::memory_order_relaxed)) {
|
||||
signal_.store(false, std::memory_order_relaxed);
|
||||
func();
|
||||
}
|
||||
}} {
|
||||
@@ -42,9 +43,18 @@ class SleeperThread {
|
||||
}
|
||||
|
||||
SleeperThread& operator=(std::function<void()> func) {
|
||||
if (thread_.joinable()) {
|
||||
stop();
|
||||
thread_.join();
|
||||
}
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(mutex_);
|
||||
do_run_.store(true, std::memory_order_relaxed);
|
||||
signal_.store(false, std::memory_order_relaxed);
|
||||
}
|
||||
thread_ = std::thread([this, func] {
|
||||
while (do_run_) {
|
||||
signal_ = false;
|
||||
while (do_run_.load(std::memory_order_relaxed)) {
|
||||
signal_.store(false, std::memory_order_relaxed);
|
||||
func();
|
||||
}
|
||||
});
|
||||
@@ -56,24 +66,34 @@ class SleeperThread {
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool isRunning() const { return do_run_; }
|
||||
bool isRunning() const { return do_run_.load(std::memory_order_relaxed); }
|
||||
|
||||
auto sleep() {
|
||||
std::unique_lock lk(mutex_);
|
||||
CancellationGuard cancel_lock;
|
||||
return condvar_.wait(lk, [this] { return signal_ || !do_run_; });
|
||||
return condvar_.wait(lk, [this] {
|
||||
return signal_.load(std::memory_order_relaxed) || !do_run_.load(std::memory_order_relaxed);
|
||||
});
|
||||
}
|
||||
|
||||
auto sleep_for(std::chrono::system_clock::duration dur) {
|
||||
std::unique_lock lk(mutex_);
|
||||
CancellationGuard cancel_lock;
|
||||
|
||||
condvar_.wait(lk, [this] {
|
||||
return !is_paused_ || signal_.load(std::memory_order_relaxed) ||
|
||||
!do_run_.load(std::memory_order_relaxed);
|
||||
});
|
||||
|
||||
constexpr auto max_time_point = std::chrono::steady_clock::time_point::max();
|
||||
auto wait_end = max_time_point;
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
if (now < max_time_point - dur) {
|
||||
wait_end = now + dur;
|
||||
}
|
||||
return condvar_.wait_until(lk, wait_end, [this] { return signal_ || !do_run_; });
|
||||
return condvar_.wait_until(lk, wait_end, [this] {
|
||||
return signal_.load(std::memory_order_relaxed) || !do_run_.load(std::memory_order_relaxed);
|
||||
});
|
||||
}
|
||||
|
||||
auto sleep_until(
|
||||
@@ -81,22 +101,30 @@ class SleeperThread {
|
||||
time_point) {
|
||||
std::unique_lock lk(mutex_);
|
||||
CancellationGuard cancel_lock;
|
||||
return condvar_.wait_until(lk, time_point, [this] { return signal_ || !do_run_; });
|
||||
|
||||
condvar_.wait(lk, [this] {
|
||||
return !is_paused_ || signal_.load(std::memory_order_relaxed) ||
|
||||
!do_run_.load(std::memory_order_relaxed);
|
||||
});
|
||||
|
||||
return condvar_.wait_until(lk, time_point, [this] {
|
||||
return signal_.load(std::memory_order_relaxed) || !do_run_.load(std::memory_order_relaxed);
|
||||
});
|
||||
}
|
||||
|
||||
void wake_up() {
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(mutex_);
|
||||
signal_ = true;
|
||||
signal_.store(true, std::memory_order_relaxed);
|
||||
}
|
||||
condvar_.notify_all();
|
||||
}
|
||||
|
||||
auto stop() {
|
||||
void stop() {
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(mutex_);
|
||||
signal_ = true;
|
||||
do_run_ = false;
|
||||
signal_.store(true, std::memory_order_relaxed);
|
||||
do_run_.store(false, std::memory_order_relaxed);
|
||||
}
|
||||
condvar_.notify_all();
|
||||
auto handle = thread_.native_handle();
|
||||
@@ -106,6 +134,17 @@ class SleeperThread {
|
||||
}
|
||||
}
|
||||
|
||||
void pause() {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
is_paused_ = true;
|
||||
}
|
||||
|
||||
void resume() {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
is_paused_ = false;
|
||||
condvar_.notify_all();
|
||||
}
|
||||
|
||||
~SleeperThread() {
|
||||
connection_.disconnect();
|
||||
stop();
|
||||
@@ -118,9 +157,10 @@ class SleeperThread {
|
||||
std::thread thread_;
|
||||
std::condition_variable condvar_;
|
||||
std::mutex mutex_;
|
||||
bool do_run_ = true;
|
||||
bool signal_ = false;
|
||||
std::atomic<bool> do_run_ = true;
|
||||
std::atomic<bool> signal_ = false;
|
||||
sigc::connection connection_;
|
||||
bool is_paused_{false};
|
||||
};
|
||||
|
||||
} // namespace waybar::util
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <libudev.h>
|
||||
|
||||
namespace waybar::util {
|
||||
struct UdevDeleter {
|
||||
void operator()(udev* ptr) const { udev_unref(ptr); }
|
||||
};
|
||||
|
||||
struct UdevDeviceDeleter {
|
||||
void operator()(udev_device* ptr) const { udev_device_unref(ptr); }
|
||||
};
|
||||
|
||||
struct UdevEnumerateDeleter {
|
||||
void operator()(udev_enumerate* ptr) const { udev_enumerate_unref(ptr); }
|
||||
};
|
||||
|
||||
struct UdevMonitorDeleter {
|
||||
void operator()(udev_monitor* ptr) const { udev_monitor_unref(ptr); }
|
||||
};
|
||||
} // namespace waybar::util
|
||||
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
|
||||
namespace waybar::util {
|
||||
size_t utf8_width(const std::string& str);
|
||||
void utf8_truncate(std::string& s, const std::string& ellipsis, size_t max_len);
|
||||
} // namespace waybar::util
|
||||
@@ -27,6 +27,11 @@ The *battery* module displays the current capacity and state (eg. charging) of y
|
||||
default: false ++
|
||||
Option to use the battery design capacity instead of its current maximal capacity.
|
||||
|
||||
*full-at-plugged*: ++
|
||||
typeof: bool ++
|
||||
default: false ++
|
||||
When enabled, a battery that is *Full* while the adapter is online is reported with the *Plugged* status instead of *Full* (so you can style/format it separately). Disabled by default to preserve the existing *Full* behaviour.
|
||||
|
||||
*interval*: ++
|
||||
typeof: integer ++
|
||||
default: 60 ++
|
||||
@@ -91,6 +96,11 @@ The *battery* module displays the current capacity and state (eg. charging) of y
|
||||
typeof: string ++
|
||||
Command to execute when scrolling up on the module.
|
||||
|
||||
*weighted-average*: ++
|
||||
typeof: bool ++
|
||||
default: true ++
|
||||
Option to combine multiple batteries with different capacities.
|
||||
|
||||
*on-scroll-down*: ++
|
||||
typeof: string ++
|
||||
Command to execute when scrolling down on the module.
|
||||
@@ -177,6 +187,7 @@ Every entry in the *events* object consists of a *<event-name>* (typeof: *string
|
||||
|
||||
- *on-<status>-<state>*
|
||||
- *on-<status>-<capacity>*
|
||||
- *on-<status>*
|
||||
|
||||
Where:
|
||||
|
||||
@@ -198,7 +209,9 @@ Where:
|
||||
"events": {
|
||||
"on-discharging-warning": "notify-send -u normal 'Low Battery'",
|
||||
"on-discharging-critical": "notify-send -u critical 'Very Low Battery'",
|
||||
"on-charging-100": "notify-send -u normal 'Battery Full!'"
|
||||
"on-charging-100": "notify-send -u normal 'Battery Full!'",
|
||||
"on-discharging": "notify-send -u normal 'Power Switch' Discharging",
|
||||
"on-charging": "notify-send -u normal 'Power Switch' Charging'"
|
||||
},
|
||||
"format": "{capacity}% {icon}",
|
||||
"format-icons": ["", "", "", "", ""],
|
||||
|
||||
+508
-7
@@ -8,6 +8,8 @@ waybar - cava module
|
||||
|
||||
*cava* module for karlstav/cava project. See it on github: https://github.com/karlstav/cava.
|
||||
|
||||
Module supports two different frontends starting from the 0.15.0 release. The frontend that
|
||||
will be used is managed by the method parameter in the [output] section of the cava configuration file.
|
||||
|
||||
# FILES
|
||||
|
||||
@@ -32,6 +34,10 @@ libcava lives in:
|
||||
:[ string
|
||||
:[
|
||||
:< Path where cava configuration file is placed to
|
||||
|[ *method* \[output\]
|
||||
:[ string
|
||||
:[
|
||||
:< Manages which frontend Waybar cava module should use. Values: raw, sdl_glsl
|
||||
|[ *framerate*
|
||||
:[ integer
|
||||
:[ 30
|
||||
@@ -43,7 +49,7 @@ libcava lives in:
|
||||
|[ *sensitivity*
|
||||
:[ integer
|
||||
:[ 100
|
||||
:[ Manual sensitivity in %. It's recommended to be omitted when *autosens* = 1
|
||||
:[ Manual sensitivity in %. If autosens is enabled, this will only be the initial value. 200 means double height. Accepts only non-negative values
|
||||
|[ *bars*
|
||||
:[ integer
|
||||
:[ 12
|
||||
@@ -68,7 +74,7 @@ libcava lives in:
|
||||
:[ string
|
||||
:[
|
||||
:[ Widget's text after sleep_timer elapsed (hide_on_silence has to be false)
|
||||
|[ *method*
|
||||
|[ *method* \[input\]
|
||||
:[ string
|
||||
:[ pulse
|
||||
:[ Audio capturing method. Possible methods are: pipewire, pulse, alsa, fifo, sndio or shmem
|
||||
@@ -105,9 +111,9 @@ libcava lives in:
|
||||
:[ false
|
||||
:[ Disables or enables the so-called "Monstercat smoothing" with or without "waves"
|
||||
|[ *noise_reduction*
|
||||
:[ double
|
||||
:[ 0.77
|
||||
:[ Range between 0 - 1. The raw visualization is very noisy, this factor adjusts the integral and gravity filters to keep the signal smooth. 1 - will be very slow and smooth, 0 - will be fast but noisy
|
||||
:[ integer
|
||||
:[ 77
|
||||
:[ Range between 0 - 100. The raw visualization is very noisy, this factor adjusts the integral and gravity filters to keep the signal smooth. 100 will be very slow and smooth, 0 will be fast but noisy
|
||||
|[ *input_delay*
|
||||
:[ integer
|
||||
:[ 2
|
||||
@@ -119,11 +125,11 @@ libcava lives in:
|
||||
|[ *data_format*
|
||||
:[ string
|
||||
:[ asci
|
||||
:[ It's impossible to set it. Waybar sets it to = asci for internal needs
|
||||
:[ Raw data format. Can be 'binary' or 'ascii'
|
||||
|[ *raw_target*
|
||||
:[ string
|
||||
:[ /dev/stdout
|
||||
:[ It's impossible to set it. Waybar sets it to = /dev/stdout for internal needs
|
||||
:[ Raw output target. A fifo will be created if target does not exist
|
||||
|[ *menu*
|
||||
:[ string
|
||||
:[
|
||||
@@ -136,6 +142,50 @@ libcava lives in:
|
||||
:[ array
|
||||
:[
|
||||
:[ The actions corresponding to the buttons of the menu.
|
||||
|[ *bar_spacing*
|
||||
:[ integer
|
||||
:[
|
||||
:[ Bars' space between bars in number of characters
|
||||
|[ *bar_width*
|
||||
:[ integer
|
||||
:[
|
||||
:[ Bars' width between bars in number of characters
|
||||
|[ *bar_height*
|
||||
:[ integer
|
||||
:[
|
||||
:[ Useless. bar_height is only used for output in "noritake" format
|
||||
|[ *background*
|
||||
:[ string
|
||||
:[
|
||||
:[ GLSL actual. Support hex code colors only. Must be within ''
|
||||
|[ *foreground*
|
||||
:[ string
|
||||
:[
|
||||
:[ GLSL actual. Support hex code colors only. Must be within ''
|
||||
|[ *gradient*
|
||||
:[ integer
|
||||
:[ 0
|
||||
:[ GLSL actual. Gradient mode(0/1 - on/off)
|
||||
|[ *gradient_count*
|
||||
:[ integer
|
||||
:[ 0
|
||||
:[ GLSL actual. The count of colors for the gradient
|
||||
|[ *gradient_color_N*
|
||||
:[ string
|
||||
:[
|
||||
:[ GLSL actual. N - the number of the gradient color between 1 and 8. Only hex defined colors are supported. Must be within ''
|
||||
|[ *sdl_width*
|
||||
:[ integer
|
||||
:[
|
||||
:[ GLSL actual. Manages the width of the waybar cava GLSL frontend module
|
||||
|[ *sdl_height*
|
||||
:[ integer
|
||||
:[
|
||||
:[ GLSL actual. Manages the height of the waybar cava GLSL frontend module
|
||||
|[ *continuous_rendering*
|
||||
:[ integer
|
||||
:[ 0
|
||||
:[ GLSL actual. Keep rendering even if no audio. Recommended to set to 1
|
||||
|
||||
Configuration can be provided as:
|
||||
- The only cava configuration file which is provided through *cava_config*. The rest configuration can be skipped
|
||||
@@ -153,6 +203,7 @@ Configuration can be provided as:
|
||||
|
||||
- iniparser
|
||||
- fftw3
|
||||
- epoxy (GLSL frontend only)
|
||||
|
||||
# SOLVING ISSUES
|
||||
|
||||
@@ -205,3 +256,453 @@ In case when cava releases new version and you're wanna get it, it should be rai
|
||||
- *#cava*
|
||||
- *#cava.silent* Applied after no sound has been detected for sleep_timer seconds
|
||||
- *#cava.updated* Applied when a new frame is shown
|
||||
# FRONTENDS
|
||||
|
||||
## RAW
|
||||
The cava raw frontend uses ASCII characters to visualize incoming audio data. Each ASCII symbol position corresponds to the value of the audio power pulse.
|
||||
|
||||
Under the hood:
|
||||
```
|
||||
. Incoming audio power pulse list is : 12684
|
||||
. Configured array of ASCII codes is: ["▁", "▂", "▃", "▄", "▅", "▆", "▇", "█" ]. See `format-icons` https://github.com/Alexays/Waybar/wiki/Module:-Cava#example
|
||||
```
|
||||
As a result cava frontend will give ▁▂▆█▄
|
||||
|
||||
Examples:
|
||||
|
||||
waybar config
|
||||
```
|
||||
"cava": {
|
||||
"cava_config": "$XDG_CONFIG_HOME/cava/waybar_raw.conf",
|
||||
"input_delay": 2,
|
||||
"format-icons" : ["▁", "▂", "▃", "▄", "▅", "▆", "▇", "█" ],
|
||||
"actions": {
|
||||
"on-click-right": "mode"
|
||||
}
|
||||
},
|
||||
```
|
||||
|
||||
waybar_raw.conf
|
||||
```
|
||||
## Configuration file for CAVA.
|
||||
# Remove the ; to change parameters.
|
||||
|
||||
|
||||
[general]
|
||||
|
||||
# Smoothing mode. Can be 'normal', 'scientific' or 'waves'. DEPRECATED as of 0.6.0
|
||||
|
||||
# Accepts only non-negative values.
|
||||
|
||||
# 'autosens' will attempt to decrease sensitivity if the bars peak. 1 = on, 0 = off
|
||||
# new as of 0.6.0 autosens of low values (dynamic range)
|
||||
# 'overshoot' allows bars to overshoot (in % of terminal height) without initiating autosens. DEPRECATED as of 0.6.0
|
||||
|
||||
# Manual sensitivity in %. If autosens is enabled, this will only be the initial value.
|
||||
# 200 means double height. Accepts only non-negative values.
|
||||
|
||||
# The number of bars (0-512). 0 sets it to auto (fill up console).
|
||||
# Bars' width and space between bars in number of characters.
|
||||
bars = 12
|
||||
# bar_height is only used for output in "noritake" format
|
||||
|
||||
# For SDL width and space between bars is in pixels, defaults are:
|
||||
|
||||
# sdl_glsl have these default values, they are only used to calculate max number of bars.
|
||||
|
||||
|
||||
# Lower and higher cutoff frequencies for lowest and highest bars
|
||||
# the bandwidth of the visualizer.
|
||||
# Note: there is a minimum total bandwidth of 43Mhz x number of bars.
|
||||
# Cava will automatically increase the higher cutoff if a too low band is specified.
|
||||
|
||||
# Seconds with no input before cava goes to sleep mode. Cava will not perform FFT or drawing and
|
||||
# only check for input once per second. Cava will wake up once input is detected. 0 = disable.
|
||||
sleep_timer = 5
|
||||
|
||||
|
||||
[input]
|
||||
|
||||
# Audio capturing method. Possible methods are: 'fifo', 'portaudio', 'pipewire', 'alsa', 'pulse', 'sndio', 'oss', 'jack' or 'shmem'
|
||||
# Defaults to 'oss', 'pipewire', 'sndio', 'jack', 'pulse', 'alsa', 'portaudio' or 'fifo', in that order, dependent on what support cava was built with.
|
||||
# On Mac it defaults to 'portaudio' or 'fifo'
|
||||
# On windows this is automatic and no input settings are needed.
|
||||
#
|
||||
# All input methods uses the same config variable 'source'
|
||||
# to define where it should get the audio.
|
||||
#
|
||||
# For pulseaudio and pipewire 'source' will be the source. Default: 'auto', which uses the monitor source of the default sink
|
||||
# (all pulseaudio sinks(outputs) have 'monitor' sources(inputs) associated with them).
|
||||
#
|
||||
# For pipewire 'source' will be the object name or object.serial of the device to capture from.
|
||||
# Both input and output devices are supported.
|
||||
#
|
||||
# For alsa 'source' will be the capture device.
|
||||
# For fifo 'source' will be the path to fifo-file.
|
||||
# For shmem 'source' will be /squeezelite-AA:BB:CC:DD:EE:FF where 'AA:BB:CC:DD:EE:FF' will be squeezelite's MAC address
|
||||
#
|
||||
# For sndio 'source' will be a raw recording audio descriptor or a monitoring sub-device, e.g. 'rsnd/2' or 'snd/1'. Default: 'default'.
|
||||
# README.md contains further information on how to setup CAVA for sndio.
|
||||
#
|
||||
# For oss 'source' will be the path to a audio device, e.g. '/dev/dsp2'. Default: '/dev/dsp', i.e. the default audio device.
|
||||
# README.md contains further information on how to setup CAVA for OSS on FreeBSD.
|
||||
#
|
||||
# For jack 'source' will be the name of the JACK server to connect to, e.g. 'foobar'. Default: 'default'.
|
||||
# README.md contains further information on how to setup CAVA for JACK.
|
||||
#
|
||||
|
||||
# The options 'sample_rate', 'sample_bits', 'channels' and 'autoconnect' can be configured for some input methods:
|
||||
# sample_rate: fifo, pipewire, sndio, oss
|
||||
# sample_bits: fifo, pipewire, sndio, oss
|
||||
# channels: sndio, oss, jack
|
||||
# autoconnect: jack
|
||||
# Other methods ignore these settings.
|
||||
#
|
||||
# For 'sndio' and 'oss' they are only preferred values, i.e. if the values are not supported
|
||||
# by the chosen audio device, the device will use other supported values instead.
|
||||
# Example: 48000, 32 and 2, but the device only supports 44100, 16 and 1, then it
|
||||
# will use 44100, 16 and 1.
|
||||
#
|
||||
|
||||
|
||||
[output]
|
||||
|
||||
# Output method. Can be 'ncurses', 'noncurses', 'raw', 'noritake', 'sdl'
|
||||
# or 'sdl_glsl'.
|
||||
# 'noncurses' (default) uses a buffer and cursor movements to only print
|
||||
# changes from frame to frame in the terminal. Uses less resources and is less
|
||||
# prone to tearing (vsync issues) than 'ncurses'.
|
||||
#
|
||||
# 'raw' is an 8 or 16 bit (configurable via the 'bit_format' option) data
|
||||
# stream of the bar heights that can be used to send to other applications.
|
||||
# 'raw' defaults to 200 bars, which can be adjusted in the 'bars' option above.
|
||||
#
|
||||
# 'noritake' outputs a bitmap in the format expected by a Noritake VFD display
|
||||
# in graphic mode. It only support the 3000 series graphical VFDs for now.
|
||||
#
|
||||
# 'sdl' uses the Simple DirectMedia Layer to render in a graphical context.
|
||||
# 'sdl_glsl' uses SDL to create an OpenGL context. Write your own shaders or
|
||||
# use one of the predefined ones.
|
||||
method = raw
|
||||
|
||||
# Orientation of the visualization. Can be 'bottom', 'top', 'left', 'right' or
|
||||
# 'horizontal'. Default is 'bottom'. 'left and 'right' are only supported on sdl
|
||||
# and ncruses output. 'horizontal' (bars go up and down from center) is only supported
|
||||
# on noncurses output.
|
||||
# Note: many fonts have weird or missing glyphs for characters used in orientations
|
||||
# other than 'bottom', which can make output not look right.
|
||||
|
||||
# Visual channels. Can be 'stereo' or 'mono'.
|
||||
# 'stereo' mirrors both channels with low frequencies in center.
|
||||
# 'mono' outputs left to right lowest to highest frequencies.
|
||||
# 'mono_option' set mono to either take input from 'left', 'right' or 'average'.
|
||||
# set 'reverse' to 1 to display frequencies the other way around.
|
||||
|
||||
# Raw output target. A fifo will be created if target does not exist.
|
||||
raw_target = /dev/stdout
|
||||
|
||||
# Raw data format. Can be 'binary' or 'ascii'.
|
||||
data_format = ascii
|
||||
|
||||
# Binary bit format, can be '8bit' (0-255) or '16bit' (0-65530).
|
||||
|
||||
# Ascii max value. In 'ascii' mode range will run from 0 to value specified here
|
||||
|
||||
# Ascii delimiters. In ascii format each bar and frame is separated by a delimiters.
|
||||
# Use decimal value in ascii table (i.e. 59 = ';' and 10 = '\n' (line feed)).
|
||||
bar_delimiter = 0
|
||||
|
||||
# sdl window size and position. -1,-1 is centered.
|
||||
|
||||
# set label on bars on the x-axis. Can be 'frequency' or 'none'. Default: 'none'
|
||||
# 'frequency' displays the lower cut off frequency of the bar above.
|
||||
# Only supported on ncurses and noncurses output.
|
||||
|
||||
# enable synchronized sync. 1 = on, 0 = off
|
||||
# removes flickering in alacritty terminal emulator.
|
||||
# defaults to off since the behaviour in other terminal emulators is unknown
|
||||
|
||||
# Shaders for sdl_glsl, located in $HOME/.config/cava/shaders
|
||||
|
||||
; for glsl output mode, keep rendering even if no audio
|
||||
|
||||
# disable console blank (screen saver) in tty
|
||||
# (Not supported on FreeBSD)
|
||||
|
||||
# show a flat bar at the bottom of the screen when idle, 1 = on, 0 = off
|
||||
|
||||
# show waveform instead of frequency spectrum, 1 = on, 0 = off
|
||||
|
||||
[color]
|
||||
|
||||
# Colors can be one of seven predefined: black, blue, cyan, green, magenta, red, white, yellow.
|
||||
# Or defined by hex code '#xxxxxx' (hex code must be within ''). User defined colors requires
|
||||
# a terminal that can change color definitions such as Gnome-terminal or rxvt.
|
||||
# default is to keep current terminal color
|
||||
|
||||
# SDL and sdl_glsl only support hex code colors, these are the default:
|
||||
|
||||
# Gradient mode, only hex defined colors are supported,
|
||||
# background must also be defined in hex or remain commented out. 1 = on, 0 = off.
|
||||
# You can define as many as 8 different colors. They range from bottom to top of screen
|
||||
|
||||
[smoothing]
|
||||
|
||||
# Disables or enables the so-called "Monstercat smoothing" with or without "waves". Set to 0 to disable.
|
||||
|
||||
# Noise reduction, int 0 - 100. default 77
|
||||
# the raw visualization is very noisy, this factor adjusts the integral and gravity filters to keep the signal smooth
|
||||
# 100 will be very slow and smooth, 0 will be fast but noisy.
|
||||
|
||||
|
||||
[eq]
|
||||
|
||||
# This one is tricky. You can have as much keys as you want.
|
||||
# Remember to uncomment more than one key! More keys = more precision.
|
||||
# Look at readme.md on github for further explanations and examples.
|
||||
```
|
||||
## GLSL
|
||||
The Cava GLSL frontend delegates the visualization of incoming audio data to the GPU via OpenGL.
|
||||
|
||||
There are some mandatory dependencies that need to be satisfied in order for Cava GLSL to be built and function properly:
|
||||
|
||||
. epoxy library must be installed on the system
|
||||
. Vertex and fragment shaders from the original project must be used. They should be downloaded, and the file paths must be configured correctly in the Waybar Cava configuration:
|
||||
1. cava shaders [cava shaders](https://github.com/karlstav/cava/tree/master/output/shaders)
|
||||
2. libcava shaders [libcava shaders](https://github.com/LukashonakV/cava/tree/master/output/shaders)
|
||||
. It is highly recommended to have a separate cava configuration for the Waybar Cava GLSL module and to use this as the cava_config in the Waybar configuration.
|
||||
. It is common for cava configurations to be placed in the XDG_CONFIG_HOME directory, including shaders as well. Consider keeping them in the $XDG_CONFIG_HOME/cava/shaders folder.
|
||||
|
||||
Key configuration options:
|
||||
|
||||
. bars. The more values the parameter has, the more interesting the visualization becomes.
|
||||
. method in output section must be set to sdl_glsl
|
||||
. sdl_width and sdl_height manage the size of the module. Adjust them according to your needs.
|
||||
. Shaders for sdl_glsl, located in $HOME/.config/cava/shaders. Example: "vertex_shader" = "pass_through.vert" "fragment_shader" = "spectrogram.frag"
|
||||
. Set continuous_rendering to 1 to enable smooth rendering; set it to 0 otherwise. It is recommended to keep it set to 1.
|
||||
. background, foreground, and gradient_color_N (where N is a number between 1 and 8) must be defined using hex code
|
||||
|
||||
Example:
|
||||
|
||||
waybar config
|
||||
```
|
||||
"cava": {
|
||||
"cava_config": "$XDG_CONFIG_HOME/cava/waybar_cava#3.conf",
|
||||
"input_delay": 2,
|
||||
"actions": {
|
||||
"on-click-right": "mode"
|
||||
}
|
||||
},
|
||||
```
|
||||
|
||||
waybar_raw.conf
|
||||
```
|
||||
## Configuration file for CAVA.
|
||||
# Remove the ; to change parameters.
|
||||
|
||||
|
||||
[general]
|
||||
|
||||
# Smoothing mode. Can be 'normal', 'scientific' or 'waves'. DEPRECATED as of 0.6.0
|
||||
|
||||
# Accepts only non-negative values.
|
||||
|
||||
# 'autosens' will attempt to decrease sensitivity if the bars peak. 1 = on, 0 = off
|
||||
# new as of 0.6.0 autosens of low values (dynamic range)
|
||||
# 'overshoot' allows bars to overshoot (in % of terminal height) without initiating autosens. DEPRECATED as of 0.6.0
|
||||
|
||||
# Manual sensitivity in %. If autosens is enabled, this will only be the initial value.
|
||||
# 200 means double height. Accepts only non-negative values.
|
||||
|
||||
# The number of bars (0-512). 0 sets it to auto (fill up console).
|
||||
# Bars' width and space between bars in number of characters.
|
||||
bars = 50
|
||||
|
||||
# bar_height is only used for output in "noritake" format
|
||||
|
||||
# For SDL width and space between bars is in pixels, defaults are:
|
||||
|
||||
# sdl_glsl have these default values, they are only used to calculate max number of bars.
|
||||
|
||||
# Lower and higher cutoff frequencies for lowest and highest bars
|
||||
# the bandwidth of the visualizer.
|
||||
# Note: there is a minimum total bandwidth of 43Mhz x number of bars.
|
||||
# Cava will automatically increase the higher cutoff if a too low band is specified.
|
||||
|
||||
# Seconds with no input before cava goes to sleep mode. Cava will not perform FFT or drawing and
|
||||
# only check for input once per second. Cava will wake up once input is detected. 0 = disable.
|
||||
sleep_timer = 5
|
||||
|
||||
|
||||
[input]
|
||||
|
||||
# Audio capturing method. Possible methods are: 'fifo', 'portaudio', 'pipewire', 'alsa', 'pulse', 'sndio', 'oss', 'jack' or 'shmem'
|
||||
# Defaults to 'oss', 'pipewire', 'sndio', 'jack', 'pulse', 'alsa', 'portaudio' or 'fifo', in that order, dependent on what support cava was built with.
|
||||
# On Mac it defaults to 'portaudio' or 'fifo'
|
||||
# On windows this is automatic and no input settings are needed.
|
||||
#
|
||||
# All input methods uses the same config variable 'source'
|
||||
# to define where it should get the audio.
|
||||
#
|
||||
# For pulseaudio and pipewire 'source' will be the source. Default: 'auto', which uses the monitor source of the default sink
|
||||
# (all pulseaudio sinks(outputs) have 'monitor' sources(inputs) associated with them).
|
||||
#
|
||||
# For pipewire 'source' will be the object name or object.serial of the device to capture from.
|
||||
# Both input and output devices are supported.
|
||||
#
|
||||
# For alsa 'source' will be the capture device.
|
||||
# For fifo 'source' will be the path to fifo-file.
|
||||
# For shmem 'source' will be /squeezelite-AA:BB:CC:DD:EE:FF where 'AA:BB:CC:DD:EE:FF' will be squeezelite's MAC address
|
||||
#
|
||||
# For sndio 'source' will be a raw recording audio descriptor or a monitoring sub-device, e.g. 'rsnd/2' or 'snd/1'. Default: 'default'.
|
||||
# README.md contains further information on how to setup CAVA for sndio.
|
||||
#
|
||||
# For oss 'source' will be the path to a audio device, e.g. '/dev/dsp2'. Default: '/dev/dsp', i.e. the default audio device.
|
||||
# README.md contains further information on how to setup CAVA for OSS on FreeBSD.
|
||||
#
|
||||
# For jack 'source' will be the name of the JACK server to connect to, e.g. 'foobar'. Default: 'default'.
|
||||
# README.md contains further information on how to setup CAVA for JACK.
|
||||
#
|
||||
|
||||
|
||||
# The options 'sample_rate', 'sample_bits', 'channels' and 'autoconnect' can be configured for some input methods:
|
||||
# sample_rate: fifo, pipewire, sndio, oss
|
||||
# sample_bits: fifo, pipewire, sndio, oss
|
||||
# channels: sndio, oss, jack
|
||||
# autoconnect: jack
|
||||
# Other methods ignore these settings.
|
||||
#
|
||||
# For 'sndio' and 'oss' they are only preferred values, i.e. if the values are not supported
|
||||
# by the chosen audio device, the device will use other supported values instead.
|
||||
# Example: 48000, 32 and 2, but the device only supports 44100, 16 and 1, then it
|
||||
# will use 44100, 16 and 1.
|
||||
#
|
||||
|
||||
|
||||
[output]
|
||||
|
||||
# Output method. Can be 'ncurses', 'noncurses', 'raw', 'noritake', 'sdl'
|
||||
# or 'sdl_glsl'.
|
||||
# 'noncurses' (default) uses a buffer and cursor movements to only print
|
||||
# changes from frame to frame in the terminal. Uses less resources and is less
|
||||
# prone to tearing (vsync issues) than 'ncurses'.
|
||||
#
|
||||
# 'raw' is an 8 or 16 bit (configurable via the 'bit_format' option) data
|
||||
# stream of the bar heights that can be used to send to other applications.
|
||||
# 'raw' defaults to 200 bars, which can be adjusted in the 'bars' option above.
|
||||
#
|
||||
# 'noritake' outputs a bitmap in the format expected by a Noritake VFD display
|
||||
# in graphic mode. It only support the 3000 series graphical VFDs for now.
|
||||
#
|
||||
# 'sdl' uses the Simple DirectMedia Layer to render in a graphical context.
|
||||
# 'sdl_glsl' uses SDL to create an OpenGL context. Write your own shaders or
|
||||
# use one of the predefined ones.
|
||||
method = sdl_glsl
|
||||
|
||||
# Orientation of the visualization. Can be 'bottom', 'top', 'left', 'right' or
|
||||
# 'horizontal'. Default is 'bottom'. 'left and 'right' are only supported on sdl
|
||||
# and ncruses output. 'horizontal' (bars go up and down from center) is only supported
|
||||
# on noncurses output.
|
||||
# Note: many fonts have weird or missing glyphs for characters used in orientations
|
||||
# other than 'bottom', which can make output not look right.
|
||||
|
||||
# Visual channels. Can be 'stereo' or 'mono'.
|
||||
# 'stereo' mirrors both channels with low frequencies in center.
|
||||
# 'mono' outputs left to right lowest to highest frequencies.
|
||||
# 'mono_option' set mono to either take input from 'left', 'right' or 'average'.
|
||||
# set 'reverse' to 1 to display frequencies the other way around.
|
||||
|
||||
# Raw output target. A fifo will be created if target does not exist.
|
||||
|
||||
# Raw data format. Can be 'binary' or 'ascii'.
|
||||
|
||||
# Binary bit format, can be '8bit' (0-255) or '16bit' (0-65530).
|
||||
|
||||
# Ascii max value. In 'ascii' mode range will run from 0 to value specified here
|
||||
|
||||
# Ascii delimiters. In ascii format each bar and frame is separated by a delimiters.
|
||||
# Use decimal value in ascii table (i.e. 59 = ';' and 10 = '\n' (line feed)).
|
||||
bar_delimiter = 0
|
||||
|
||||
# sdl window size and position. -1,-1 is centered.
|
||||
sdl_width = 150
|
||||
sdl_height = 39
|
||||
|
||||
# set label on bars on the x-axis. Can be 'frequency' or 'none'. Default: 'none'
|
||||
# 'frequency' displays the lower cut off frequency of the bar above.
|
||||
# Only supported on ncurses and noncurses output.
|
||||
|
||||
# enable synchronized sync. 1 = on, 0 = off
|
||||
# removes flickering in alacritty terminal emulator.
|
||||
# defaults to off since the behaviour in other terminal emulators is unknown
|
||||
|
||||
# Shaders for sdl_glsl, located in $HOME/.config/cava/shaders
|
||||
vertex_shader = pass_through.vert
|
||||
fragment_shader = bar_spectrum.frag
|
||||
|
||||
; for glsl output mode, keep rendering even if no audio
|
||||
continuous_rendering = 1;
|
||||
|
||||
# disable console blank (screen saver) in tty
|
||||
# (Not supported on FreeBSD)
|
||||
|
||||
# show a flat bar at the bottom of the screen when idle, 1 = on, 0 = off
|
||||
|
||||
# show waveform instead of frequency spectrum, 1 = on, 0 = off
|
||||
|
||||
[color]
|
||||
|
||||
|
||||
# Colors can be one of seven predefined: black, blue, cyan, green, magenta, red, white, yellow.
|
||||
# Or defined by hex code '#xxxxxx' (hex code must be within ''). User defined colors requires
|
||||
# a terminal that can change color definitions such as Gnome-terminal or rxvt.
|
||||
# default is to keep current terminal color
|
||||
|
||||
# SDL and sdl_glsl only support hex code colors, these are the default:
|
||||
background = '#282C34'
|
||||
|
||||
# Gradient mode, only hex defined colors are supported,
|
||||
# background must also be defined in hex or remain commented out. 1 = on, 0 = off.
|
||||
# You can define as many as 8 different colors. They range from bottom to top of screen
|
||||
gradient = 1
|
||||
gradient_count = 2
|
||||
gradient_color_1 = '#282C34'
|
||||
gradient_color_2 = '#45475A'
|
||||
|
||||
; gradient_color_1 = '#59cc33'
|
||||
; gradient_color_2 = '#80cc33'
|
||||
gradient_color_3 = '#a6cc33'
|
||||
gradient_color_4 = '#cccc33'
|
||||
gradient_color_5 = '#cca633'
|
||||
gradient_color_6 = '#cc8033'
|
||||
gradient_color_7 = '#cc5933'
|
||||
gradient_color_8 = '#cc3333'
|
||||
|
||||
[smoothing]
|
||||
|
||||
# Percentage value for integral smoothing. Takes values from 0 - 100.
|
||||
# Higher values means smoother, but less precise. 0 to disable.
|
||||
# DEPRECATED as of 0.8.0, use noise_reduction instead
|
||||
|
||||
# Disables or enables the so-called "Monstercat smoothing" with or without "waves". Set to 0 to disable.
|
||||
|
||||
# Set gravity percentage for "drop off". Higher values means bars will drop faster.
|
||||
# Accepts only non-negative values. 50 means half gravity, 200 means double. Set to 0 to disable "drop off".
|
||||
# DEPRECATED as of 0.8.0, use noise_reduction instead
|
||||
|
||||
|
||||
# In bar height, bars that would have been lower that this will not be drawn.
|
||||
# DEPRECATED as of 0.8.0
|
||||
|
||||
# Noise reduction, int 0 - 100. default 77
|
||||
# the raw visualization is very noisy, this factor adjusts the integral and gravity filters to keep the signal smooth
|
||||
# 100 will be very slow and smooth, 0 will be fast but noisy.
|
||||
|
||||
[eq]
|
||||
|
||||
# This one is tricky. You can have as much keys as you want.
|
||||
# Remember to uncomment more than one key! More keys = more precision.
|
||||
# Look at readme.md on github for further explanations and examples.
|
||||
```
|
||||
|
||||
Different waybar_cava#N.conf see at [cava GLSL](https://github.com/Alexays/Waybar/wiki/Module:-Cava:-GLSL)
|
||||
|
||||
+14
-2
@@ -204,7 +204,7 @@ View all valid format options in *strftime(3)* or have a look https://en.cpprefe
|
||||
```
|
||||
"clock": {
|
||||
"format": "{:%H:%M} ",
|
||||
"format-alt": "{:%A, %B %d, %Y (%R)} ",
|
||||
"format-alt": "{:%A, %B %d, %Y (%R)} ",
|
||||
"tooltip-format": "<tt><small>{calendar}</small></tt>",
|
||||
"calendar": {
|
||||
"mode" : "year",
|
||||
@@ -259,7 +259,19 @@ View all valid format options in *strftime(3)* or have a look https://en.cpprefe
|
||||
"tooltip-format": "{tz_list}"
|
||||
}
|
||||
```
|
||||
5. Simple calendar tooltip
|
||||
|
||||
```
|
||||
"clock": {
|
||||
"format": "{:%H:%M}",
|
||||
"tooltip-format": "<tt>{calendar}</tt>",
|
||||
"calendar": {
|
||||
"format": {
|
||||
"today": "<span color='#ffcc66'><b>{}</b></span>"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
# STYLE
|
||||
|
||||
- *#clock*
|
||||
@@ -287,7 +299,7 @@ Example of working config
|
||||
```
|
||||
"clock": {
|
||||
"format": "{:%H:%M} ",
|
||||
"format-alt": "{:%A, %B %d, %Y (%R)} ",
|
||||
"format-alt": "{:%A, %B %d, %Y (%R)} ",
|
||||
"tooltip-format": "\n<span size='9pt' font='WenQuanYi Zen Hei Mono'>{calendar}</span>",
|
||||
"calendar": {
|
||||
"mode" : "year",
|
||||
|
||||
@@ -234,7 +234,6 @@ $text\\n$tooltip\\n$class*
|
||||
```
|
||||
"custom/pacman": {
|
||||
"format": "{text} ",
|
||||
"interval": 3600, // every hour
|
||||
"exec": "checkupdates | wc -l", // # of updates
|
||||
"exec-if": "exit 0", // always run; consider advanced run conditions
|
||||
"on-click": "termite -e 'sudo pacman -Syu'; pkill -SIGRTMIN+8 waybar", // update system
|
||||
@@ -242,7 +241,7 @@ $text\\n$tooltip\\n$class*
|
||||
}
|
||||
```
|
||||
|
||||
You can use the signal and update the number of available packages with *pkill -RTMIN+8 waybar*.
|
||||
Under the premise that interval is not defined, you can use the signal and update the number of available packages with *pkill -RTMIN+8 waybar*.
|
||||
|
||||
# STYLE
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ Addressed by *dwl/tags*
|
||||
|
||||
- *#tags button*
|
||||
- *#tags button.occupied*
|
||||
- *#tags button.empty*
|
||||
- *#tags button.focused*
|
||||
- *#tags button.urgent*
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ Feral Gamemode optimizations.
|
||||
|
||||
*glyph*: ++
|
||||
typeof: string ++
|
||||
default: ++
|
||||
default: ++
|
||||
The string icon to display. Only visible if *use-icon* is set to false.
|
||||
|
||||
*icon-name*: ++
|
||||
@@ -82,7 +82,7 @@ Feral Gamemode optimizations.
|
||||
"gamemode": {
|
||||
"format": "{glyph}",
|
||||
"format-alt": "{glyph} {count}",
|
||||
"glyph": "",
|
||||
"glyph": "",
|
||||
"hide-not-running": true,
|
||||
"use-icon": true,
|
||||
"icon-name": "input-gaming-symbolic",
|
||||
|
||||
@@ -38,6 +38,20 @@ Addressed by *hyprland/language*
|
||||
typeof: array ++
|
||||
The actions corresponding to the buttons of the menu.
|
||||
|
||||
*tooltip*: ++
|
||||
typeof: boolean ++
|
||||
default: true ++
|
||||
Enables or disables the tooltip for the language module. By default, the tooltip is enabled. Set to *false* to disable.
|
||||
|
||||
*tooltip-format*: ++
|
||||
typeof: string ++
|
||||
default: {long} ++
|
||||
Specifies the format of the tooltip when it is enabled. It follows the same format replacement rules as the *format* key.
|
||||
|
||||
*tooltip-format-<lang>*: ++
|
||||
typeof: string ++
|
||||
Allows specifying a different tooltip format for each language. The *<lang>* should be replaced with the language code. This can be used to provide a custom tooltip for each language.
|
||||
|
||||
*expand*: ++
|
||||
typeof: bool ++
|
||||
default: false ++
|
||||
@@ -66,6 +80,28 @@ Addressed by *hyprland/language*
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
"hyprland/language": {
|
||||
"format": "{}",
|
||||
"format-en": "US",
|
||||
"format-es": "ES",
|
||||
"tooltip": true,
|
||||
"tooltip-format": "{long}"
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
"hyprland/language": {
|
||||
"format": "{}",
|
||||
"format-en": "US",
|
||||
"format-es": "ES",
|
||||
"tooltip": true,
|
||||
"tooltip-format": "{}",
|
||||
"tooltip-format-es": "{Español}",
|
||||
"tooltip-format-en": "{English (american)}"
|
||||
}
|
||||
```
|
||||
|
||||
# STYLE
|
||||
|
||||
- *#language*
|
||||
|
||||
@@ -130,6 +130,19 @@ This setting is ignored if *workspace-taskbar.enable* is set to true.
|
||||
Otherwise, the workspace will open on the monitor where it was previously assigned.
|
||||
Analog to using `focusworkspaceoncurrentmonitor` dispatcher instead of `workspace` in Hyprland.
|
||||
|
||||
*enable-bar-scroll*: ++
|
||||
typeof: bool ++
|
||||
default: false ++
|
||||
If set to false, you can't scroll to cycle throughout workspaces from the entire bar. If set to true this behaviour is enabled.
|
||||
|
||||
*on-scroll-up*: ++
|
||||
typeof: string ++
|
||||
Command to execute when scrolling up on the module. This replaces the default behaviour of workspace cycling.
|
||||
|
||||
*on-scroll-down*: ++
|
||||
typeof: string ++
|
||||
Command to execute when scrolling down on the module. This replaces the default behaviour of workspace cycling.
|
||||
|
||||
*ignore-workspaces*: ++
|
||||
typeof: array ++
|
||||
default: [] ++
|
||||
|
||||
@@ -26,7 +26,8 @@ The *image* module displays an image from a path.
|
||||
*interval*: ++
|
||||
typeof: integer or float ++
|
||||
The interval (in seconds) to re-render the image. ++
|
||||
Minimum value is 0.001 (1ms). Values smaller than 1ms will be set to 1ms. ++
|
||||
If set to a positive value, the minimum is 0.001 (1ms). Values smaller than 1ms will be set to 1ms. ++
|
||||
Zero or negative values are treated as "once". ++
|
||||
This is useful if the contents of *path* changes. ++
|
||||
If no *interval* is defined, the image will only be rendered once.
|
||||
|
||||
|
||||
+12
-6
@@ -102,23 +102,29 @@ Addressed by *memory*
|
||||
default: false ++
|
||||
Enables this module to consume all left over space dynamically.
|
||||
|
||||
*unit*: ++
|
||||
typeof: string ++
|
||||
default: GiB ++
|
||||
Used to specify unit for total, swapTotal, used, swapUsed, avail, swapAvail,
|
||||
and swapState. Accepts B, kB, kiB, MB, MiB, GB, GiB, TB, and TiB.
|
||||
|
||||
# FORMAT REPLACEMENTS
|
||||
|
||||
*{percentage}*: Percentage of memory in use.
|
||||
|
||||
*{swapPercentage}*: Percentage of swap in use.
|
||||
|
||||
*{total}*: Amount of total memory available in GiB.
|
||||
*{total}*: Amount of total memory available. Defaults to GiB.
|
||||
|
||||
*{swapTotal}*: Amount of total swap available in GiB.
|
||||
*{swapTotal}*: Amount of total swap available. Defaults to GiB.
|
||||
|
||||
*{used}*: Amount of used memory in GiB.
|
||||
*{used}*: Amount of used memory. Defaults to GiB.
|
||||
|
||||
*{swapUsed}*: Amount of used swap in GiB.
|
||||
*{swapUsed}*: Amount of used swap. Defaults to GiB.
|
||||
|
||||
*{avail}*: Amount of available memory in GiB.
|
||||
*{avail}*: Amount of available memory. Defaults to GiB.
|
||||
|
||||
*{swapAvail}*: Amount of available swap in GiB.
|
||||
*{swapAvail}*: Amount of available swap. Defaults to GiB.
|
||||
|
||||
*{swapState}*: Signals if swap is activated or not
|
||||
|
||||
|
||||
@@ -77,12 +77,12 @@ The slider is a component with multiple CSS Nodes, of which the following are ex
|
||||
min-height: 80px;
|
||||
min-width: 10px;
|
||||
border-radius: 5px;
|
||||
background-color: black;
|
||||
background: black;
|
||||
}
|
||||
|
||||
#pulseaudio-slider highlight {
|
||||
min-width: 10px;
|
||||
border-radius: 5px;
|
||||
background-color: green;
|
||||
background: green;
|
||||
}
|
||||
```
|
||||
|
||||
@@ -97,7 +97,11 @@ Additionally, you can control the volume by scrolling *up* or *down* while the c
|
||||
|
||||
*reverse-scrolling*: ++
|
||||
typeof: bool ++
|
||||
Option to reverse the scroll direction.
|
||||
Option to reverse the scroll direction for touchpads.
|
||||
|
||||
*reverse-mouse-scrolling*: ++
|
||||
typeof: bool ++
|
||||
Option to reverse the scroll direction for mice.
|
||||
|
||||
*tooltip*: ++
|
||||
typeof: bool ++
|
||||
@@ -173,9 +177,9 @@ to be selected when the corresponding audio device is muted. This applies to *de
|
||||
"format-icons": {
|
||||
"alsa_output.pci-0000_00_1f.3.analog-stereo": "",
|
||||
"alsa_output.pci-0000_00_1f.3.analog-stereo-muted": "",
|
||||
"headphones": "",
|
||||
"handsfree": "",
|
||||
"headset": "",
|
||||
"headphone": "",
|
||||
"hands-free": "",
|
||||
"headset": "",
|
||||
"phone": "",
|
||||
"phone-muted": "",
|
||||
"portable": "",
|
||||
|
||||
@@ -87,7 +87,7 @@ Addressed by *river/mode*
|
||||
|
||||
```
|
||||
"river/mode": {
|
||||
"format": " {}"
|
||||
"format": " {}"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@ Addressed by *sway/mode*
|
||||
|
||||
```
|
||||
"sway/mode": {
|
||||
"format": " {}",
|
||||
"format": " {}",
|
||||
"max-length": 50
|
||||
}
|
||||
```
|
||||
|
||||
@@ -179,7 +179,6 @@ n.b.: the list of outputs can be obtained from command line using *swaymsg -t ge
|
||||
"format": "<span size='larger'>{name}</span> {windows}",
|
||||
"format-window-separator": " | ",
|
||||
"window-rewrite-default": "{name}",
|
||||
"window-format": "<span color='#e0e0e0'>{name}</span>",
|
||||
"window-rewrite": {
|
||||
"class<firefox>": "",
|
||||
"class<kitty>": "k",
|
||||
|
||||
@@ -19,7 +19,7 @@ Addressed by *systemd-failed-units*
|
||||
|
||||
*format-ok*: ++
|
||||
typeof: string ++
|
||||
This format is used when there is no failing units.
|
||||
This format is used when there are no failing units.
|
||||
|
||||
*user*: ++
|
||||
typeof: bool ++
|
||||
@@ -34,15 +34,30 @@ Addressed by *systemd-failed-units*
|
||||
*hide-on-ok*: ++
|
||||
typeof: bool ++
|
||||
default: *true* ++
|
||||
Option to hide this module when there is no failing units.
|
||||
Option to hide this module when there are no failed units.
|
||||
|
||||
*tooltip-format*: ++
|
||||
typeof: string ++
|
||||
default: *System: {system_state}\nUser: {user_state}\nFailed units ({nr_failed}):\n{failed_units_list}* ++
|
||||
Tooltip format shown when there are failed units.
|
||||
|
||||
*tooltip-format-ok*: ++
|
||||
typeof: string ++
|
||||
default: *System: {system_state}\nUser: {user_state}* ++
|
||||
Tooltip format used when there are no failed units.
|
||||
|
||||
*tooltip-unit-format*: ++
|
||||
typeof: string ++
|
||||
default: *{name}: {description}* ++
|
||||
Format used to render each failed unit inside the tooltip. Each item is prefixed with a bullet.
|
||||
|
||||
*menu*: ++
|
||||
typeof: string ++
|
||||
Action that popups the menu.
|
||||
Action that pops up the menu.
|
||||
|
||||
*menu-file*: ++
|
||||
typeof: string ++
|
||||
Location of the menu descriptor file. There need to be an element of type
|
||||
Location of the menu descriptor file. There needs to be an element of type
|
||||
GtkMenu with id *menu*
|
||||
|
||||
*menu-actions*: ++
|
||||
@@ -62,11 +77,23 @@ Addressed by *systemd-failed-units*
|
||||
|
||||
*{nr_failed}*: Number of total failed units.
|
||||
|
||||
*{systemd_state}:* State of the systemd system session
|
||||
*{system_state}:* State of the systemd system session.
|
||||
|
||||
*{user_state}:* State of the systemd user session
|
||||
*{user_state}:* State of the systemd user session.
|
||||
|
||||
*{overall_state}:* Overall state of the systemd and user session. ("Ok" or "Degraded")
|
||||
*{overall_state}:* Overall state of the systemd and user session. ("ok" or "degraded")
|
||||
|
||||
*{failed_units_list}:* Bulleted list of failed units using *tooltip-unit-format*. Empty when
|
||||
there are no failed units.
|
||||
|
||||
The *tooltip-unit-format* string supports the following replacements:
|
||||
|
||||
*{name}*: Unit name ++
|
||||
*{description}*: Unit description ++
|
||||
*{load_state}*: Unit load state ++
|
||||
*{active_state}*: Unit active state ++
|
||||
*{sub_state}*: Unit sub state ++
|
||||
*{scope}*: Either *system* or *user* depending on where the unit originated
|
||||
|
||||
# EXAMPLES
|
||||
|
||||
@@ -77,6 +104,8 @@ Addressed by *systemd-failed-units*
|
||||
"format-ok": "✓",
|
||||
"system": true,
|
||||
"user": false,
|
||||
"tooltip-format": "{nr_failed} failed units:\n{failed_units_list}",
|
||||
"tooltip-unit-format": "{scope}: {name} ({active_state})",
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -128,7 +128,7 @@ The *wireplumber* module displays the current volume reported by WirePlumber.
|
||||
```
|
||||
"wireplumber#sink": {
|
||||
"format": "{volume}% {icon}",
|
||||
"format-muted": "",
|
||||
"format-muted": "",
|
||||
"format-icons": ["", "", ""],
|
||||
"on-click": "helvum",
|
||||
"on-click-right": "wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle",
|
||||
|
||||
+6
-1
@@ -36,7 +36,7 @@ The visual display elements for waybar use a CSS stylesheet, see *waybar-styles(
|
||||
*expand-right* ++
|
||||
typeof: bool ++
|
||||
default: false ++
|
||||
Enables the modules-left to consume all left over space dynamically.
|
||||
Enables the modules-right to consume all left over space dynamically.
|
||||
|
||||
*layer* ++
|
||||
typeof: string ++
|
||||
@@ -363,6 +363,11 @@ A group may hide all but one element, showing them only on mouse hover. In order
|
||||
default: false ++
|
||||
Whether left click should reveal the content rather than mouse over. Note that grouped modules may still process their own on-click events.
|
||||
|
||||
*start-expanded*: ++
|
||||
typeof: bool ++
|
||||
default: false ++
|
||||
Defines whether the drawer should initialize in an expanded state.
|
||||
|
||||
*transition-left-to-right*: ++
|
||||
typeof: bool ++
|
||||
default: true ++
|
||||
|
||||
+17
-6
@@ -1,6 +1,6 @@
|
||||
project(
|
||||
'waybar', 'cpp', 'c',
|
||||
version: '0.14.0',
|
||||
version: '0.15.0',
|
||||
license: 'MIT',
|
||||
meson_version: '>= 0.59.0',
|
||||
default_options : [
|
||||
@@ -185,7 +185,9 @@ src_files = files(
|
||||
'src/util/gtk_icon.cpp',
|
||||
'src/util/icon_loader.cpp',
|
||||
'src/util/regex_collection.cpp',
|
||||
'src/util/css_reload_helper.cpp'
|
||||
'src/util/css_reload_helper.cpp',
|
||||
'src/util/transform_8bit_to_rgba.cpp',
|
||||
'src/util/utf8_string.cpp'
|
||||
)
|
||||
|
||||
man_files = files(
|
||||
@@ -497,16 +499,24 @@ else
|
||||
man_files += files('man/waybar-clock.5.scd')
|
||||
endif
|
||||
|
||||
cava = dependency('cava',
|
||||
version : '>=0.10.4',
|
||||
cava = dependency('libcava',
|
||||
version : '>=0.10.7',
|
||||
required: get_option('cava'),
|
||||
fallback : ['cava', 'cava_dep'],
|
||||
fallback : ['libcava', 'cava_dep'],
|
||||
not_found_message: 'cava is not found. Building waybar without cava')
|
||||
|
||||
eproxy = dependency('epoxy', required: false)
|
||||
|
||||
if cava.found()
|
||||
add_project_arguments('-DHAVE_LIBCAVA', language: 'cpp')
|
||||
src_files += files('src/modules/cava/cava.cpp', 'src/modules/cava/cava_backend.cpp')
|
||||
src_files += files('src/modules/cava/cavaRaw.cpp',
|
||||
'src/modules/cava/cava_backend.cpp')
|
||||
man_files += files('man/waybar-cava.5.scd')
|
||||
|
||||
if eproxy.found()
|
||||
add_project_arguments('-DHAVE_LIBCAVAGLSL', language: 'cpp')
|
||||
src_files += files('src/modules/cava/cavaGLSL.cpp')
|
||||
endif
|
||||
endif
|
||||
|
||||
if libgps.found()
|
||||
@@ -554,6 +564,7 @@ executable(
|
||||
tz_dep,
|
||||
xkbregistry,
|
||||
cava,
|
||||
eproxy,
|
||||
libgps
|
||||
],
|
||||
include_directories: inc_dirs,
|
||||
|
||||
+6
-5
@@ -6,16 +6,17 @@
|
||||
}:
|
||||
let
|
||||
libcava = rec {
|
||||
version = "0.10.4";
|
||||
version = "0.10.7";
|
||||
src = pkgs.fetchFromGitHub {
|
||||
owner = "LukashonakV";
|
||||
repo = "cava";
|
||||
tag = version;
|
||||
hash = "sha256-9eTDqM+O1tA/3bEfd1apm8LbEcR9CVgELTIspSVPMKM=";
|
||||
# NOTE: Needs to match the cava.wrap
|
||||
tag = "${version}";
|
||||
hash = "sha256-zkyj1vBzHtoypX4Bxdh1Vmwh967DKKxN751v79hzmgQ=";
|
||||
};
|
||||
};
|
||||
in
|
||||
(waybar.overrideAttrs (oldAttrs: {
|
||||
waybar.overrideAttrs (oldAttrs: {
|
||||
inherit version;
|
||||
|
||||
src = lib.cleanSourceWith {
|
||||
@@ -43,4 +44,4 @@ in
|
||||
patchShebangs .
|
||||
popd
|
||||
'';
|
||||
}))
|
||||
})
|
||||
|
||||
@@ -128,7 +128,7 @@
|
||||
"critical-threshold": 80,
|
||||
// "format-critical": "{temperatureC}°C {icon}",
|
||||
"format": "{temperatureC}°C {icon}",
|
||||
"format-icons": ["", "", ""]
|
||||
"format-icons": ["", "", ""]
|
||||
},
|
||||
"backlight": {
|
||||
// "device": "acpi_video1",
|
||||
@@ -143,7 +143,7 @@
|
||||
},
|
||||
"format": "{capacity}% {icon}",
|
||||
"format-full": "{capacity}% {icon}",
|
||||
"format-charging": "{capacity}% ",
|
||||
"format-charging": "{capacity}% ",
|
||||
"format-plugged": "{capacity}% ",
|
||||
"format-alt": "{time} {icon}",
|
||||
// "format-good": "", // An empty format will hide the module
|
||||
@@ -167,9 +167,9 @@
|
||||
"network": {
|
||||
// "interface": "wlp2*", // (Optional) To force the use of this interface
|
||||
"format-wifi": "{essid} ({signalStrength}%) ",
|
||||
"format-ethernet": "{ipaddr}/{cidr} ",
|
||||
"tooltip-format": "{ifname} via {gwaddr} ",
|
||||
"format-linked": "{ifname} (No IP) ",
|
||||
"format-ethernet": "{ipaddr}/{cidr} ",
|
||||
"tooltip-format": "{ifname} via {gwaddr} ",
|
||||
"format-linked": "{ifname} (No IP) ",
|
||||
"format-disconnected": "Disconnected ⚠",
|
||||
"format-alt": "{ifname}: {ipaddr}/{cidr}"
|
||||
},
|
||||
@@ -177,14 +177,14 @@
|
||||
// "scroll-step": 1, // %, can be a float
|
||||
"format": "{volume}% {icon} {format_source}",
|
||||
"format-bluetooth": "{volume}% {icon} {format_source}",
|
||||
"format-bluetooth-muted": " {icon} {format_source}",
|
||||
"format-muted": " {format_source}",
|
||||
"format-bluetooth-muted": " {icon} {format_source}",
|
||||
"format-muted": " {format_source}",
|
||||
"format-source": "{volume}% ",
|
||||
"format-source-muted": "",
|
||||
"format-icons": {
|
||||
"headphone": "",
|
||||
"hands-free": "",
|
||||
"headset": "",
|
||||
"hands-free": "",
|
||||
"headset": "",
|
||||
"phone": "",
|
||||
"portable": "",
|
||||
"car": "",
|
||||
|
||||
@@ -112,17 +112,19 @@ class PlayerManager:
|
||||
logger.debug(f"Metadata changed for player {player.props.player_name}")
|
||||
player_name = player.props.player_name
|
||||
artist = player.get_artist()
|
||||
artist = artist.replace("&", "&")
|
||||
artist = artist and artist.replace("&", "&")
|
||||
title = player.get_title()
|
||||
title = title.replace("&", "&")
|
||||
title = title and title.replace("&", "&")
|
||||
|
||||
track_info = ""
|
||||
if player_name == "spotify" and "mpris:trackid" in metadata.keys() and ":ad:" in player.props.metadata["mpris:trackid"]:
|
||||
track_info = "Advertisement"
|
||||
elif artist is not None and title is not None:
|
||||
track_info = f"{artist} - {title}"
|
||||
else:
|
||||
elif title is not None:
|
||||
track_info = title
|
||||
elif artist is not None:
|
||||
track_info = artist
|
||||
|
||||
if track_info:
|
||||
if player.props.status == "Playing":
|
||||
|
||||
+11
-6
@@ -33,9 +33,7 @@ std::string toLowerCase(const std::string& input) {
|
||||
|
||||
std::optional<std::string> getFileBySuffix(const std::string& dir, const std::string& suffix,
|
||||
bool check_lower_case) {
|
||||
if (!std::filesystem::exists(dir)) {
|
||||
return {};
|
||||
}
|
||||
try {
|
||||
for (const auto& entry : std::filesystem::recursive_directory_iterator(dir)) {
|
||||
if (entry.is_regular_file()) {
|
||||
std::string filename = entry.path().filename().string();
|
||||
@@ -49,6 +47,8 @@ std::optional<std::string> getFileBySuffix(const std::string& dir, const std::st
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (const std::filesystem::filesystem_error&) {
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
@@ -151,19 +151,24 @@ void AAppIconLabel::updateAppIconName(const std::string& app_identifier,
|
||||
}
|
||||
|
||||
void AAppIconLabel::updateAppIcon() {
|
||||
if (update_app_icon_) {
|
||||
if (update_app_icon_ || (!iconEnabled() && image_.get_visible())) {
|
||||
update_app_icon_ = false;
|
||||
if (app_icon_name_.empty()) {
|
||||
image_.set_visible(false);
|
||||
} else if (app_icon_name_.front() == '/') {
|
||||
auto pixbuf = Gdk::Pixbuf::create_from_file(app_icon_name_);
|
||||
try {
|
||||
int scaled_icon_size = app_icon_size_ * image_.get_scale_factor();
|
||||
pixbuf = Gdk::Pixbuf::create_from_file(app_icon_name_, scaled_icon_size, scaled_icon_size);
|
||||
auto pixbuf =
|
||||
Gdk::Pixbuf::create_from_file(app_icon_name_, scaled_icon_size, scaled_icon_size);
|
||||
|
||||
auto surface = Gdk::Cairo::create_surface_from_pixbuf(pixbuf, image_.get_scale_factor(),
|
||||
image_.get_window());
|
||||
image_.set(surface);
|
||||
image_.set_visible(true);
|
||||
} catch (const Glib::Exception& e) {
|
||||
spdlog::warn("Failed to load app icon {}: {}", app_icon_name_, std::string(e.what()));
|
||||
image_.set_visible(false);
|
||||
}
|
||||
} else {
|
||||
image_.set_from_icon_name(app_icon_name_, Gtk::ICON_SIZE_INVALID);
|
||||
image_.set_visible(true);
|
||||
|
||||
+24
-7
@@ -17,12 +17,17 @@ ALabel::ALabel(const Json::Value& config, const std::string& name, const std::st
|
||||
config["format-alt"].isString() || config["menu"].isString() || enable_click,
|
||||
enable_scroll),
|
||||
format_(config_["format"].isString() ? config_["format"].asString() : format),
|
||||
|
||||
// Leave the default option outside of the std::max(1L, ...), because the zero value
|
||||
// (default) is used in modules/custom.cpp to make the difference between
|
||||
// two types of custom scripts. Fixes #4521.
|
||||
interval_(config_["interval"] == "once"
|
||||
? std::chrono::milliseconds::max()
|
||||
: std::chrono::milliseconds(
|
||||
std::max(1L, // Minimum 1ms due to millisecond precision
|
||||
static_cast<long>(
|
||||
(config_["interval"].isNumeric() ? config_["interval"].asDouble() : interval) * 1000)))),
|
||||
(config_["interval"].isNumeric()
|
||||
? std::max(1L, // Minimum 1ms due to millisecond precision
|
||||
static_cast<long>(config_["interval"].asDouble() * 1000))
|
||||
: 1000 * (long)interval))),
|
||||
default_format_(format_) {
|
||||
label_.set_name(name);
|
||||
if (!id.empty()) {
|
||||
@@ -86,13 +91,17 @@ ALabel::ALabel(const Json::Value& config, const std::string& name, const std::st
|
||||
|
||||
// Make the GtkBuilder and check for errors in his parsing
|
||||
if (gtk_builder_add_from_string(builder, fileContent.str().c_str(), -1, nullptr) == 0U) {
|
||||
g_object_unref(builder);
|
||||
throw std::runtime_error("Error found in the file " + menuFile);
|
||||
}
|
||||
|
||||
menu_ = gtk_builder_get_object(builder, "menu");
|
||||
if (menu_ == nullptr) {
|
||||
g_object_unref(builder);
|
||||
throw std::runtime_error("Failed to get 'menu' object from GtkBuilder");
|
||||
}
|
||||
// Keep the menu alive after dropping the transient GtkBuilder.
|
||||
g_object_ref(menu_);
|
||||
submenus_ = std::map<std::string, GtkMenuItem*>();
|
||||
menuActionsMap_ = std::map<std::string, std::string>();
|
||||
|
||||
@@ -100,11 +109,17 @@ ALabel::ALabel(const Json::Value& config, const std::string& name, const std::st
|
||||
for (Json::Value::const_iterator it = config_["menu-actions"].begin();
|
||||
it != config_["menu-actions"].end(); ++it) {
|
||||
std::string key = it.key().asString();
|
||||
submenus_[key] = GTK_MENU_ITEM(gtk_builder_get_object(builder, key.c_str()));
|
||||
auto* item = gtk_builder_get_object(builder, key.c_str());
|
||||
if (item == nullptr) {
|
||||
spdlog::warn("Menu item '{}' not found in builder file", key);
|
||||
continue;
|
||||
}
|
||||
submenus_[key] = GTK_MENU_ITEM(item);
|
||||
menuActionsMap_[key] = it->asString();
|
||||
g_signal_connect(submenus_[key], "activate", G_CALLBACK(handleGtkMenuEvent),
|
||||
(gpointer)menuActionsMap_[key].c_str());
|
||||
}
|
||||
g_object_unref(builder);
|
||||
} catch (std::runtime_error& e) {
|
||||
spdlog::warn("Error while creating the menu : {}. Menu popup not activated.", e.what());
|
||||
}
|
||||
@@ -136,7 +151,8 @@ std::string ALabel::getIcon(uint16_t percentage, const std::string& alt, uint16_
|
||||
if (format_icons.isArray()) {
|
||||
auto size = format_icons.size();
|
||||
if (size != 0U) {
|
||||
auto idx = std::clamp(percentage / ((max == 0 ? 100 : max) / size), 0U, size - 1);
|
||||
auto divisor = std::max(1U, (max == 0 ? 100U : static_cast<unsigned>(max)) / size);
|
||||
auto idx = std::clamp(percentage / divisor, 0U, size - 1);
|
||||
format_icons = format_icons[idx];
|
||||
}
|
||||
}
|
||||
@@ -162,7 +178,8 @@ std::string ALabel::getIcon(uint16_t percentage, const std::vector<std::string>&
|
||||
if (format_icons.isArray()) {
|
||||
auto size = format_icons.size();
|
||||
if (size != 0U) {
|
||||
auto idx = std::clamp(percentage / ((max == 0 ? 100 : max) / size), 0U, size - 1);
|
||||
auto divisor = std::max(1U, (max == 0 ? 100U : static_cast<unsigned>(max)) / size);
|
||||
auto idx = std::clamp(percentage / divisor, 0U, size - 1);
|
||||
format_icons = format_icons[idx];
|
||||
}
|
||||
}
|
||||
@@ -185,7 +202,7 @@ bool waybar::ALabel::handleToggle(GdkEventButton* const& e) {
|
||||
}
|
||||
|
||||
void ALabel::handleGtkMenuEvent(GtkMenuItem* /*menuitem*/, gpointer data) {
|
||||
waybar::util::command::res res = waybar::util::command::exec((char*)data, "GtkMenu");
|
||||
waybar::util::command::forkExec((char*)data, "GtkMenu");
|
||||
}
|
||||
|
||||
std::string ALabel::getState(uint8_t value, bool lesser) {
|
||||
|
||||
+20
-5
@@ -17,10 +17,14 @@ AModule::AModule(const Json::Value& config, const std::string& name, const std::
|
||||
isTooltip{config_["tooltip"].isBool() ? config_["tooltip"].asBool() : true},
|
||||
isExpand{config_["expand"].isBool() ? config_["expand"].asBool() : false},
|
||||
distance_scrolled_y_(0.0),
|
||||
distance_scrolled_x_(0.0) {
|
||||
distance_scrolled_x_(0.0),
|
||||
cursor_timeout_conn_() {
|
||||
// Configure module action Map
|
||||
const Json::Value actions{config_["actions"]};
|
||||
|
||||
disable_on_sleep_ =
|
||||
config_["disable-on-sleep"].isBool() ? config_["disable-on-sleep"].asBool() : false;
|
||||
|
||||
for (Json::Value::const_iterator it = actions.begin(); it != actions.end(); ++it) {
|
||||
if (it.key().isString() && it->isString())
|
||||
if (!eventActionMap_.contains(it.key().asString())) {
|
||||
@@ -72,8 +76,12 @@ AModule::AModule(const Json::Value& config, const std::string& name, const std::
|
||||
|
||||
// Respect user configuration of cursor
|
||||
if (config_.isMember("cursor")) {
|
||||
if (config_["cursor"].isBool() && config_["cursor"].asBool()) {
|
||||
if (config_["cursor"].isBool()) {
|
||||
if (config_["cursor"].asBool()) {
|
||||
setCursor(Gdk::HAND2);
|
||||
} else {
|
||||
setCursor(Gdk::ARROW);
|
||||
}
|
||||
} else if (config_["cursor"].isInt()) {
|
||||
setCursor(Gdk::CursorType(config_["cursor"].asInt()));
|
||||
} else {
|
||||
@@ -83,11 +91,18 @@ AModule::AModule(const Json::Value& config, const std::string& name, const std::
|
||||
}
|
||||
|
||||
AModule::~AModule() {
|
||||
if (cursor_timeout_conn_.connected()) {
|
||||
cursor_timeout_conn_.disconnect();
|
||||
}
|
||||
for (const auto& pid : pid_children_) {
|
||||
if (pid != -1) {
|
||||
killpg(pid, SIGTERM);
|
||||
}
|
||||
}
|
||||
if (menu_ != nullptr) {
|
||||
g_object_unref(menu_);
|
||||
menu_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
auto AModule::update() -> void {
|
||||
@@ -114,7 +129,7 @@ void AModule::setCursor(Gdk::CursorType const& c) {
|
||||
} else {
|
||||
// window may not be accessible yet, in this case,
|
||||
// schedule another call for setting the cursor in 1 sec
|
||||
Glib::signal_timeout().connect_seconds(
|
||||
cursor_timeout_conn_ = Glib::signal_timeout().connect_seconds(
|
||||
[this, c]() {
|
||||
setCursor(c);
|
||||
return false;
|
||||
@@ -166,9 +181,9 @@ bool AModule::handleUserEvent(GdkEventButton* const& e) {
|
||||
}
|
||||
|
||||
// Check that a menu has been configured
|
||||
if (config_["menu"].isString()) {
|
||||
if (rec != eventMap_.cend() && config_["menu"].isString()) {
|
||||
// Check if the event is the one specified for the "menu" option
|
||||
if (rec->second == config_["menu"].asString()) {
|
||||
if (rec->second == config_["menu"].asString() && menu_ != nullptr) {
|
||||
// Popup the menu
|
||||
gtk_widget_show_all(GTK_WIDGET(menu_));
|
||||
gtk_menu_popup_at_pointer(GTK_MENU(menu_), reinterpret_cast<GdkEvent*>(e));
|
||||
|
||||
+37
-5
@@ -229,7 +229,8 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config)
|
||||
gtk_layer_init_for_window(gtk_window);
|
||||
gtk_layer_set_keyboard_mode(gtk_window, GTK_LAYER_SHELL_KEYBOARD_MODE_NONE);
|
||||
gtk_layer_set_monitor(gtk_window, output->monitor->gobj());
|
||||
gtk_layer_set_namespace(gtk_window, "waybar");
|
||||
gtk_layer_set_namespace(gtk_window,
|
||||
config["name"].isString() ? config["name"].asCString() : "waybar");
|
||||
|
||||
gtk_layer_set_margin(gtk_window, GTK_LAYER_SHELL_EDGE_LEFT, margins_.left);
|
||||
gtk_layer_set_margin(gtk_window, GTK_LAYER_SHELL_EDGE_RIGHT, margins_.right);
|
||||
@@ -262,6 +263,16 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config)
|
||||
|
||||
window.signal_map_event().connect_notify(sigc::mem_fun(*this, &Bar::onMap));
|
||||
|
||||
window.signal_unmap().connect([this]() {
|
||||
spdlog::debug("Output {} unmapped (DPMS off), suspending modules", output->name);
|
||||
toggleSuspend(true);
|
||||
});
|
||||
|
||||
window.signal_map().connect([this]() {
|
||||
spdlog::debug("Output {} mapped (DPMS on), resuming modules", output->name);
|
||||
toggleSuspend(false);
|
||||
});
|
||||
|
||||
#if HAVE_SWAY
|
||||
if (auto ipc = config["ipc"]; ipc.isBool() && ipc.asBool()) {
|
||||
bar_id = Client::inst()->bar_id;
|
||||
@@ -540,13 +551,15 @@ void waybar::Bar::getModules(const Factory& factory, const std::string& pos,
|
||||
auto vertical = (group != nullptr ? group->getBox().get_orientation()
|
||||
: box_.get_orientation()) == Gtk::ORIENTATION_VERTICAL;
|
||||
|
||||
auto group_config = config[ref];
|
||||
const Json::Value& group_config = config[ref];
|
||||
if (group_config["modules"].isNull()) {
|
||||
spdlog::warn("Group definition '{}' has not been found, group will be hidden", ref);
|
||||
}
|
||||
auto* group_module = new waybar::Group(id_name, class_name, group_config, vertical);
|
||||
getModules(factory, ref, group_module);
|
||||
module = group_module;
|
||||
auto group_module =
|
||||
std::make_unique<waybar::Group>(id_name, class_name, group_config, vertical);
|
||||
|
||||
getModules(factory, ref, group_module.get());
|
||||
module = group_module.release();
|
||||
} else {
|
||||
module = factory.makeModule(ref, pos);
|
||||
}
|
||||
@@ -693,3 +706,22 @@ void waybar::Bar::configureGlobalOffset(int width, int height) {
|
||||
void waybar::Bar::onOutputGeometryChanged() {
|
||||
configureGlobalOffset(window.get_width(), window.get_height());
|
||||
}
|
||||
|
||||
void waybar::Bar::toggleSuspend(bool suspend) {
|
||||
auto process_modules = [suspend](Gtk::Box& module_box) {
|
||||
for (auto* widget : module_box.get_children()) {
|
||||
auto* module = dynamic_cast<waybar::AModule*>(widget);
|
||||
if (module && module->shouldSuspend()) {
|
||||
if (suspend) {
|
||||
module->suspend();
|
||||
} else {
|
||||
module->resume();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
process_modules(left_);
|
||||
process_modules(center_);
|
||||
process_modules(right_);
|
||||
}
|
||||
|
||||
+70
-13
@@ -10,6 +10,7 @@
|
||||
#include "idle-inhibit-unstable-v1-client-protocol.h"
|
||||
#include "util/clara.hpp"
|
||||
#include "util/format.hpp"
|
||||
#include "util/hex_checker.hpp"
|
||||
|
||||
waybar::Client* waybar::Client::inst() {
|
||||
static auto* c = new Client();
|
||||
@@ -19,11 +20,23 @@ waybar::Client *waybar::Client::inst() {
|
||||
void waybar::Client::handleGlobal(void* data, struct wl_registry* registry, uint32_t name,
|
||||
const char* interface, uint32_t version) {
|
||||
auto* client = static_cast<Client*>(data);
|
||||
|
||||
if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0 &&
|
||||
version >= ZXDG_OUTPUT_V1_NAME_SINCE_VERSION) {
|
||||
if (client->xdg_output_manager != nullptr) {
|
||||
zxdg_output_manager_v1_destroy(client->xdg_output_manager);
|
||||
client->xdg_output_manager = nullptr;
|
||||
}
|
||||
|
||||
client->xdg_output_manager = static_cast<struct zxdg_output_manager_v1*>(wl_registry_bind(
|
||||
registry, name, &zxdg_output_manager_v1_interface, ZXDG_OUTPUT_V1_NAME_SINCE_VERSION));
|
||||
|
||||
} else if (strcmp(interface, zwp_idle_inhibit_manager_v1_interface.name) == 0) {
|
||||
if (client->idle_inhibit_manager != nullptr) {
|
||||
zwp_idle_inhibit_manager_v1_destroy(client->idle_inhibit_manager);
|
||||
client->idle_inhibit_manager = nullptr;
|
||||
}
|
||||
|
||||
client->idle_inhibit_manager = static_cast<struct zwp_idle_inhibit_manager_v1*>(
|
||||
wl_registry_bind(registry, name, &zwp_idle_inhibit_manager_v1_interface, 1));
|
||||
}
|
||||
@@ -78,11 +91,14 @@ void waybar::Client::handleOutputDone(void *data, struct zxdg_output_v1 * /*xdg_
|
||||
output.xdg_output.reset();
|
||||
spdlog::debug("Output detection done: {} ({})", output.name, output.identifier);
|
||||
|
||||
auto configs = client->getOutputConfigs(output);
|
||||
if (!configs.empty()) {
|
||||
for (const auto &config : configs) {
|
||||
client->bars.emplace_back(std::make_unique<Bar>(&output, config));
|
||||
}
|
||||
client->pending_outputs_.push_back(&output);
|
||||
|
||||
if (!client->bars_scheduled_) {
|
||||
client->bars_scheduled_ = true;
|
||||
|
||||
Glib::signal_idle().connect_once([client]() {
|
||||
client->createBarsBatch();
|
||||
}, Glib::PRIORITY_HIGH_IDLE);
|
||||
}
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
@@ -90,6 +106,25 @@ void waybar::Client::handleOutputDone(void *data, struct zxdg_output_v1 * /*xdg_
|
||||
}
|
||||
}
|
||||
|
||||
void waybar::Client::createBarsBatch() {
|
||||
pending_outputs_.remove_if([this](auto* output) { return std::none_of(outputs_.begin(), outputs_.end(), [&output](const auto& o) { return &o == output; }); });
|
||||
for (auto* output : pending_outputs_) {
|
||||
try {
|
||||
auto configs = getOutputConfigs(*output);
|
||||
if (!configs.empty()) {
|
||||
for (const auto& config : configs) {
|
||||
bars.emplace_back(std::make_unique<Bar>(output, config));
|
||||
}
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
spdlog::warn("Error creating bar: {}", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
pending_outputs_.clear();
|
||||
bars_scheduled_ = false;
|
||||
}
|
||||
|
||||
void waybar::Client::handleOutputName(void* data, struct zxdg_output_v1* /*xdg_output*/,
|
||||
const char* name) {
|
||||
auto* client = waybar::Client::inst();
|
||||
@@ -183,15 +218,27 @@ const std::string waybar::Client::getStyle(const std::string &style,
|
||||
};
|
||||
|
||||
auto waybar::Client::setupCss(const std::string& css_file) -> void {
|
||||
css_provider_ = Gtk::CssProvider::create();
|
||||
style_context_ = Gtk::StyleContext::create();
|
||||
auto screen = Gdk::Screen::get_default();
|
||||
if (!screen) {
|
||||
throw std::runtime_error("No default screen");
|
||||
}
|
||||
|
||||
// Load our css file, wherever that may be hiding
|
||||
if (css_provider_) {
|
||||
Gtk::StyleContext::remove_provider_for_screen(screen, css_provider_);
|
||||
css_provider_.reset();
|
||||
}
|
||||
|
||||
css_provider_ = Gtk::CssProvider::create();
|
||||
auto [modified_css, was_transformed] = transform_8bit_to_hex(css_file);
|
||||
if (was_transformed) {
|
||||
css_provider_->load_from_data(modified_css);
|
||||
} else {
|
||||
if (!css_provider_->load_from_path(css_file)) {
|
||||
css_provider_.reset();
|
||||
throw std::runtime_error("Can't open style file");
|
||||
}
|
||||
// there's always only one screen
|
||||
style_context_->add_provider_for_screen(Gdk::Screen::get_default(), css_provider_,
|
||||
}
|
||||
Gtk::StyleContext::add_provider_for_screen(screen, css_provider_,
|
||||
GTK_STYLE_PROVIDER_PRIORITY_USER);
|
||||
}
|
||||
|
||||
@@ -211,13 +258,22 @@ void waybar::Client::bindInterfaces() {
|
||||
if (xdg_output_manager == nullptr) {
|
||||
throw std::runtime_error("Failed to acquire required resources.");
|
||||
}
|
||||
|
||||
// Disconnect previous signal handlers to prevent duplicate handlers on reload
|
||||
monitor_added_connection_.disconnect();
|
||||
monitor_removed_connection_.disconnect();
|
||||
|
||||
// Clear stale outputs from previous run
|
||||
outputs_.clear();
|
||||
|
||||
// add existing outputs and subscribe to updates
|
||||
for (auto i = 0; i < gdk_display->get_n_monitors(); ++i) {
|
||||
auto monitor = gdk_display->get_monitor(i);
|
||||
handleMonitorAdded(monitor);
|
||||
}
|
||||
gdk_display->signal_monitor_added().connect(sigc::mem_fun(*this, &Client::handleMonitorAdded));
|
||||
gdk_display->signal_monitor_removed().connect(
|
||||
monitor_added_connection_ = gdk_display->signal_monitor_added().connect(
|
||||
sigc::mem_fun(*this, &Client::handleMonitorAdded));
|
||||
monitor_removed_connection_ = gdk_display->signal_monitor_removed().connect(
|
||||
sigc::mem_fun(*this, &Client::handleMonitorRemoved));
|
||||
}
|
||||
|
||||
@@ -272,9 +328,10 @@ int waybar::Client::main(int argc, char *argv[]) {
|
||||
}
|
||||
m_cssFile = getStyle(style_opt);
|
||||
setupCss(m_cssFile);
|
||||
m_cssReloadHelper = std::make_unique<CssReloadHelper>(m_cssFile, [&]() { setupCss(m_cssFile); });
|
||||
m_cssReloadHelper = std::make_unique<CssReloadHelper>(m_cssFile, [&](const std::string& css_file) { setupCss(css_file); });
|
||||
portal->signal_appearance_changed().connect([&](waybar::Appearance appearance) {
|
||||
auto css_file = getStyle(style_opt, appearance);
|
||||
m_cssReloadHelper->changeCssFile(css_file);
|
||||
setupCss(css_file);
|
||||
});
|
||||
|
||||
|
||||
+2
-6
@@ -108,15 +108,13 @@
|
||||
#ifdef HAVE_LIBWIREPLUMBER
|
||||
#include "modules/wireplumber.hpp"
|
||||
#endif
|
||||
#ifdef HAVE_LIBCAVA
|
||||
#include "modules/cava/cava.hpp"
|
||||
#endif
|
||||
#ifdef HAVE_SYSTEMD_MONITOR
|
||||
#include "modules/systemd_failed_units.hpp"
|
||||
#endif
|
||||
#ifdef HAVE_LIBGPS
|
||||
#include "modules/gps.hpp"
|
||||
#endif
|
||||
#include "modules/cava/cava_frontend.hpp"
|
||||
#include "modules/cffi.hpp"
|
||||
#include "modules/custom.hpp"
|
||||
#include "modules/image.hpp"
|
||||
@@ -341,11 +339,9 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name,
|
||||
return new waybar::modules::Wireplumber(id, config_[name]);
|
||||
}
|
||||
#endif
|
||||
#ifdef HAVE_LIBCAVA
|
||||
if (ref == "cava") {
|
||||
return new waybar::modules::cava::Cava(id, config_[name]);
|
||||
return waybar::modules::cava::getModule(id, config_[name]);
|
||||
}
|
||||
#endif
|
||||
#ifdef HAVE_SYSTEMD_MONITOR
|
||||
if (ref == "systemd-failed-units") {
|
||||
return new waybar::modules::SystemdFailedUnits(id, config_[name]);
|
||||
|
||||
+15
-2
@@ -26,7 +26,7 @@ Gtk::RevealerTransitionType getPreferredTransitionType(bool is_vertical) {
|
||||
|
||||
Group::Group(const std::string& name, const std::string& id, const Json::Value& config,
|
||||
bool vertical)
|
||||
: AModule(config, name, id, true, true),
|
||||
: AModule(config, name, id, true, false),
|
||||
box{vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0},
|
||||
revealer_box{vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0} {
|
||||
box.set_name(name_);
|
||||
@@ -64,11 +64,19 @@ Group::Group(const std::string& name, const std::string& id, const Json::Value&
|
||||
: true);
|
||||
click_to_reveal = drawer_config["click-to-reveal"].asBool();
|
||||
|
||||
const bool start_expanded =
|
||||
(drawer_config["start-expanded"].isBool() ? drawer_config["start-expanded"].asBool()
|
||||
: false);
|
||||
|
||||
auto transition_type = getPreferredTransitionType(vertical);
|
||||
|
||||
revealer.set_transition_type(transition_type);
|
||||
revealer.set_transition_duration(transition_duration);
|
||||
revealer.set_reveal_child(false);
|
||||
revealer.set_reveal_child(start_expanded);
|
||||
|
||||
if (start_expanded) {
|
||||
box.set_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT);
|
||||
}
|
||||
|
||||
revealer.get_style_context()->add_class("drawer");
|
||||
|
||||
@@ -124,6 +132,11 @@ auto Group::update() -> void {
|
||||
// noop
|
||||
}
|
||||
|
||||
bool Group::handleScroll(GdkEventScroll* e) {
|
||||
// no scroll.
|
||||
return true;
|
||||
}
|
||||
|
||||
Gtk::Box& Group::getBox() { return is_drawer ? (is_first_widget ? box : revealer_box) : box; }
|
||||
|
||||
void Group::addWidget(Gtk::Widget& widget) {
|
||||
|
||||
+13
-5
@@ -33,7 +33,10 @@ static void writeSignalToPipe(int signum) {
|
||||
// to `signal_handler`.
|
||||
static void catchSignals(waybar::SafeSignal<int>& signal_handler) {
|
||||
int fd[2];
|
||||
pipe(fd);
|
||||
if (pipe(fd) != 0) {
|
||||
spdlog::error("Failed to create signal pipe: {}", strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
int signal_pipe_read_fd = fd[0];
|
||||
signal_pipe_write_fd = fd[1];
|
||||
@@ -52,9 +55,11 @@ static void catchSignals(waybar::SafeSignal<int>& signal_handler) {
|
||||
std::signal(SIGINT, writeSignalToPipe);
|
||||
std::signal(SIGCHLD, writeSignalToPipe);
|
||||
|
||||
#ifdef SIGRTMIN
|
||||
for (int sig = SIGRTMIN + 1; sig <= SIGRTMAX; ++sig) {
|
||||
std::signal(sig, writeSignalToPipe);
|
||||
}
|
||||
#endif
|
||||
|
||||
while (true) {
|
||||
int signum;
|
||||
@@ -116,12 +121,14 @@ void handleUserSignal(int signal, bool& reload) {
|
||||
// If this signal should restart or close the bar, this function will write
|
||||
// `true` or `false`, respectively, into `reload`.
|
||||
static void handleSignalMainThread(int signum, bool& reload) {
|
||||
#ifdef SIGRTMIN
|
||||
if (signum >= SIGRTMIN + 1 && signum <= SIGRTMAX) {
|
||||
for (auto& bar : waybar::Client::inst()->bars) {
|
||||
bar->handleSignal(signum);
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
switch (signum) {
|
||||
case SIGUSR1:
|
||||
@@ -137,15 +144,16 @@ static void handleSignalMainThread(int signum, bool& reload) {
|
||||
break;
|
||||
case SIGCHLD:
|
||||
spdlog::debug("Received SIGCHLD in signalThread");
|
||||
if (!reap.empty()) {
|
||||
reap_mtx.lock();
|
||||
for (auto it = reap.begin(); it != reap.end(); ++it) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(reap_mtx);
|
||||
for (auto it = reap.begin(); it != reap.end();) {
|
||||
if (waitpid(*it, nullptr, WNOHANG) == *it) {
|
||||
spdlog::debug("Reaped child with PID: {}", *it);
|
||||
it = reap.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
reap_mtx.unlock();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -58,11 +58,11 @@ auto waybar::modules::Backlight::update() -> void {
|
||||
tooltip_format = config_["tooltip-format"].asString();
|
||||
}
|
||||
if (!tooltip_format.empty()) {
|
||||
label_.set_tooltip_text(fmt::format(fmt::runtime(tooltip_format),
|
||||
label_.set_tooltip_markup(fmt::format(fmt::runtime(tooltip_format),
|
||||
fmt::arg("percent", percent),
|
||||
fmt::arg("icon", getIcon(percent))));
|
||||
} else {
|
||||
label_.set_tooltip_text(desc);
|
||||
label_.set_tooltip_markup(desc);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
+109
-75
@@ -7,7 +7,10 @@
|
||||
#if defined(__FreeBSD__)
|
||||
#include <sys/sysctl.h>
|
||||
#endif
|
||||
#include <libudev.h>
|
||||
#include <poll.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <sys/signalfd.h>
|
||||
|
||||
waybar::modules::Battery::Battery(const std::string& id, const Bar& bar, const Json::Value& config)
|
||||
: ALabel(config, "battery", id, "{capacity}%", 60), last_event_(""), bar_(bar) {
|
||||
@@ -16,17 +19,21 @@ waybar::modules::Battery::Battery(const std::string& id, const Bar& bar, const J
|
||||
if (battery_watch_fd_ == -1) {
|
||||
throw std::runtime_error("Unable to listen batteries.");
|
||||
}
|
||||
|
||||
global_watch_fd_ = inotify_init1(IN_CLOEXEC);
|
||||
if (global_watch_fd_ == -1) {
|
||||
throw std::runtime_error("Unable to listen batteries.");
|
||||
udev_ = std::unique_ptr<udev, util::UdevDeleter>(udev_new());
|
||||
if (udev_ == nullptr) {
|
||||
throw std::runtime_error("udev_new failed");
|
||||
}
|
||||
|
||||
// Watch the directory for any added or removed batteries
|
||||
global_watch = inotify_add_watch(global_watch_fd_, data_dir_.c_str(), IN_CREATE | IN_DELETE);
|
||||
if (global_watch < 0) {
|
||||
throw std::runtime_error("Could not watch for battery plug/unplug");
|
||||
mon_ = std::unique_ptr<udev_monitor, util::UdevMonitorDeleter>(
|
||||
udev_monitor_new_from_netlink(udev_.get(), "kernel"));
|
||||
if (mon_ == nullptr) {
|
||||
throw std::runtime_error("udev monitor new failed");
|
||||
}
|
||||
if (udev_monitor_filter_add_match_subsystem_devtype(mon_.get(), "power_supply", nullptr) < 0) {
|
||||
throw std::runtime_error("udev failed to add monitor filter");
|
||||
}
|
||||
udev_monitor_enable_receiving(mon_.get());
|
||||
|
||||
if (config_["weighted-average"].isBool()) weightedAverage_ = config_["weighted-average"].asBool();
|
||||
#endif
|
||||
spdlog::debug("battery: worker interval is {}", interval_.count());
|
||||
worker();
|
||||
@@ -36,11 +43,6 @@ waybar::modules::Battery::~Battery() {
|
||||
#if defined(__linux__)
|
||||
std::lock_guard<std::mutex> guard(battery_list_mutex_);
|
||||
|
||||
if (global_watch >= 0) {
|
||||
inotify_rm_watch(global_watch_fd_, global_watch);
|
||||
}
|
||||
close(global_watch_fd_);
|
||||
|
||||
for (auto it = batteries_.cbegin(), next_it = it; it != batteries_.cend(); it = next_it) {
|
||||
++next_it;
|
||||
auto watch_id = (*it).second;
|
||||
@@ -77,12 +79,18 @@ void waybar::modules::Battery::worker() {
|
||||
dp.emit();
|
||||
};
|
||||
thread_battery_update_ = [this] {
|
||||
struct inotify_event event = {0};
|
||||
int nbytes = read(global_watch_fd_, &event, sizeof(event));
|
||||
if (nbytes != sizeof(event) || event.mask & IN_IGNORED) {
|
||||
poll_fds_[0].revents = 0;
|
||||
poll_fds_[0].events = POLLIN;
|
||||
poll_fds_[0].fd = udev_monitor_get_fd(mon_.get());
|
||||
int ret = poll(poll_fds_.data(), poll_fds_.size(), -1);
|
||||
if (ret < 0) {
|
||||
thread_.stop();
|
||||
return;
|
||||
}
|
||||
if ((poll_fds_[0].revents & POLLIN) != 0) {
|
||||
signalfd_siginfo signal_info;
|
||||
read(poll_fds_[0].fd, &signal_info, sizeof(signal_info));
|
||||
}
|
||||
refreshBatteries();
|
||||
dp.emit();
|
||||
};
|
||||
@@ -109,17 +117,16 @@ void waybar::modules::Battery::refreshBatteries() {
|
||||
if (((bat_defined && dir_name == config_["bat"].asString()) || !bat_defined) &&
|
||||
(fs::exists(node.path() / "capacity") || fs::exists(node.path() / "charge_now")) &&
|
||||
fs::exists(node.path() / "uevent") &&
|
||||
(fs::exists(node.path() / "status") || bat_compatibility) &&
|
||||
fs::exists(node.path() / "type")) {
|
||||
(fs::exists(node.path() / "status") || bat_compatibility)) {
|
||||
std::string type;
|
||||
std::ifstream(node.path() / "type") >> type;
|
||||
|
||||
if (!type.compare("Battery")) {
|
||||
if (std::ifstream{node.path() / "type"} >> type && !type.compare("Battery")) {
|
||||
// Ignore non-system power supplies unless explicitly requested
|
||||
if (!bat_defined && fs::exists(node.path() / "scope")) {
|
||||
if (!bat_defined) {
|
||||
std::string scope;
|
||||
std::ifstream(node.path() / "scope") >> scope;
|
||||
if (g_ascii_strcasecmp(scope.data(), "device") == 0) {
|
||||
// for hotplug-in device, access it is always unstable because you may remove the
|
||||
// device anytime so just allow failure happen and do nothing
|
||||
if (std::ifstream{node.path() / "scope"} >> scope &&
|
||||
g_ascii_strcasecmp(scope.data(), "device") == 0) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -131,7 +138,9 @@ void waybar::modules::Battery::refreshBatteries() {
|
||||
auto event_path = (node.path() / "uevent");
|
||||
auto wd = inotify_add_watch(battery_watch_fd_, event_path.c_str(), IN_ACCESS);
|
||||
if (wd < 0) {
|
||||
throw std::runtime_error("Could not watch events for " + node.path().string());
|
||||
spdlog::warn("Could not watch events for {} (device may have been removed)",
|
||||
node.path().string());
|
||||
continue;
|
||||
}
|
||||
batteries_[node.path()] = wd;
|
||||
}
|
||||
@@ -144,7 +153,7 @@ void waybar::modules::Battery::refreshBatteries() {
|
||||
}
|
||||
}
|
||||
} catch (fs::filesystem_error& e) {
|
||||
throw std::runtime_error(e.what());
|
||||
spdlog::warn("Battery directory tracking failed: {}", e.what());
|
||||
}
|
||||
if (warnFirstTime_ && batteries_.empty()) {
|
||||
if (config_["bat"].isString()) {
|
||||
@@ -265,11 +274,11 @@ waybar::modules::Battery::getInfos() {
|
||||
auto bat = item.first;
|
||||
std::string _status;
|
||||
|
||||
/* Check for adapter status if battery is not available */
|
||||
if (!std::ifstream(bat / "status")) {
|
||||
{
|
||||
std::ifstream f{bat / "status"};
|
||||
if (!std::getline(f, _status)) {
|
||||
std::getline(std::ifstream(adapter_ / "status"), _status);
|
||||
} else {
|
||||
std::getline(std::ifstream(bat / "status"), _status);
|
||||
}
|
||||
}
|
||||
|
||||
// Some battery will report current and charge in μA/μAh.
|
||||
@@ -278,64 +287,74 @@ waybar::modules::Battery::getInfos() {
|
||||
uint32_t current_now = 0;
|
||||
int32_t _current_now_int = 0;
|
||||
bool current_now_exists = false;
|
||||
if (fs::exists(bat / "current_now")) {
|
||||
if (std::ifstream current_now_f{bat / "current_now"}) {
|
||||
if (current_now_f >> _current_now_int) {
|
||||
current_now_exists = true;
|
||||
std::ifstream(bat / "current_now") >> _current_now_int;
|
||||
} else if (fs::exists(bat / "current_avg")) {
|
||||
}
|
||||
} else if (std::ifstream current_avg_f{bat / "current_avg"}) {
|
||||
if (current_avg_f >> _current_now_int) {
|
||||
current_now_exists = true;
|
||||
std::ifstream(bat / "current_avg") >> _current_now_int;
|
||||
}
|
||||
}
|
||||
// Documentation ABI allows a negative value when discharging, positive
|
||||
// value when charging.
|
||||
current_now = std::abs(_current_now_int);
|
||||
|
||||
if (fs::exists(bat / "time_to_empty_now")) {
|
||||
if (std::ifstream f{bat / "time_to_empty_now"}) {
|
||||
if (f >> time_to_empty_now) {
|
||||
time_to_empty_now_exists = true;
|
||||
std::ifstream(bat / "time_to_empty_now") >> time_to_empty_now;
|
||||
}
|
||||
}
|
||||
|
||||
if (fs::exists(bat / "time_to_full_now")) {
|
||||
if (std::ifstream f{bat / "time_to_full_now"}) {
|
||||
if (f >> time_to_full_now) {
|
||||
time_to_full_now_exists = true;
|
||||
std::ifstream(bat / "time_to_full_now") >> time_to_full_now;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t voltage_now = 0;
|
||||
bool voltage_now_exists = false;
|
||||
if (fs::exists(bat / "voltage_now")) {
|
||||
if (std::ifstream voltage_now_f{bat / "voltage_now"}) {
|
||||
if (voltage_now_f >> voltage_now) {
|
||||
voltage_now_exists = true;
|
||||
std::ifstream(bat / "voltage_now") >> voltage_now;
|
||||
} else if (fs::exists(bat / "voltage_avg")) {
|
||||
}
|
||||
} else if (std::ifstream voltage_avg_f{bat / "voltage_avg"}) {
|
||||
if (voltage_avg_f >> voltage_now) {
|
||||
voltage_now_exists = true;
|
||||
std::ifstream(bat / "voltage_avg") >> voltage_now;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t charge_full = 0;
|
||||
bool charge_full_exists = false;
|
||||
if (fs::exists(bat / "charge_full")) {
|
||||
if (std::ifstream f{bat / "charge_full"}) {
|
||||
if (f >> charge_full) {
|
||||
charge_full_exists = true;
|
||||
std::ifstream(bat / "charge_full") >> charge_full;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t charge_full_design = 0;
|
||||
bool charge_full_design_exists = false;
|
||||
if (fs::exists(bat / "charge_full_design")) {
|
||||
if (std::ifstream f{bat / "charge_full_design"}) {
|
||||
if (f >> charge_full_design) {
|
||||
charge_full_design_exists = true;
|
||||
std::ifstream(bat / "charge_full_design") >> charge_full_design;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t charge_now = 0;
|
||||
bool charge_now_exists = false;
|
||||
if (fs::exists(bat / "charge_now")) {
|
||||
if (std::ifstream f{bat / "charge_now"}) {
|
||||
if (f >> charge_now) {
|
||||
charge_now_exists = true;
|
||||
std::ifstream(bat / "charge_now") >> charge_now;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t power_now = 0;
|
||||
int32_t _power_now_int = 0;
|
||||
bool power_now_exists = false;
|
||||
if (fs::exists(bat / "power_now")) {
|
||||
if (std::ifstream f{bat / "power_now"}) {
|
||||
if (f >> _power_now_int) {
|
||||
power_now_exists = true;
|
||||
std::ifstream(bat / "power_now") >> _power_now_int;
|
||||
}
|
||||
}
|
||||
// Some drivers (example: Qualcomm) exposes use a negative value when
|
||||
// discharging, positive value when charging.
|
||||
@@ -343,28 +362,31 @@ waybar::modules::Battery::getInfos() {
|
||||
|
||||
uint32_t energy_now = 0;
|
||||
bool energy_now_exists = false;
|
||||
if (fs::exists(bat / "energy_now")) {
|
||||
if (std::ifstream f{bat / "energy_now"}) {
|
||||
if (f >> energy_now) {
|
||||
energy_now_exists = true;
|
||||
std::ifstream(bat / "energy_now") >> energy_now;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t energy_full = 0;
|
||||
bool energy_full_exists = false;
|
||||
if (fs::exists(bat / "energy_full")) {
|
||||
if (std::ifstream f{bat / "energy_full"}) {
|
||||
if (f >> energy_full) {
|
||||
energy_full_exists = true;
|
||||
std::ifstream(bat / "energy_full") >> energy_full;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t energy_full_design = 0;
|
||||
bool energy_full_design_exists = false;
|
||||
if (fs::exists(bat / "energy_full_design")) {
|
||||
if (std::ifstream f{bat / "energy_full_design"}) {
|
||||
if (f >> energy_full_design) {
|
||||
energy_full_design_exists = true;
|
||||
std::ifstream(bat / "energy_full_design") >> energy_full_design;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t cycleCount = 0;
|
||||
if (fs::exists(bat / "cycle_count")) {
|
||||
std::ifstream(bat / "cycle_count") >> cycleCount;
|
||||
if (std::ifstream f{bat / "cycle_count"}) {
|
||||
f >> cycleCount;
|
||||
}
|
||||
if (charge_full_design >= largestDesignCapacity) {
|
||||
largestDesignCapacity = charge_full_design;
|
||||
@@ -394,9 +416,10 @@ waybar::modules::Battery::getInfos() {
|
||||
} else if (energy_now_exists && energy_full_exists && energy_full != 0) {
|
||||
capacity_exists = true;
|
||||
capacity = 100 * (uint64_t)energy_now / (uint64_t)energy_full;
|
||||
} else if (fs::exists(bat / "capacity")) {
|
||||
} else if (std::ifstream f{bat / "capacity"}) {
|
||||
if (f >> capacity) {
|
||||
capacity_exists = true;
|
||||
std::ifstream(bat / "capacity") >> capacity;
|
||||
}
|
||||
}
|
||||
|
||||
if (!voltage_now_exists) {
|
||||
@@ -576,17 +599,17 @@ waybar::modules::Battery::getInfos() {
|
||||
|
||||
float calculated_capacity{0.0f};
|
||||
if (total_capacity_exists) {
|
||||
if (total_capacity > 0.0f)
|
||||
if (total_capacity > 0.0f) {
|
||||
calculated_capacity = (float)total_capacity / batteries_.size();
|
||||
else if (total_energy_full_exists && total_energy_exists) {
|
||||
if (total_energy_full > 0.0f)
|
||||
} else if (total_energy_full_exists && total_energy_exists) {
|
||||
if (total_energy_full > 0.0f) {
|
||||
calculated_capacity = ((float)total_energy * 100.0f / (float)total_energy_full);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle weighted-average
|
||||
if ((config_["weighted-average"].isBool() ? config_["weighted-average"].asBool() : false) &&
|
||||
total_energy_exists && total_energy_full_exists) {
|
||||
if (weightedAverage_ && total_energy_exists && total_energy_full_exists) {
|
||||
if (total_energy_full > 0.0f)
|
||||
calculated_capacity = ((float)total_energy * 100.0f / (float)total_energy_full);
|
||||
}
|
||||
@@ -678,12 +701,20 @@ auto waybar::modules::Battery::update() -> void {
|
||||
if (status == "Unknown") {
|
||||
status = getAdapterStatus(capacity);
|
||||
}
|
||||
auto state = getState(capacity, true);
|
||||
bool adapter_online = false;
|
||||
if (!adapter_.empty()) {
|
||||
std::ifstream(adapter_ / "online") >> adapter_online;
|
||||
}
|
||||
if (config_["full-at-plugged"].asBool() && status == "Full" && adapter_online) {
|
||||
status = "Plugged";
|
||||
}
|
||||
auto status_pretty = status;
|
||||
|
||||
// Transform to lowercase and replace space with dash
|
||||
std::ranges::transform(status.begin(), status.end(), status.begin(),
|
||||
[](char ch) { return ch == ' ' ? '-' : std::tolower(ch); });
|
||||
auto format = format_;
|
||||
auto state = getState(capacity, true);
|
||||
processEvents(state, status, capacity);
|
||||
setBarClass(state);
|
||||
auto time_remaining_formatted = formatTimeRemaining(time_remaining);
|
||||
@@ -782,16 +813,19 @@ void waybar::modules::Battery::processEvents(std::string& state, std::string& st
|
||||
if (!events.isObject() || events.empty()) {
|
||||
return;
|
||||
}
|
||||
std::string event_name = fmt::format("on-{}-{}", status == "discharging" ? status : "charging",
|
||||
state.empty() ? std::to_string(capacity) : state);
|
||||
auto exec = [](Json::Value const& event) {
|
||||
if (!event.isString()) return;
|
||||
if (auto command = event.asString(); !command.empty()) {
|
||||
util::command::exec(command, "");
|
||||
}
|
||||
};
|
||||
std::string status_name = status == "discharging" ? "on-discharging" : "on-charging";
|
||||
std::string event_name = status_name + '-' + (state.empty() ? std::to_string(capacity) : state);
|
||||
if (last_event_ != event_name) {
|
||||
spdlog::debug("battery: triggering event {}", event_name);
|
||||
if (events[event_name].isString()) {
|
||||
std::string exec = events[event_name].asString();
|
||||
// Execute the command if it is not empty
|
||||
if (!exec.empty()) {
|
||||
util::command::exec(exec, "");
|
||||
}
|
||||
exec(events[event_name]);
|
||||
if (!last_event_.empty() && last_event_[3] != event_name[3]) {
|
||||
exec(events[status_name]);
|
||||
}
|
||||
last_event_ = event_name;
|
||||
}
|
||||
|
||||
@@ -111,13 +111,16 @@ waybar::modules::Bluetooth::Bluetooth(const std::string& id, const Json::Value&
|
||||
findConnectedDevices(cur_controller_->path, connected_devices_);
|
||||
}
|
||||
|
||||
if (manager_) {
|
||||
g_signal_connect(manager_.get(), "object-added", G_CALLBACK(onObjectAdded), this);
|
||||
g_signal_connect(manager_.get(), "object-removed", G_CALLBACK(onObjectRemoved), this);
|
||||
g_signal_connect(manager_.get(), "interface-proxy-properties-changed",
|
||||
G_CALLBACK(onInterfaceProxyPropertiesChanged), this);
|
||||
g_signal_connect(manager_.get(), "interface-added", G_CALLBACK(onInterfaceAddedOrRemoved), this);
|
||||
g_signal_connect(manager_.get(), "interface-added", G_CALLBACK(onInterfaceAddedOrRemoved),
|
||||
this);
|
||||
g_signal_connect(manager_.get(), "interface-removed", G_CALLBACK(onInterfaceAddedOrRemoved),
|
||||
this);
|
||||
}
|
||||
|
||||
#ifdef WANT_RFKILL
|
||||
rfkill_.on_update.connect(sigc::hide(sigc::mem_fun(*this, &Bluetooth::update)));
|
||||
@@ -264,7 +267,7 @@ auto waybar::modules::Bluetooth::update() -> void {
|
||||
device_enumerate_.erase(0, 1);
|
||||
}
|
||||
}
|
||||
label_.set_tooltip_text(fmt::format(
|
||||
label_.set_tooltip_markup(fmt::format(
|
||||
fmt::runtime(tooltip_format), fmt::arg("status", state_),
|
||||
fmt::arg("num_connections", connected_devices_.size()),
|
||||
fmt::arg("controller_address", cur_controller_ ? cur_controller_->address : "null"),
|
||||
@@ -446,6 +449,10 @@ auto waybar::modules::Bluetooth::getControllerProperties(GDBusObject* object,
|
||||
auto waybar::modules::Bluetooth::findCurController() -> std::optional<ControllerInfo> {
|
||||
std::optional<ControllerInfo> controller_info;
|
||||
|
||||
if (!manager_) {
|
||||
return controller_info;
|
||||
}
|
||||
|
||||
GList* objects = g_dbus_object_manager_get_objects(manager_.get());
|
||||
for (GList* l = objects; l != NULL; l = l->next) {
|
||||
GDBusObject* object = G_DBUS_OBJECT(l->data);
|
||||
@@ -465,6 +472,9 @@ auto waybar::modules::Bluetooth::findCurController() -> std::optional<Controller
|
||||
auto waybar::modules::Bluetooth::findConnectedDevices(const std::string& cur_controller_path,
|
||||
std::vector<DeviceInfo>& connected_devices)
|
||||
-> void {
|
||||
if (!manager_) {
|
||||
return;
|
||||
}
|
||||
GList* objects = g_dbus_object_manager_get_objects(manager_.get());
|
||||
for (GList* l = objects; l != NULL; l = l->next) {
|
||||
GDBusObject* object = G_DBUS_OBJECT(l->data);
|
||||
|
||||
@@ -0,0 +1,271 @@
|
||||
#include "modules/cava/cavaGLSL.hpp"
|
||||
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#include <fstream>
|
||||
|
||||
waybar::modules::cava::CavaGLSL::CavaGLSL(const std::string& id, const Json::Value& config)
|
||||
: AModule(config, "cavaGLSL", id, false, false),
|
||||
backend_{waybar::modules::cava::CavaBackend::inst(config)} {
|
||||
set_name(name_);
|
||||
if (config_["hide_on_silence"].isBool()) hide_on_silence_ = config_["hide_on_silence"].asBool();
|
||||
if (!id.empty()) {
|
||||
get_style_context()->add_class(id);
|
||||
}
|
||||
get_style_context()->add_class(MODULE_CLASS);
|
||||
|
||||
set_use_es(true);
|
||||
// set_auto_render(true);
|
||||
signal_realize().connect(sigc::mem_fun(*this, &CavaGLSL::onRealize));
|
||||
signal_render().connect(sigc::mem_fun(*this, &CavaGLSL::onRender), false);
|
||||
|
||||
// Get parameters_config struct from the backend
|
||||
prm_ = *backend_->getPrm();
|
||||
|
||||
// Set widget length
|
||||
int length{0};
|
||||
if (config_["min-length"].isUInt())
|
||||
length = config_["min-length"].asUInt();
|
||||
else if (config_["max-length"].isUInt())
|
||||
length = config_["max-length"].asUInt();
|
||||
else
|
||||
length = prm_.sdl_width;
|
||||
|
||||
set_size_request(length, prm_.sdl_height);
|
||||
|
||||
// Subscribe for changes
|
||||
backend_->signal_audio_raw_update().connect(sigc::mem_fun(*this, &CavaGLSL::onUpdate));
|
||||
// Subscribe for silence
|
||||
backend_->signal_silence().connect(sigc::mem_fun(*this, &CavaGLSL::onSilence));
|
||||
event_box_.add(*this);
|
||||
}
|
||||
|
||||
auto waybar::modules::cava::CavaGLSL::onUpdate(const ::cava::audio_raw& input) -> void {
|
||||
Glib::signal_idle().connect_once([this, input]() {
|
||||
m_data_ = std::make_shared<::cava::audio_raw>(input);
|
||||
if (silence_) {
|
||||
get_style_context()->remove_class("silent");
|
||||
if (!get_style_context()->has_class("updated")) get_style_context()->add_class("updated");
|
||||
show();
|
||||
silence_ = false;
|
||||
}
|
||||
|
||||
queue_render();
|
||||
});
|
||||
}
|
||||
|
||||
auto waybar::modules::cava::CavaGLSL::onSilence() -> void {
|
||||
Glib::signal_idle().connect_once([this]() {
|
||||
if (!silence_) {
|
||||
if (get_style_context()->has_class("updated")) get_style_context()->remove_class("updated");
|
||||
|
||||
if (hide_on_silence_) hide();
|
||||
silence_ = true;
|
||||
get_style_context()->add_class("silent");
|
||||
// Set clear color to black
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
queue_render();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bool waybar::modules::cava::CavaGLSL::onRender(const Glib::RefPtr<Gdk::GLContext>& context) {
|
||||
if (!m_data_) return true;
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, texture_);
|
||||
glUniform1i(glGetUniformLocation(shaderProgram_, "inputTexture"), 0);
|
||||
|
||||
glUniform1fv(uniform_bars_, m_data_->number_of_bars, m_data_->bars_raw);
|
||||
glUniform1fv(uniform_previous_bars_, m_data_->number_of_bars, m_data_->previous_bars_raw);
|
||||
glUniform1i(uniform_bars_count_, m_data_->number_of_bars);
|
||||
++frame_counter;
|
||||
glUniform1f(uniform_time_, (frame_counter / backend_->getFrameTimeMilsec().count()) / 1e3);
|
||||
|
||||
// glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
glDrawElements(GL_TRIANGLE_FAN, 4, GL_UNSIGNED_INT, nullptr);
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo_);
|
||||
glDrawElements(GL_TRIANGLE_FAN, 4, GL_UNSIGNED_INT, nullptr);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void waybar::modules::cava::CavaGLSL::onRealize() {
|
||||
make_current();
|
||||
initShaders();
|
||||
initGLSL();
|
||||
initSurface();
|
||||
}
|
||||
|
||||
struct colors {
|
||||
uint16_t R;
|
||||
uint16_t G;
|
||||
uint16_t B;
|
||||
};
|
||||
|
||||
static void parse_color(char* color_string, struct colors* color) {
|
||||
if (color_string[0] == '#') {
|
||||
sscanf(++color_string, "%02hx%02hx%02hx", &color->R, &color->G, &color->B);
|
||||
}
|
||||
}
|
||||
|
||||
void waybar::modules::cava::CavaGLSL::initGLSL() {
|
||||
GLint gVertexPos2DLocation{glGetAttribLocation(shaderProgram_, "vertexPosition_modelspace")};
|
||||
if (gVertexPos2DLocation == -1) {
|
||||
spdlog::error("{0}. Could not find vertex position shader variable", name_);
|
||||
}
|
||||
|
||||
glClearColor(0.f, 0.f, 0.f, 1.f);
|
||||
|
||||
GLfloat vertexData[]{-1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f};
|
||||
GLint indexData[]{0, 1, 2, 3};
|
||||
|
||||
GLuint gVBO{0};
|
||||
glGenBuffers(1, &gVBO);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, gVBO);
|
||||
glBufferData(GL_ARRAY_BUFFER, 2 * 4 * sizeof(GLfloat), vertexData, GL_STATIC_DRAW);
|
||||
|
||||
GLuint gIBO{0};
|
||||
glGenBuffers(1, &gIBO);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gIBO);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, 4 * sizeof(GLuint), indexData, GL_STATIC_DRAW);
|
||||
|
||||
GLuint gVAO{0};
|
||||
glGenVertexArrays(1, &gVAO);
|
||||
glBindVertexArray(gVAO);
|
||||
glEnableVertexAttribArray(gVertexPos2DLocation);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, gVBO);
|
||||
glVertexAttribPointer(gVertexPos2DLocation, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), nullptr);
|
||||
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gIBO);
|
||||
|
||||
glGenFramebuffers(1, &fbo_);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo_);
|
||||
|
||||
// Create a texture to attach the framebuffer
|
||||
glGenTextures(1, &texture_);
|
||||
glBindTexture(GL_TEXTURE_2D, texture_);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, prm_.sdl_width, prm_.sdl_height, 0, GL_RGBA,
|
||||
GL_UNSIGNED_BYTE, nullptr);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture_, 0);
|
||||
|
||||
// Check is framebuffer is complete
|
||||
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
|
||||
spdlog::error("{0}. Framebuffer not complete", name_);
|
||||
}
|
||||
|
||||
// Unbind the framebuffer
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
uniform_bars_ = glGetUniformLocation(shaderProgram_, "bars");
|
||||
uniform_previous_bars_ = glGetUniformLocation(shaderProgram_, "previous_bars");
|
||||
uniform_bars_count_ = glGetUniformLocation(shaderProgram_, "bars_count");
|
||||
uniform_time_ = glGetUniformLocation(shaderProgram_, "shader_time");
|
||||
|
||||
GLuint err{glGetError()};
|
||||
if (err != 0) {
|
||||
spdlog::error("{0}. Error on initGLSL: {1}", name_, err);
|
||||
}
|
||||
}
|
||||
|
||||
void waybar::modules::cava::CavaGLSL::initSurface() {
|
||||
colors color = {0};
|
||||
GLint uniform_bg_col{glGetUniformLocation(shaderProgram_, "bg_color")};
|
||||
parse_color(prm_.bcolor, &color);
|
||||
glUniform3f(uniform_bg_col, (float)color.R / 255.0, (float)color.G / 255.0,
|
||||
(float)color.B / 255.0);
|
||||
GLint uniform_fg_col{glGetUniformLocation(shaderProgram_, "fg_color")};
|
||||
parse_color(prm_.color, &color);
|
||||
glUniform3f(uniform_fg_col, (float)color.R / 255.0, (float)color.G / 255.0,
|
||||
(float)color.B / 255.0);
|
||||
GLint uniform_res{glGetUniformLocation(shaderProgram_, "u_resolution")};
|
||||
glUniform3f(uniform_res, (float)prm_.sdl_width, (float)prm_.sdl_height, 0.0f);
|
||||
GLint uniform_bar_width{glGetUniformLocation(shaderProgram_, "bar_width")};
|
||||
glUniform1i(uniform_bar_width, prm_.bar_width);
|
||||
GLint uniform_bar_spacing{glGetUniformLocation(shaderProgram_, "bar_spacing")};
|
||||
glUniform1i(uniform_bar_spacing, prm_.bar_spacing);
|
||||
GLint uniform_gradient_count{glGetUniformLocation(shaderProgram_, "gradient_count")};
|
||||
glUniform1i(uniform_gradient_count, prm_.gradient_count);
|
||||
GLint uniform_gradient_colors{glGetUniformLocation(shaderProgram_, "gradient_colors")};
|
||||
GLfloat gradient_colors[8][3];
|
||||
for (int i{0}; i < prm_.gradient_count; ++i) {
|
||||
parse_color(prm_.gradient_colors[i], &color);
|
||||
gradient_colors[i][0] = (float)color.R / 255.0;
|
||||
gradient_colors[i][1] = (float)color.G / 255.0;
|
||||
gradient_colors[i][2] = (float)color.B / 255.0;
|
||||
}
|
||||
glUniform3fv(uniform_gradient_colors, 8, (const GLfloat*)gradient_colors);
|
||||
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
glDrawElements(GL_TRIANGLE_FAN, 4, GL_UNSIGNED_INT, nullptr);
|
||||
}
|
||||
|
||||
void waybar::modules::cava::CavaGLSL::initShaders() {
|
||||
shaderProgram_ = glCreateProgram();
|
||||
|
||||
GLuint vertexShader{loadShader(prm_.vertex_shader, GL_VERTEX_SHADER)};
|
||||
GLuint fragmentShader{loadShader(prm_.fragment_shader, GL_FRAGMENT_SHADER)};
|
||||
|
||||
glAttachShader(shaderProgram_, vertexShader);
|
||||
glAttachShader(shaderProgram_, fragmentShader);
|
||||
|
||||
glLinkProgram(shaderProgram_);
|
||||
|
||||
glDeleteShader(vertexShader);
|
||||
glDeleteShader(fragmentShader);
|
||||
|
||||
// Check for linking errors
|
||||
GLint success, len;
|
||||
glGetProgramiv(shaderProgram_, GL_LINK_STATUS, &success);
|
||||
if (!success) {
|
||||
glGetProgramiv(shaderProgram_, GL_INFO_LOG_LENGTH, &len);
|
||||
GLchar* infoLog{(char*)'\0'};
|
||||
glGetProgramInfoLog(shaderProgram_, len, &len, infoLog);
|
||||
spdlog::error("{0}. Shader linking error: {1}", name_, infoLog);
|
||||
}
|
||||
|
||||
glReleaseShaderCompiler();
|
||||
glUseProgram(shaderProgram_);
|
||||
}
|
||||
|
||||
GLuint waybar::modules::cava::CavaGLSL::loadShader(const std::string& fileName, GLenum type) {
|
||||
spdlog::debug("{0}. loadShader: {1}", name_, fileName);
|
||||
|
||||
// Read shader source code from the file
|
||||
std::ifstream shaderFile{fileName};
|
||||
|
||||
if (!shaderFile.is_open()) {
|
||||
spdlog::error("{0}. Could not open shader file: {1}", name_, fileName);
|
||||
}
|
||||
|
||||
std::ostringstream buffer;
|
||||
buffer << shaderFile.rdbuf(); // read file content into stringstream
|
||||
std::string str{buffer.str()};
|
||||
const char* shaderSource = str.c_str();
|
||||
shaderFile.close();
|
||||
|
||||
GLuint shaderID{glCreateShader(type)};
|
||||
if (shaderID == 0) spdlog::error("{0}. Error creating shader type: {0}", type);
|
||||
glShaderSource(shaderID, 1, &shaderSource, nullptr);
|
||||
glCompileShader(shaderID);
|
||||
|
||||
// Check for compilation errors
|
||||
GLint success, len;
|
||||
|
||||
glGetShaderiv(shaderID, GL_COMPILE_STATUS, &success);
|
||||
|
||||
if (!success) {
|
||||
glGetShaderiv(shaderID, GL_INFO_LOG_LENGTH, &len);
|
||||
|
||||
GLchar* infoLog{(char*)'\0'};
|
||||
glGetShaderInfoLog(shaderID, len, nullptr, infoLog);
|
||||
spdlog::error("{0}. Shader compilation error in {1}: {2}", name_, fileName, infoLog);
|
||||
}
|
||||
|
||||
return shaderID;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "modules/cava/cava.hpp"
|
||||
#include "modules/cava/cavaRaw.hpp"
|
||||
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
@@ -24,8 +24,11 @@ auto waybar::modules::cava::Cava::doAction(const std::string& name) -> void {
|
||||
// Cava actions
|
||||
void waybar::modules::cava::Cava::pause_resume() { backend_->doPauseResume(); }
|
||||
auto waybar::modules::cava::Cava::onUpdate(const std::string& input) -> void {
|
||||
Glib::signal_idle().connect_once([this, input]() {
|
||||
if (silence_) {
|
||||
silence_ = false;
|
||||
label_.get_style_context()->remove_class("silent");
|
||||
if (!label_.get_style_context()->has_class("updated"))
|
||||
label_.get_style_context()->add_class("updated");
|
||||
}
|
||||
label_text_.clear();
|
||||
@@ -35,17 +38,23 @@ auto waybar::modules::cava::Cava::onUpdate(const std::string& input) -> void {
|
||||
label_.set_markup(label_text_);
|
||||
label_.show();
|
||||
ALabel::update();
|
||||
silence_ = false;
|
||||
});
|
||||
}
|
||||
|
||||
auto waybar::modules::cava::Cava::onSilence() -> void {
|
||||
Glib::signal_idle().connect_once([this]() {
|
||||
if (!silence_) {
|
||||
if (label_.get_style_context()->has_class("updated"))
|
||||
label_.get_style_context()->remove_class("updated");
|
||||
|
||||
if (hide_on_silence_)
|
||||
if (hide_on_silence_) {
|
||||
// Clear the label markup before hiding to prevent GTK from rendering a NULL Pango layout
|
||||
label_.set_markup("");
|
||||
label_.hide();
|
||||
else if (config_["format_silent"].isString())
|
||||
} else if (config_["format_silent"].isString())
|
||||
label_.set_markup(format_silent_);
|
||||
silence_ = true;
|
||||
label_.get_style_context()->add_class("silent");
|
||||
}
|
||||
});
|
||||
}
|
||||
+151
-101
@@ -9,96 +9,9 @@ std::shared_ptr<waybar::modules::cava::CavaBackend> waybar::modules::cava::CavaB
|
||||
return backend_ptr;
|
||||
}
|
||||
|
||||
waybar::modules::cava::CavaBackend::CavaBackend(const Json::Value& config) {
|
||||
waybar::modules::cava::CavaBackend::CavaBackend(const Json::Value& config) : config_(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
|
||||
loadConfig();
|
||||
// Read audio source trough cava API. Cava orginizes this process via infinity loop
|
||||
read_thread_ = [this] {
|
||||
try {
|
||||
@@ -107,33 +20,38 @@ waybar::modules::cava::CavaBackend::CavaBackend(const Json::Value& config) {
|
||||
spdlog::warn("Cava backend. Read source error: {0}", e.what());
|
||||
}
|
||||
read_thread_.sleep_for(fetch_input_delay_);
|
||||
loadConfig();
|
||||
};
|
||||
|
||||
thread_ = [this] {
|
||||
doUpdate();
|
||||
thread_.sleep_for(frame_time_milsec_);
|
||||
// Write outcoming data. Emit signals
|
||||
out_thread_ = [this] {
|
||||
doUpdate(false);
|
||||
out_thread_.sleep_for(frame_time_milsec_);
|
||||
};
|
||||
}
|
||||
|
||||
waybar::modules::cava::CavaBackend::~CavaBackend() {
|
||||
thread_.stop();
|
||||
out_thread_.stop();
|
||||
read_thread_.stop();
|
||||
delete plan_;
|
||||
plan_ = nullptr;
|
||||
|
||||
freeBackend();
|
||||
}
|
||||
|
||||
static void upThreadDelay(std::chrono::milliseconds& delay, std::chrono::seconds& delta) {
|
||||
static bool upThreadDelay(std::chrono::milliseconds& delay, std::chrono::seconds& delta) {
|
||||
if (delta == std::chrono::seconds{0}) {
|
||||
delta += std::chrono::seconds{1};
|
||||
delay += delta;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void downThreadDelay(std::chrono::milliseconds& delay, std::chrono::seconds& delta) {
|
||||
static bool downThreadDelay(std::chrono::milliseconds& delay, std::chrono::seconds& delta) {
|
||||
if (delta > std::chrono::seconds{0}) {
|
||||
delay -= delta;
|
||||
delta -= std::chrono::seconds{1};
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool waybar::modules::cava::CavaBackend::isSilence() {
|
||||
@@ -183,6 +101,7 @@ void waybar::modules::cava::CavaBackend::doPauseResume() {
|
||||
upThreadDelay(frame_time_milsec_, suspend_silence_delay_);
|
||||
}
|
||||
pthread_mutex_unlock(&audio_data_.lock);
|
||||
Update();
|
||||
}
|
||||
|
||||
waybar::modules::cava::CavaBackend::type_signal_update
|
||||
@@ -190,6 +109,11 @@ waybar::modules::cava::CavaBackend::signal_update() {
|
||||
return m_signal_update_;
|
||||
}
|
||||
|
||||
waybar::modules::cava::CavaBackend::type_signal_audio_raw_update
|
||||
waybar::modules::cava::CavaBackend::signal_audio_raw_update() {
|
||||
return m_signal_audio_raw_;
|
||||
}
|
||||
|
||||
waybar::modules::cava::CavaBackend::type_signal_silence
|
||||
waybar::modules::cava::CavaBackend::signal_silence() {
|
||||
return m_signal_silence_;
|
||||
@@ -212,12 +136,138 @@ void waybar::modules::cava::CavaBackend::doUpdate(bool force) {
|
||||
}
|
||||
|
||||
if (!silence_ || prm_.sleep_timer == 0) {
|
||||
downThreadDelay(frame_time_milsec_, suspend_silence_delay_);
|
||||
if (downThreadDelay(frame_time_milsec_, suspend_silence_delay_)) Update();
|
||||
execute();
|
||||
if (re_paint_ == 1 || force) m_signal_update_.emit(output_);
|
||||
if (re_paint_ == 1 || force || prm_.continuous_rendering) {
|
||||
m_signal_update_.emit(output_);
|
||||
m_signal_audio_raw_.emit(audio_raw_);
|
||||
}
|
||||
} else {
|
||||
upThreadDelay(frame_time_milsec_, suspend_silence_delay_);
|
||||
if (upThreadDelay(frame_time_milsec_, suspend_silence_delay_)) Update();
|
||||
if (silence_ != silence_prev_ || force) m_signal_silence_.emit();
|
||||
}
|
||||
silence_prev_ = silence_;
|
||||
}
|
||||
|
||||
void waybar::modules::cava::CavaBackend::freeBackend() {
|
||||
if (plan_ != NULL) {
|
||||
cava_destroy(plan_);
|
||||
plan_ = NULL;
|
||||
}
|
||||
|
||||
audio_raw_clean(&audio_raw_);
|
||||
pthread_mutex_lock(&audio_data_.lock);
|
||||
audio_data_.terminate = 1;
|
||||
pthread_mutex_unlock(&audio_data_.lock);
|
||||
free_config(&prm_);
|
||||
free(audio_data_.source);
|
||||
free(audio_data_.cava_in);
|
||||
}
|
||||
|
||||
void waybar::modules::cava::CavaBackend::loadConfig() {
|
||||
freeBackend();
|
||||
// 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_, &error_)) {
|
||||
spdlog::error("cava backend. Error loading config. {0}", error_.message);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Override cava parameters by the user config
|
||||
prm_.inAtty = 0;
|
||||
auto const output{prm_.output};
|
||||
// prm_.output = ::cava::output_method::OUTPUT_RAW;
|
||||
if (prm_.data_format) free(prm_.data_format);
|
||||
// Default to ascii for format-icons output; allow user override
|
||||
prm_.data_format = strdup(
|
||||
config_["data_format"].isString() ? config_["data_format"].asString().c_str() : "ascii");
|
||||
if (config_["raw_target"].isString()) {
|
||||
if (prm_.raw_target) free(prm_.raw_target);
|
||||
prm_.raw_target = strdup(config_["raw_target"].asString().c_str());
|
||||
}
|
||||
prm_.ascii_range = config_["format-icons"].size() - 1;
|
||||
|
||||
if (config_["bar_spacing"].isInt()) prm_.bar_spacing = config_["bar_spacing"].asInt();
|
||||
if (config_["bar_width"].isInt()) prm_.bar_width = config_["bar_width"].asInt();
|
||||
if (config_["bar_height"].isInt()) prm_.bar_height = config_["bar_height"].asInt();
|
||||
prm_.orientation = ::cava::ORIENT_TOP;
|
||||
prm_.xaxis = ::cava::xaxis_scale::NONE;
|
||||
prm_.mono_opt = ::cava::AVERAGE;
|
||||
prm_.autobars = 0;
|
||||
if (config_["gravity"].isInt()) prm_.gravity = config_["gravity"].asInt();
|
||||
if (config_["integral"].isInt()) prm_.integral = config_["integral"].asInt();
|
||||
|
||||
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()) {
|
||||
if (prm_.audio_source) free(prm_.audio_source);
|
||||
prm_.audio_source = strdup(config_["source"].asString().c_str());
|
||||
}
|
||||
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_["gradient"].isInt()) prm_.gradient = config_["gradient"].asInt();
|
||||
if (prm_.gradient == 0)
|
||||
prm_.gradient_count = 0;
|
||||
else if (config_["gradient_count"].isInt())
|
||||
prm_.gradient_count = config_["gradient_count"].asInt();
|
||||
if (config_["sdl_width"].isInt()) prm_.sdl_width = config_["sdl_width"].asInt();
|
||||
if (config_["sdl_height"].isInt()) prm_.sdl_height = config_["sdl_height"].asInt();
|
||||
|
||||
audio_raw_.height = prm_.ascii_range;
|
||||
audio_data_.format = -1;
|
||||
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_.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);
|
||||
}
|
||||
|
||||
prm_.output = ::cava::output_method::OUTPUT_RAW;
|
||||
|
||||
// Make cava parameters configuration
|
||||
// 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
|
||||
|
||||
prm_.output = output;
|
||||
}
|
||||
|
||||
const struct ::cava::config_params* waybar::modules::cava::CavaBackend::getPrm() { return &prm_; }
|
||||
std::chrono::milliseconds waybar::modules::cava::CavaBackend::getFrameTimeMilsec() {
|
||||
return frame_time_milsec_;
|
||||
};
|
||||
|
||||
@@ -35,7 +35,7 @@ CFFI::CFFI(const std::string& name, const std::string& id, const Json::Value& co
|
||||
throw std::runtime_error{std::string{"Missing wbcffi_init function: "} + dlerror()};
|
||||
}
|
||||
hooks_.deinit = reinterpret_cast<DenitFn*>(dlsym(handle, "wbcffi_deinit"));
|
||||
if (!hooks_.init) {
|
||||
if (!hooks_.deinit) {
|
||||
throw std::runtime_error{std::string{"Missing wbcffi_deinit function: "} + dlerror()};
|
||||
}
|
||||
// Optional functions
|
||||
@@ -71,6 +71,7 @@ CFFI::CFFI(const std::string& name, const std::string& id, const Json::Value& co
|
||||
|
||||
// Prepare config_entries array
|
||||
std::vector<ffi::wbcffi_config_entry> config_entries;
|
||||
config_entries.reserve(keys.size());
|
||||
for (size_t i = 0; i < keys.size(); i++) {
|
||||
config_entries.push_back({keys[i].c_str(), config_entries_stringstor[i].c_str()});
|
||||
}
|
||||
|
||||
@@ -30,7 +30,9 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
|
||||
cldMonShift_{year(1900) / January},
|
||||
tzInTooltip_{m_tlpFmt_.find("{" + kTZPlaceholder + "}") != std::string::npos},
|
||||
tzCurrIdx_{0},
|
||||
tzTooltipFormat_{config_["timezone-tooltip-format"].isString() ? config_["timezone-tooltip-format"].asString() : ""},
|
||||
tzTooltipFormat_{config_["timezone-tooltip-format"].isString()
|
||||
? config_["timezone-tooltip-format"].asString()
|
||||
: ""},
|
||||
ordInTooltip_{m_tlpFmt_.find("{" + kOrdPlaceholder + "}") != std::string::npos} {
|
||||
m_tlpText_ = m_tlpFmt_;
|
||||
|
||||
@@ -228,7 +230,7 @@ const unsigned cldRowsInMonth(const year_month& ym, const weekday& firstdow) {
|
||||
auto cldGetWeekForLine(const year_month& ym, const weekday& firstdow, const unsigned line)
|
||||
-> const year_month_weekday {
|
||||
const unsigned idx = line - 2;
|
||||
const std::chrono::weekday_indexed indexed_first_day_of_week =
|
||||
const auto indexed_first_day_of_week =
|
||||
weekday{ym / 1} == firstdow ? firstdow[idx + 1] : firstdow[idx];
|
||||
|
||||
return ym / indexed_first_day_of_week;
|
||||
|
||||
+16
-7
@@ -26,9 +26,7 @@ auto waybar::modules::Cpu::update() -> void {
|
||||
auto [load1, load5, load15] = Load::getLoad();
|
||||
auto [cpu_usage, tooltip] = CpuUsage::getCpuUsage(prev_times_);
|
||||
auto [max_frequency, min_frequency, avg_frequency] = CpuFrequency::getCpuFrequency();
|
||||
if (tooltipEnabled()) {
|
||||
label_.set_tooltip_text(tooltip);
|
||||
}
|
||||
|
||||
auto format = format_;
|
||||
auto total_usage = cpu_usage.empty() ? 0 : cpu_usage[0];
|
||||
auto state = getState(total_usage);
|
||||
@@ -48,14 +46,25 @@ auto waybar::modules::Cpu::update() -> void {
|
||||
store.push_back(fmt::arg("max_frequency", max_frequency));
|
||||
store.push_back(fmt::arg("min_frequency", min_frequency));
|
||||
store.push_back(fmt::arg("avg_frequency", avg_frequency));
|
||||
std::vector<std::string> arg_names;
|
||||
arg_names.reserve(cpu_usage.size() * 2);
|
||||
for (size_t i = 1; i < cpu_usage.size(); ++i) {
|
||||
auto core_i = i - 1;
|
||||
auto core_format = fmt::format("usage{}", core_i);
|
||||
store.push_back(fmt::arg(core_format.c_str(), cpu_usage[i]));
|
||||
auto icon_format = fmt::format("icon{}", core_i);
|
||||
store.push_back(fmt::arg(icon_format.c_str(), getIcon(cpu_usage[i], icons)));
|
||||
arg_names.push_back(fmt::format("usage{}", core_i));
|
||||
store.push_back(fmt::arg(arg_names.back().c_str(), cpu_usage[i]));
|
||||
arg_names.push_back(fmt::format("icon{}", core_i));
|
||||
store.push_back(fmt::arg(arg_names.back().c_str(), getIcon(cpu_usage[i], icons)));
|
||||
}
|
||||
label_.set_markup(fmt::vformat(format, store));
|
||||
|
||||
if (tooltipEnabled()) {
|
||||
if (config_["tooltip-format"].isString()) {
|
||||
tooltip = config_["tooltip-format"].asString();
|
||||
label_.set_tooltip_markup(fmt::vformat(tooltip, store));
|
||||
} else {
|
||||
label_.set_tooltip_markup(tooltip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Call parent update
|
||||
|
||||
@@ -20,12 +20,7 @@ waybar::modules::CpuFrequency::CpuFrequency(const std::string& id, const Json::V
|
||||
auto waybar::modules::CpuFrequency::update() -> void {
|
||||
// TODO: as creating dynamic fmt::arg arrays is buggy we have to calc both
|
||||
auto [max_frequency, min_frequency, avg_frequency] = CpuFrequency::getCpuFrequency();
|
||||
if (tooltipEnabled()) {
|
||||
auto tooltip =
|
||||
fmt::format("Minimum frequency: {}\nAverage frequency: {}\nMaximum frequency: {}\n",
|
||||
min_frequency, avg_frequency, max_frequency);
|
||||
label_.set_tooltip_text(tooltip);
|
||||
}
|
||||
|
||||
auto format = format_;
|
||||
auto state = getState(avg_frequency);
|
||||
if (!state.empty() && config_["format-" + state].isString()) {
|
||||
@@ -43,6 +38,18 @@ auto waybar::modules::CpuFrequency::update() -> void {
|
||||
store.push_back(fmt::arg("min_frequency", min_frequency));
|
||||
store.push_back(fmt::arg("avg_frequency", avg_frequency));
|
||||
label_.set_markup(fmt::vformat(format, store));
|
||||
|
||||
if (tooltipEnabled()) {
|
||||
std::string tooltip;
|
||||
if (config_["tooltip-format"].isString()) {
|
||||
tooltip = config_["tooltip-format"].asString();
|
||||
label_.set_tooltip_markup(fmt::vformat(tooltip, store));
|
||||
} else {
|
||||
tooltip = "Minimum frequency: {}\nAverage frequency: {}\nMaximum frequency: {}\n";
|
||||
label_.set_tooltip_markup(
|
||||
fmt::format(fmt::runtime(tooltip), min_frequency, avg_frequency, max_frequency));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Call parent update
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user