Merge remote-tracking branch 'origin/master' into 2025-wlr-taskbar-add-justify-option
# Conflicts: # man/waybar-wlr-taskbar.5.scd
This commit is contained in:
@@ -10,13 +10,9 @@ jobs:
|
|||||||
lint:
|
lint:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v6
|
||||||
# TODO: bump to clang 19 release
|
- uses: RafikFarhad/clang-format-github-action@v6
|
||||||
# - uses: DoozyX/clang-format-lint-action@v0.18.2
|
|
||||||
- uses: DoozyX/clang-format-lint-action@558090054b3f39e3d6af24f0cd73b319535da809
|
|
||||||
name: clang-format
|
name: clang-format
|
||||||
with:
|
with:
|
||||||
source: "."
|
sources: "src/**/*.hpp,src/**/*.cpp"
|
||||||
extensions: "hpp,h,cpp,c"
|
style: "file"
|
||||||
style: "file:.clang-format"
|
|
||||||
clangFormatVersion: 19
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ jobs:
|
|||||||
container:
|
container:
|
||||||
image: alexays/waybar:debian
|
image: alexays/waybar:debian
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v6
|
||||||
- name: configure
|
- name: configure
|
||||||
run: |
|
run: |
|
||||||
meson -Dcpp_std=c++20 build # necessary to generate compile_commands.json
|
meson -Dcpp_std=c++20 build # necessary to generate compile_commands.json
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Login to Docker Hub
|
- name: Login to Docker Hub
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ jobs:
|
|||||||
# https://github.com/actions/runner/issues/385 - for FreeBSD runner support
|
# https://github.com/actions/runner/issues/385 - for FreeBSD runner support
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v6
|
||||||
- name: Test in FreeBSD VM
|
- name: Test in FreeBSD VM
|
||||||
uses: cross-platform-actions/action@v0.28.0
|
uses: cross-platform-actions/action@v0.28.0
|
||||||
timeout-minutes: 180
|
timeout-minutes: 180
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ jobs:
|
|||||||
image: alexays/waybar:${{ matrix.distro }}
|
image: alexays/waybar:${{ matrix.distro }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v6
|
||||||
- name: configure
|
- name: configure
|
||||||
run: meson setup -Dman-pages=enabled -Dcpp_std=${{matrix.cpp_std}} build
|
run: meson setup -Dman-pages=enabled -Dcpp_std=${{matrix.cpp_std}} build
|
||||||
- name: build
|
- name: build
|
||||||
|
|||||||
@@ -2,12 +2,15 @@ name: "Nix-Tests"
|
|||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
push:
|
push:
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-nix-${{ github.event.pull_request.number || github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
jobs:
|
jobs:
|
||||||
nix-flake-check:
|
nix-flake-check:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
- uses: cachix/install-nix-action@v27
|
- uses: cachix/install-nix-action@v31
|
||||||
with:
|
with:
|
||||||
extra_nix_config: |
|
extra_nix_config: |
|
||||||
experimental-features = nix-command flakes
|
experimental-features = nix-command flakes
|
||||||
|
|||||||
@@ -12,11 +12,11 @@ jobs:
|
|||||||
if: github.event_name != 'schedule' || github.repository == 'Alexays/Waybar'
|
if: github.event_name != 'schedule' || github.repository == 'Alexays/Waybar'
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
- name: Install Nix
|
- name: Install Nix
|
||||||
uses: cachix/install-nix-action@v27
|
uses: cachix/install-nix-action@v31
|
||||||
with:
|
with:
|
||||||
extra_nix_config: |
|
extra_nix_config: |
|
||||||
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Update flake.lock
|
- name: Update flake.lock
|
||||||
uses: DeterminateSystems/update-flake-lock@v21
|
uses: DeterminateSystems/update-flake-lock@v28
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ vgcore.*
|
|||||||
*.swp
|
*.swp
|
||||||
packagecache
|
packagecache
|
||||||
/subprojects/**/
|
/subprojects/**/
|
||||||
|
/subprojects/.wraplock
|
||||||
/build*
|
/build*
|
||||||
/dist
|
/dist
|
||||||
/meson.egg-info
|
/meson.egg-info
|
||||||
@@ -50,3 +51,5 @@ result
|
|||||||
result-*
|
result-*
|
||||||
|
|
||||||
.ccls-cache
|
.ccls-cache
|
||||||
|
_codeql_detected_source_root
|
||||||
|
heaptrack*
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ sudo apt install \
|
|||||||
On Arch, you can use this command:
|
On Arch, you can use this command:
|
||||||
|
|
||||||
```
|
```
|
||||||
pacman -S \
|
pacman -S --asdeps \
|
||||||
gtkmm3 \
|
gtkmm3 \
|
||||||
jsoncpp \
|
jsoncpp \
|
||||||
libsigc++ \
|
libsigc++ \
|
||||||
@@ -151,6 +151,10 @@ Contributions welcome!<br>
|
|||||||
Have fun :)<br>
|
Have fun :)<br>
|
||||||
The style guidelines are [Google's](https://google.github.io/styleguide/cppguide.html)
|
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
|
## License
|
||||||
|
|
||||||
Waybar is licensed under the MIT license. [See LICENSE for more information](https://github.com/Alexays/Waybar/blob/master/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 @@
|
|||||||
|
.
|
||||||
Generated
+6
-6
@@ -3,11 +3,11 @@
|
|||||||
"flake-compat": {
|
"flake-compat": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1747046372,
|
"lastModified": 1767039857,
|
||||||
"narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=",
|
"narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=",
|
||||||
"owner": "edolstra",
|
"owner": "edolstra",
|
||||||
"repo": "flake-compat",
|
"repo": "flake-compat",
|
||||||
"rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885",
|
"rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -18,11 +18,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1759036355,
|
"lastModified": 1769461804,
|
||||||
"narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=",
|
"narHash": "sha256-msG8SU5WsBUfVVa/9RPLaymvi5bI8edTavbIq3vRlhI=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127",
|
"rev": "bfc1b8a4574108ceef22f02bafcf6611380c100d",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
#include <gtkmm/label.h>
|
#include <gtkmm/label.h>
|
||||||
#include <json/json.h>
|
#include <json/json.h>
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
#include "AModule.hpp"
|
#include "AModule.hpp"
|
||||||
|
|
||||||
namespace waybar {
|
namespace waybar {
|
||||||
@@ -25,12 +27,19 @@ class ALabel : public AModule {
|
|||||||
bool alt_ = false;
|
bool alt_ = false;
|
||||||
std::string default_format_;
|
std::string default_format_;
|
||||||
|
|
||||||
|
bool setLabelMarkup(const Glib::ustring& markup);
|
||||||
|
bool setTooltipMarkup(const Glib::ustring& markup);
|
||||||
|
|
||||||
bool handleToggle(GdkEventButton* const& e) override;
|
bool handleToggle(GdkEventButton* const& e) override;
|
||||||
virtual std::string getState(uint8_t value, bool lesser = false);
|
virtual std::string getState(uint8_t value, bool lesser = false);
|
||||||
|
|
||||||
std::map<std::string, GtkMenuItem*> submenus_;
|
std::map<std::string, GtkMenuItem*> submenus_;
|
||||||
std::map<std::string, std::string> menuActionsMap_;
|
std::map<std::string, std::string> menuActionsMap_;
|
||||||
static void handleGtkMenuEvent(GtkMenuItem* menuitem, gpointer data);
|
static void handleGtkMenuEvent(GtkMenuItem* menuitem, gpointer data);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::optional<Glib::ustring> last_label_markup_;
|
||||||
|
std::optional<Glib::ustring> last_tooltip_markup_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace waybar
|
} // namespace waybar
|
||||||
|
|||||||
+8
-1
@@ -25,6 +25,10 @@ class AModule : public IModule {
|
|||||||
|
|
||||||
bool expandEnabled() const;
|
bool expandEnabled() const;
|
||||||
|
|
||||||
|
virtual void suspend() {};
|
||||||
|
virtual void resume() {};
|
||||||
|
bool shouldSuspend() const { return disable_on_sleep_; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Don't need to make an object directly
|
// Don't need to make an object directly
|
||||||
// Derived classes are able to use it
|
// Derived classes are able to use it
|
||||||
@@ -48,7 +52,9 @@ class AModule : public IModule {
|
|||||||
virtual bool handleMouseLeave(GdkEventCrossing* const& ev);
|
virtual bool handleMouseLeave(GdkEventCrossing* const& ev);
|
||||||
virtual bool handleScroll(GdkEventScroll*);
|
virtual bool handleScroll(GdkEventScroll*);
|
||||||
virtual bool handleRelease(GdkEventButton* const& ev);
|
virtual bool handleRelease(GdkEventButton* const& ev);
|
||||||
GObject *menu_;
|
|
||||||
|
bool disable_on_sleep_{false};
|
||||||
|
GObject* menu_ = nullptr;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool handleUserEvent(GdkEventButton* const& ev);
|
bool handleUserEvent(GdkEventButton* const& ev);
|
||||||
@@ -57,6 +63,7 @@ class AModule : public IModule {
|
|||||||
bool hasUserEvents_;
|
bool hasUserEvents_;
|
||||||
gdouble distance_scrolled_y_;
|
gdouble distance_scrolled_y_;
|
||||||
gdouble distance_scrolled_x_;
|
gdouble distance_scrolled_x_;
|
||||||
|
sigc::connection cursor_timeout_conn_;
|
||||||
std::map<std::string, std::string> eventActionMap_;
|
std::map<std::string, std::string> eventActionMap_;
|
||||||
static const inline std::map<std::pair<uint, GdkEventType>, std::string> eventMap_{
|
static const inline std::map<std::pair<uint, GdkEventType>, std::string> eventMap_{
|
||||||
{std::make_pair(1, GdkEventType::GDK_BUTTON_PRESS), "on-click"},
|
{std::make_pair(1, GdkEventType::GDK_BUTTON_PRESS), "on-click"},
|
||||||
|
|||||||
@@ -75,6 +75,8 @@ class Bar : public sigc::trackable {
|
|||||||
util::KillSignalAction getOnSigusr1Action();
|
util::KillSignalAction getOnSigusr1Action();
|
||||||
util::KillSignalAction getOnSigusr2Action();
|
util::KillSignalAction getOnSigusr2Action();
|
||||||
|
|
||||||
|
void toggleSuspend(bool suspend);
|
||||||
|
|
||||||
struct waybar_output* output;
|
struct waybar_output* output;
|
||||||
Json::Value config;
|
Json::Value config;
|
||||||
struct wl_surface* surface;
|
struct wl_surface* surface;
|
||||||
@@ -99,6 +101,7 @@ class Bar : public sigc::trackable {
|
|||||||
void setMode(const bar_mode&);
|
void setMode(const bar_mode&);
|
||||||
void setPassThrough(bool passthrough);
|
void setPassThrough(bool passthrough);
|
||||||
void setPosition(Gtk::PositionType position);
|
void setPosition(Gtk::PositionType position);
|
||||||
|
void forceLayerCommit();
|
||||||
void onConfigure(GdkEventConfigure* ev);
|
void onConfigure(GdkEventConfigure* ev);
|
||||||
void configureGlobalOffset(int width, int height);
|
void configureGlobalOffset(int width, int height);
|
||||||
void onOutputGeometryChanged();
|
void onOutputGeometryChanged();
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ class Client {
|
|||||||
const char* interface, uint32_t version);
|
const char* interface, uint32_t version);
|
||||||
static void handleGlobalRemove(void* data, struct wl_registry* registry, uint32_t name);
|
static void handleGlobalRemove(void* data, struct wl_registry* registry, uint32_t name);
|
||||||
static void handleOutputDone(void*, struct zxdg_output_v1*);
|
static void handleOutputDone(void*, struct zxdg_output_v1*);
|
||||||
|
void createBarsBatch();
|
||||||
static void handleOutputName(void*, struct zxdg_output_v1*, const char*);
|
static void handleOutputName(void*, struct zxdg_output_v1*, const char*);
|
||||||
static void handleOutputDescription(void*, struct zxdg_output_v1*, const char*);
|
static void handleOutputDescription(void*, struct zxdg_output_v1*, const char*);
|
||||||
void handleMonitorAdded(Glib::RefPtr<Gdk::Monitor> monitor);
|
void handleMonitorAdded(Glib::RefPtr<Gdk::Monitor> monitor);
|
||||||
@@ -56,6 +57,10 @@ class Client {
|
|||||||
std::list<struct waybar_output> outputs_;
|
std::list<struct waybar_output> outputs_;
|
||||||
std::unique_ptr<CssReloadHelper> m_cssReloadHelper;
|
std::unique_ptr<CssReloadHelper> m_cssReloadHelper;
|
||||||
std::string m_cssFile;
|
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
|
} // namespace waybar
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ class Group : public AModule {
|
|||||||
bool handleMouseEnter(GdkEventCrossing* const& ev) override;
|
bool handleMouseEnter(GdkEventCrossing* const& ev) override;
|
||||||
bool handleMouseLeave(GdkEventCrossing* const& ev) override;
|
bool handleMouseLeave(GdkEventCrossing* const& ev) override;
|
||||||
bool handleToggle(GdkEventButton* const& ev) override;
|
bool handleToggle(GdkEventButton* const& ev) override;
|
||||||
|
bool handleScroll(GdkEventScroll* e) override;
|
||||||
void show_group();
|
void show_group();
|
||||||
void hide_group();
|
void hide_group();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#if defined(__linux__)
|
#if defined(__linux__)
|
||||||
#include <sys/inotify.h>
|
#include <sys/inotify.h>
|
||||||
#endif
|
#endif
|
||||||
|
#include <poll.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
@@ -15,6 +16,7 @@
|
|||||||
#include "ALabel.hpp"
|
#include "ALabel.hpp"
|
||||||
#include "bar.hpp"
|
#include "bar.hpp"
|
||||||
#include "util/sleeper_thread.hpp"
|
#include "util/sleeper_thread.hpp"
|
||||||
|
#include "util/udev_deleter.hpp"
|
||||||
|
|
||||||
namespace waybar::modules {
|
namespace waybar::modules {
|
||||||
|
|
||||||
@@ -37,15 +39,17 @@ class Battery : public ALabel {
|
|||||||
void setBarClass(std::string&);
|
void setBarClass(std::string&);
|
||||||
void processEvents(std::string& state, std::string& status, uint8_t capacity);
|
void processEvents(std::string& state, std::string& status, uint8_t capacity);
|
||||||
|
|
||||||
int global_watch;
|
|
||||||
std::map<fs::path, int> batteries_;
|
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_;
|
fs::path adapter_;
|
||||||
int battery_watch_fd_;
|
int battery_watch_fd_;
|
||||||
int global_watch_fd_;
|
|
||||||
std::mutex battery_list_mutex_;
|
std::mutex battery_list_mutex_;
|
||||||
std::string old_status_;
|
std::string old_status_;
|
||||||
std::string last_event_;
|
std::string last_event_;
|
||||||
bool warnFirstTime_{true};
|
bool warnFirstTime_{true};
|
||||||
|
bool weightedAverage_{true};
|
||||||
const Bar& bar_;
|
const Bar& bar_;
|
||||||
|
|
||||||
util::SleeperThread thread_;
|
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:
|
public:
|
||||||
Cava(const std::string&, const Json::Value&);
|
Cava(const std::string&, const Json::Value&);
|
||||||
~Cava() = default;
|
~Cava() = default;
|
||||||
auto onUpdate(const std::string& input) -> void;
|
|
||||||
auto onSilence() -> void;
|
|
||||||
auto doAction(const std::string& name) -> void override;
|
auto doAction(const std::string& name) -> void override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<CavaBackend> backend_;
|
std::shared_ptr<CavaBackend> backend_;
|
||||||
// Text to display
|
// Text to display
|
||||||
std::string label_text_{""};
|
Glib::ustring label_text_{""};
|
||||||
|
bool silence_{false};
|
||||||
bool hide_on_silence_{false};
|
bool hide_on_silence_{false};
|
||||||
std::string format_silent_{""};
|
std::string format_silent_{""};
|
||||||
int ascii_range_{0};
|
int ascii_range_{0};
|
||||||
bool silence_{false};
|
|
||||||
// Cava method
|
// Cava method
|
||||||
void pause_resume();
|
void pause_resume();
|
||||||
|
auto onUpdate(const std::string& input) -> void;
|
||||||
|
auto onSilence() -> void;
|
||||||
// ModuleActionMap
|
// ModuleActionMap
|
||||||
static inline std::map<const std::string, void (waybar::modules::cava::Cava::* const)()>
|
static inline std::map<const std::string, void (waybar::modules::cava::Cava::* const)()>
|
||||||
actionMap_{{"mode", &waybar::modules::cava::Cava::pause_resume}};
|
actionMap_{{"mode", &waybar::modules::cava::Cava::pause_resume}};
|
||||||
@@ -32,29 +32,36 @@ class CavaBackend final {
|
|||||||
int getAsciiRange();
|
int getAsciiRange();
|
||||||
void doPauseResume();
|
void doPauseResume();
|
||||||
void Update();
|
void Update();
|
||||||
|
const struct ::cava::config_params* getPrm();
|
||||||
|
std::chrono::milliseconds getFrameTimeMilsec();
|
||||||
|
|
||||||
// Signal accessor
|
// Signal accessor
|
||||||
using type_signal_update = sigc::signal<void(const std::string&)>;
|
using type_signal_update = sigc::signal<void(const std::string&)>;
|
||||||
type_signal_update signal_update();
|
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()>;
|
using type_signal_silence = sigc::signal<void()>;
|
||||||
type_signal_silence signal_silence();
|
type_signal_silence signal_silence();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CavaBackend(const Json::Value& config);
|
CavaBackend(const Json::Value& config);
|
||||||
util::SleeperThread thread_;
|
|
||||||
util::SleeperThread read_thread_;
|
util::SleeperThread read_thread_;
|
||||||
|
util::SleeperThread out_thread_;
|
||||||
|
|
||||||
// Cava API to read audio source
|
// Cava API to read audio source
|
||||||
::cava::ptr input_source_;
|
::cava::ptr input_source_{NULL};
|
||||||
|
|
||||||
struct ::cava::error_s error_{}; // cava errors
|
struct ::cava::error_s error_{}; // cava errors
|
||||||
struct ::cava::config_params prm_{}; // cava parameters
|
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_raw audio_raw_{}; // cava handled raw audio data(is based on audio_data)
|
||||||
struct ::cava::audio_data audio_data_{}; // cava 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};
|
std::chrono::seconds fetch_input_delay_{4};
|
||||||
// Delay to handle audio source
|
// Delay to handle audio source
|
||||||
std::chrono::milliseconds frame_time_milsec_{1s};
|
std::chrono::milliseconds frame_time_milsec_{1s};
|
||||||
|
|
||||||
|
const Json::Value& config_;
|
||||||
int re_paint_{0};
|
int re_paint_{0};
|
||||||
bool silence_{false};
|
bool silence_{false};
|
||||||
bool silence_prev_{false};
|
bool silence_prev_{false};
|
||||||
@@ -66,9 +73,12 @@ class CavaBackend final {
|
|||||||
void execute();
|
void execute();
|
||||||
bool isSilence();
|
bool isSilence();
|
||||||
void doUpdate(bool force = false);
|
void doUpdate(bool force = false);
|
||||||
|
void loadConfig();
|
||||||
|
void freeBackend();
|
||||||
|
|
||||||
// Signal
|
// Signal
|
||||||
type_signal_update m_signal_update_;
|
type_signal_update m_signal_update_;
|
||||||
|
type_signal_audio_raw_update m_signal_audio_raw_;
|
||||||
type_signal_silence m_signal_silence_;
|
type_signal_silence m_signal_silence_;
|
||||||
};
|
};
|
||||||
} // namespace waybar::modules::cava
|
} // 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 id_;
|
||||||
std::string alt_;
|
std::string alt_;
|
||||||
std::string tooltip_;
|
std::string tooltip_;
|
||||||
|
std::string last_tooltip_markup_;
|
||||||
const bool tooltip_format_enabled_;
|
const bool tooltip_format_enabled_;
|
||||||
std::vector<std::string> class_;
|
std::vector<std::string> class_;
|
||||||
int percentage_;
|
int percentage_;
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ class Disk : public ALabel {
|
|||||||
std::string path_;
|
std::string path_;
|
||||||
std::string unit_;
|
std::string unit_;
|
||||||
|
|
||||||
float calc_specific_divisor(const std::string divisor);
|
float calc_specific_divisor(const std::string& divisor);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace waybar::modules
|
} // namespace waybar::modules
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ class Gamemode : public AModule {
|
|||||||
const std::string DEFAULT_FORMAT = "{glyph}";
|
const std::string DEFAULT_FORMAT = "{glyph}";
|
||||||
const std::string DEFAULT_FORMAT_ALT = "{glyph} {count}";
|
const std::string DEFAULT_FORMAT_ALT = "{glyph} {count}";
|
||||||
const std::string DEFAULT_TOOLTIP_FORMAT = "Games running: {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,
|
void appear(const Glib::RefPtr<Gio::DBus::Connection>& connection, const Glib::ustring& name,
|
||||||
const Glib::ustring& name_owner);
|
const Glib::ustring& name_owner);
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
@@ -34,19 +36,33 @@ class IPC {
|
|||||||
Json::Value getSocket1JsonReply(const std::string& rq);
|
Json::Value getSocket1JsonReply(const std::string& rq);
|
||||||
static std::filesystem::path getSocketFolder(const char* instanceSig);
|
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:
|
protected:
|
||||||
static std::filesystem::path socketFolder_;
|
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:
|
private:
|
||||||
void socketListener();
|
void socketListener();
|
||||||
void parseIPC(const std::string&);
|
void parseIPC(const std::string&);
|
||||||
|
|
||||||
std::thread ipcThread_;
|
std::thread ipcThread_;
|
||||||
std::mutex callbackMutex_;
|
std::mutex callbackMutex_;
|
||||||
|
std::mutex socketMutex_;
|
||||||
util::JsonParser parser_;
|
util::JsonParser parser_;
|
||||||
std::list<std::pair<std::string, EventHandler*>> callbacks_;
|
std::list<std::pair<std::string, EventHandler*>> callbacks_;
|
||||||
int socketfd_; // the hyprland socket file descriptor
|
int socketfd_ = -1; // the hyprland socket file descriptor
|
||||||
pid_t socketOwnerPid_;
|
pid_t socketOwnerPid_ = -1;
|
||||||
bool running_ = true; // the ipcThread will stop running when this is false
|
std::atomic<bool> running_ = true; // the ipcThread will stop running when this is false
|
||||||
};
|
};
|
||||||
}; // namespace waybar::modules::hyprland
|
}; // namespace waybar::modules::hyprland
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ class Submap : public waybar::ALabel, public EventHandler {
|
|||||||
const Bar& bar_;
|
const Bar& bar_;
|
||||||
util::JsonParser parser_;
|
util::JsonParser parser_;
|
||||||
std::string submap_;
|
std::string submap_;
|
||||||
|
std::string prev_submap_;
|
||||||
bool always_on_ = false;
|
bool always_on_ = false;
|
||||||
std::string default_submap_ = "Default";
|
std::string default_submap_ = "Default";
|
||||||
|
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ class Window : public waybar::AAppIconLabel, public EventHandler {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
struct Workspace {
|
struct Workspace {
|
||||||
int id;
|
int id = 0;
|
||||||
int windows;
|
int windows = 0;
|
||||||
std::string last_window;
|
std::string last_window;
|
||||||
std::string last_window_title;
|
std::string last_window_title;
|
||||||
|
|
||||||
@@ -29,14 +29,14 @@ class Window : public waybar::AAppIconLabel, public EventHandler {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct WindowData {
|
struct WindowData {
|
||||||
bool floating;
|
bool floating = false;
|
||||||
int monitor = -1;
|
int monitor = -1;
|
||||||
std::string class_name;
|
std::string class_name;
|
||||||
std::string initial_class_name;
|
std::string initial_class_name;
|
||||||
std::string title;
|
std::string title;
|
||||||
std::string initial_title;
|
std::string initial_title;
|
||||||
bool fullscreen;
|
bool fullscreen = false;
|
||||||
bool grouped;
|
bool grouped = false;
|
||||||
|
|
||||||
static auto parse(const Json::Value&) -> WindowData;
|
static auto parse(const Json::Value&) -> WindowData;
|
||||||
};
|
};
|
||||||
@@ -47,7 +47,7 @@ class Window : public waybar::AAppIconLabel, public EventHandler {
|
|||||||
void queryActiveWorkspace();
|
void queryActiveWorkspace();
|
||||||
void setClass(const std::string&, bool enable);
|
void setClass(const std::string&, bool enable);
|
||||||
|
|
||||||
bool separateOutputs_;
|
bool separateOutputs_ = false;
|
||||||
std::mutex mutex_;
|
std::mutex mutex_;
|
||||||
const Bar& bar_;
|
const Bar& bar_;
|
||||||
util::JsonParser parser_;
|
util::JsonParser parser_;
|
||||||
@@ -55,11 +55,11 @@ class Window : public waybar::AAppIconLabel, public EventHandler {
|
|||||||
Workspace workspace_;
|
Workspace workspace_;
|
||||||
std::string soloClass_;
|
std::string soloClass_;
|
||||||
std::string lastSoloClass_;
|
std::string lastSoloClass_;
|
||||||
bool solo_;
|
bool solo_ = false;
|
||||||
bool allFloating_;
|
bool allFloating_ = false;
|
||||||
bool swallowing_;
|
bool swallowing_ = false;
|
||||||
bool fullscreen_;
|
bool fullscreen_ = false;
|
||||||
bool focused_;
|
bool focused_ = false;
|
||||||
|
|
||||||
IPC& m_ipc;
|
IPC& m_ipc;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -40,10 +40,11 @@ struct WindowRepr {
|
|||||||
|
|
||||||
class WindowCreationPayload {
|
class WindowCreationPayload {
|
||||||
public:
|
public:
|
||||||
WindowCreationPayload(std::string workspace_name, WindowAddress window_address,
|
WindowCreationPayload(const std::string& workspace_name, WindowAddress window_address,
|
||||||
WindowRepr window_repr);
|
WindowRepr window_repr);
|
||||||
WindowCreationPayload(std::string workspace_name, WindowAddress window_address,
|
WindowCreationPayload(const std::string& workspace_name, WindowAddress window_address,
|
||||||
std::string window_class, std::string window_title, bool is_active);
|
const std::string& window_class, const std::string& window_title,
|
||||||
|
bool is_active);
|
||||||
WindowCreationPayload(Json::Value const& client_data);
|
WindowCreationPayload(Json::Value const& client_data);
|
||||||
|
|
||||||
int incrementTimeSpentUncreated();
|
int incrementTimeSpentUncreated();
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ class Workspace {
|
|||||||
public:
|
public:
|
||||||
explicit Workspace(const Json::Value& workspace_data, Workspaces& workspace_manager,
|
explicit Workspace(const Json::Value& workspace_data, Workspaces& workspace_manager,
|
||||||
const Json::Value& clients_data = Json::Value::nullRef);
|
const Json::Value& clients_data = Json::Value::nullRef);
|
||||||
|
~Workspace();
|
||||||
std::string& selectIcon(std::map<std::string, std::string>& icons_map);
|
std::string& selectIcon(std::map<std::string, std::string>& icons_map);
|
||||||
Gtk::Button& button() { return m_button; };
|
Gtk::Button& button() { return m_button; };
|
||||||
|
|
||||||
@@ -45,6 +46,15 @@ class Workspace {
|
|||||||
bool isUrgent() const { return m_isUrgent; };
|
bool isUrgent() const { return m_isUrgent; };
|
||||||
|
|
||||||
bool handleClicked(GdkEventButton* bt) const;
|
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 setActive(bool value = true) { m_isActive = value; };
|
||||||
void setPersistentRule(bool value = true) { m_isPersistentRule = value; };
|
void setPersistentRule(bool value = true) { m_isPersistentRule = value; };
|
||||||
void setPersistentConfig(bool value = true) { m_isPersistentConfig = value; };
|
void setPersistentConfig(bool value = true) { m_isPersistentConfig = value; };
|
||||||
@@ -80,6 +90,8 @@ class Workspace {
|
|||||||
bool m_isUrgent = false;
|
bool m_isUrgent = false;
|
||||||
bool m_isVisible = false;
|
bool m_isVisible = false;
|
||||||
|
|
||||||
|
sigc::connection m_hoverCheckConnection;
|
||||||
|
|
||||||
std::vector<WindowRepr> m_windowMap;
|
std::vector<WindowRepr> m_windowMap;
|
||||||
|
|
||||||
Gtk::Button m_button;
|
Gtk::Button m_button;
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include <gtkmm/enums.h>
|
#include <gtkmm/enums.h>
|
||||||
#include <gtkmm/label.h>
|
#include <gtkmm/label.h>
|
||||||
#include <json/value.h>
|
#include <json/value.h>
|
||||||
|
#include <sigc++/connection.h>
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <map>
|
#include <map>
|
||||||
@@ -43,6 +44,7 @@ class Workspaces : public AModule, public EventHandler {
|
|||||||
auto moveToMonitor() const -> bool { return m_moveToMonitor; }
|
auto moveToMonitor() const -> bool { return m_moveToMonitor; }
|
||||||
auto enableTaskbar() const -> bool { return m_enableTaskbar; }
|
auto enableTaskbar() const -> bool { return m_enableTaskbar; }
|
||||||
auto taskbarWithIcon() const -> bool { return m_taskbarWithIcon; }
|
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 getBarOutput() const -> std::string { return m_bar.output->name; }
|
||||||
auto formatBefore() const -> std::string { return m_formatBefore; }
|
auto formatBefore() const -> std::string { return m_formatBefore; }
|
||||||
@@ -58,7 +60,7 @@ class Workspaces : public AModule, public EventHandler {
|
|||||||
enum class ActiveWindowPosition { NONE, FIRST, LAST };
|
enum class ActiveWindowPosition { NONE, FIRST, LAST };
|
||||||
auto activeWindowPosition() const -> ActiveWindowPosition { return m_activeWindowPosition; }
|
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; }
|
std::string& getWindowSeparator() { return m_formatWindowSeparator; }
|
||||||
bool isWorkspaceIgnored(std::string const& workspace_name);
|
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::pair<std::string, std::string> splitDoublePayload(std::string const& payload);
|
||||||
static std::tuple<std::string, std::string, std::string> splitTriplePayload(
|
static std::tuple<std::string, std::string, std::string> splitTriplePayload(
|
||||||
std::string const& payload);
|
std::string const& payload);
|
||||||
|
// scroll events
|
||||||
|
bool handleScroll(GdkEventScroll* e) override;
|
||||||
|
|
||||||
// Update methods
|
// Update methods
|
||||||
void doUpdate();
|
void doUpdate();
|
||||||
@@ -145,6 +149,7 @@ class Workspaces : public AModule, public EventHandler {
|
|||||||
bool m_specialVisibleOnly = false;
|
bool m_specialVisibleOnly = false;
|
||||||
bool m_persistentOnly = false;
|
bool m_persistentOnly = false;
|
||||||
bool m_moveToMonitor = false;
|
bool m_moveToMonitor = false;
|
||||||
|
bool m_barScroll = false;
|
||||||
Json::Value m_persistentWorkspaceConfig;
|
Json::Value m_persistentWorkspaceConfig;
|
||||||
|
|
||||||
// Map for windows stored in workspaces not present in the current bar.
|
// 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;
|
std::mutex m_mutex;
|
||||||
const Bar& m_bar;
|
const Bar& m_bar;
|
||||||
Gtk::Box m_box;
|
Gtk::Box m_box;
|
||||||
|
sigc::connection m_scrollEventConnection_;
|
||||||
IPC& m_ipc;
|
IPC& m_ipc;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include <fmt/chrono.h>
|
#include <fmt/chrono.h>
|
||||||
#include <gtkmm/label.h>
|
#include <gtkmm/label.h>
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
@@ -41,6 +42,7 @@ class KeyboardState : public AModule {
|
|||||||
|
|
||||||
struct libinput* libinput_;
|
struct libinput* libinput_;
|
||||||
std::unordered_map<std::string, struct libinput_device*> libinput_devices_;
|
std::unordered_map<std::string, struct libinput_device*> libinput_devices_;
|
||||||
|
std::mutex devices_mutex_; // protects libinput_devices_
|
||||||
std::set<int> binding_keys;
|
std::set<int> binding_keys;
|
||||||
|
|
||||||
util::SleeperThread libinput_thread_, hotplug_thread_;
|
util::SleeperThread libinput_thread_, hotplug_thread_;
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ class Memory : public ALabel {
|
|||||||
std::unordered_map<std::string, unsigned long> meminfo_;
|
std::unordered_map<std::string, unsigned long> meminfo_;
|
||||||
|
|
||||||
util::SleeperThread thread_;
|
util::SleeperThread thread_;
|
||||||
|
|
||||||
|
std::string unit_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace waybar::modules
|
} // namespace waybar::modules
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ class MPD : public ALabel {
|
|||||||
detail::unique_status status_;
|
detail::unique_status status_;
|
||||||
mpd_state state_;
|
mpd_state state_;
|
||||||
detail::unique_song song_;
|
detail::unique_song song_;
|
||||||
|
std::string ellipsis_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
MPD(const std::string&, const Json::Value&);
|
MPD(const std::string&, const Json::Value&);
|
||||||
@@ -44,7 +45,11 @@ class MPD : public ALabel {
|
|||||||
std::string getFilename() const;
|
std::string getFilename() const;
|
||||||
void setLabel();
|
void setLabel();
|
||||||
std::string getStateIcon() const;
|
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
|
// GUI-side methods
|
||||||
bool handlePlayPause(GdkEventButton* const&);
|
bool handlePlayPause(GdkEventButton* const&);
|
||||||
@@ -54,7 +59,6 @@ class MPD : public ALabel {
|
|||||||
void tryConnect();
|
void tryConnect();
|
||||||
void checkErrors(mpd_connection* conn);
|
void checkErrors(mpd_connection* conn);
|
||||||
void fetchState();
|
void fetchState();
|
||||||
void queryMPD();
|
|
||||||
|
|
||||||
inline bool stopped() const { return connection_ && state_ == MPD_STATE_STOP; }
|
inline bool stopped() const { return connection_ && state_ == MPD_STATE_STOP; }
|
||||||
inline bool playing() const { return connection_ && state_ == MPD_STATE_PLAY; }
|
inline bool playing() const { return connection_ && state_ == MPD_STATE_PLAY; }
|
||||||
|
|||||||
@@ -197,7 +197,6 @@ class Context {
|
|||||||
void tryConnect() const;
|
void tryConnect() const;
|
||||||
void checkErrors(mpd_connection*) const;
|
void checkErrors(mpd_connection*) const;
|
||||||
void do_update();
|
void do_update();
|
||||||
void queryMPD() const;
|
|
||||||
void fetchState() const;
|
void fetchState() const;
|
||||||
constexpr mpd_state state() const;
|
constexpr mpd_state state() const;
|
||||||
void emit() const;
|
void emit() const;
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
using namespace std::literals::chrono_literals;
|
||||||
|
|
||||||
inline bool Context::is_connected() const { return mpd_module_->connection_ != nullptr; }
|
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_playing() const { return mpd_module_->playing(); }
|
||||||
inline bool Context::is_paused() const { return mpd_module_->paused(); }
|
inline bool Context::is_paused() const { return mpd_module_->paused(); }
|
||||||
inline bool Context::is_stopped() const { return mpd_module_->stopped(); }
|
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 void Context::tryConnect() const { mpd_module_->tryConnect(); }
|
||||||
inline unique_connection& Context::connection() { return mpd_module_->connection_; }
|
inline unique_connection& Context::connection() { return mpd_module_->connection_; }
|
||||||
constexpr inline mpd_state Context::state() const { return mpd_module_->state_; }
|
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::do_update() { mpd_module_->setLabel(); }
|
||||||
|
|
||||||
inline void Context::checkErrors(mpd_connection* conn) const { mpd_module_->checkErrors(conn); }
|
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::fetchState() const { mpd_module_->fetchState(); }
|
||||||
inline void Context::emit() const { mpd_module_->emit(); }
|
inline void Context::emit() const { mpd_module_->emit(); }
|
||||||
|
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ class Mpris : public ALabel {
|
|||||||
|
|
||||||
PlayerctlPlayerManager* manager;
|
PlayerctlPlayerManager* manager;
|
||||||
PlayerctlPlayer* player;
|
PlayerctlPlayer* player;
|
||||||
|
PlayerctlPlayer* last_active_player_ = nullptr;
|
||||||
std::string lastStatus;
|
std::string lastStatus;
|
||||||
std::string lastPlayer;
|
std::string lastPlayer;
|
||||||
|
|
||||||
|
|||||||
@@ -70,6 +70,9 @@ class Network : public ALabel {
|
|||||||
|
|
||||||
unsigned long long bandwidth_down_total_{0};
|
unsigned long long bandwidth_down_total_{0};
|
||||||
unsigned long long bandwidth_up_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 state_;
|
||||||
std::string essid_;
|
std::string essid_;
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ class EventHandler {
|
|||||||
|
|
||||||
class IPC {
|
class IPC {
|
||||||
public:
|
public:
|
||||||
IPC() { startIPC(); }
|
IPC();
|
||||||
|
|
||||||
void registerForIPC(const std::string& ev, EventHandler* ev_handler);
|
void registerForIPC(const std::string& ev, EventHandler* ev_handler);
|
||||||
void unregisterForIPC(EventHandler* handler);
|
void unregisterForIPC(EventHandler* handler);
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ class Privacy : public AModule {
|
|||||||
|
|
||||||
// Config
|
// Config
|
||||||
Gtk::Box box_;
|
Gtk::Box box_;
|
||||||
|
std::vector<PrivacyItem*> modules_;
|
||||||
uint iconSpacing = 4;
|
uint iconSpacing = 4;
|
||||||
uint iconSize = 20;
|
uint iconSize = 20;
|
||||||
uint transition_duration = 250;
|
uint transition_duration = 250;
|
||||||
|
|||||||
@@ -6,11 +6,6 @@
|
|||||||
#include "util/audio_backend.hpp"
|
#include "util/audio_backend.hpp"
|
||||||
namespace waybar::modules {
|
namespace waybar::modules {
|
||||||
|
|
||||||
enum class PulseaudioSliderTarget {
|
|
||||||
Sink,
|
|
||||||
Source,
|
|
||||||
};
|
|
||||||
|
|
||||||
class PulseaudioSlider : public ASlider {
|
class PulseaudioSlider : public ASlider {
|
||||||
public:
|
public:
|
||||||
PulseaudioSlider(const std::string&, const Json::Value&);
|
PulseaudioSlider(const std::string&, const Json::Value&);
|
||||||
@@ -21,7 +16,15 @@ class PulseaudioSlider : public ASlider {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<util::AudioBackend> backend = nullptr;
|
std::shared_ptr<util::AudioBackend> backend = nullptr;
|
||||||
PulseaudioSliderTarget target = PulseaudioSliderTarget::Sink;
|
util::PulseaudioTarget target = util::PulseaudioTarget::Sink;
|
||||||
|
|
||||||
|
bool zero_on_mute = true;
|
||||||
|
bool unmute_on_volume_change = true;
|
||||||
|
// zero_on_mute and unmute_on_volume_change default to true
|
||||||
|
// in order to maintain previous behaviour when using a
|
||||||
|
// config in which these values are undefined
|
||||||
|
|
||||||
|
bool previously_muted = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace waybar::modules
|
} // namespace waybar::modules
|
||||||
@@ -16,7 +16,7 @@ class Host {
|
|||||||
public:
|
public:
|
||||||
Host(const std::size_t id, const Json::Value&, const Bar&,
|
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(std::unique_ptr<Item>&)>&, const std::function<void()>&);
|
||||||
~Host();
|
~Host();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -28,9 +28,13 @@ class Host {
|
|||||||
static void registerHost(GObject*, GAsyncResult*, gpointer);
|
static void registerHost(GObject*, GAsyncResult*, gpointer);
|
||||||
static void itemRegistered(SnWatcher*, const gchar*, gpointer);
|
static void itemRegistered(SnWatcher*, const gchar*, gpointer);
|
||||||
static void itemUnregistered(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);
|
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_;
|
std::vector<std::unique_ptr<Item>> items_;
|
||||||
const std::string bus_name_;
|
const std::string bus_name_;
|
||||||
@@ -39,10 +43,13 @@ class Host {
|
|||||||
std::size_t watcher_id_;
|
std::size_t watcher_id_;
|
||||||
GCancellable* cancellable_ = nullptr;
|
GCancellable* cancellable_ = nullptr;
|
||||||
SnWatcher* watcher_ = nullptr;
|
SnWatcher* watcher_ = nullptr;
|
||||||
|
sigc::connection retry_connection_;
|
||||||
|
unsigned retry_count_ = 0;
|
||||||
const Json::Value& config_;
|
const Json::Value& config_;
|
||||||
const Bar& bar_;
|
const Bar& bar_;
|
||||||
const std::function<void(std::unique_ptr<Item>&)> on_add_;
|
const std::function<void(std::unique_ptr<Item>&)> on_add_;
|
||||||
const std::function<void(std::unique_ptr<Item>&)> on_remove_;
|
const std::function<void(std::unique_ptr<Item>&)> on_remove_;
|
||||||
|
const std::function<void()> on_update_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace waybar::modules::SNI
|
} // namespace waybar::modules::SNI
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#include <libdbusmenu-gtk/dbusmenu-gtk.h>
|
#include <libdbusmenu-gtk/dbusmenu-gtk.h>
|
||||||
#include <sigc++/trackable.h>
|
#include <sigc++/trackable.h>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
|
||||||
@@ -25,9 +26,13 @@ struct ToolTip {
|
|||||||
|
|
||||||
class Item : public sigc::trackable {
|
class Item : public sigc::trackable {
|
||||||
public:
|
public:
|
||||||
Item(const std::string&, const std::string&, const Json::Value&, const Bar&);
|
Item(const std::string&, const std::string&, const Json::Value&, const Bar&,
|
||||||
|
const std::function<void(Item&)>&, const std::function<void(Item&)>&,
|
||||||
|
const std::function<void()>&);
|
||||||
~Item();
|
~Item();
|
||||||
|
|
||||||
|
bool isReady() const;
|
||||||
|
|
||||||
std::string bus_name;
|
std::string bus_name;
|
||||||
std::string object_path;
|
std::string object_path;
|
||||||
|
|
||||||
@@ -41,9 +46,12 @@ class Item : public sigc::trackable {
|
|||||||
std::string title;
|
std::string title;
|
||||||
std::string icon_name;
|
std::string icon_name;
|
||||||
Glib::RefPtr<Gdk::Pixbuf> icon_pixmap;
|
Glib::RefPtr<Gdk::Pixbuf> icon_pixmap;
|
||||||
|
bool has_custom_icon_ = false;
|
||||||
Glib::RefPtr<Gtk::IconTheme> icon_theme;
|
Glib::RefPtr<Gtk::IconTheme> icon_theme;
|
||||||
std::string overlay_icon_name;
|
std::string overlay_icon_name;
|
||||||
|
Glib::RefPtr<Gdk::Pixbuf> overlay_icon_pixmap;
|
||||||
std::string attention_icon_name;
|
std::string attention_icon_name;
|
||||||
|
Glib::RefPtr<Gdk::Pixbuf> attention_icon_pixmap;
|
||||||
std::string attention_movie_name;
|
std::string attention_movie_name;
|
||||||
std::string icon_theme_path;
|
std::string icon_theme_path;
|
||||||
std::string menu;
|
std::string menu;
|
||||||
@@ -62,6 +70,8 @@ class Item : public sigc::trackable {
|
|||||||
void proxyReady(Glib::RefPtr<Gio::AsyncResult>& result);
|
void proxyReady(Glib::RefPtr<Gio::AsyncResult>& result);
|
||||||
void setProperty(const Glib::ustring& name, Glib::VariantBase& value);
|
void setProperty(const Glib::ustring& name, Glib::VariantBase& value);
|
||||||
void setStatus(const Glib::ustring& value);
|
void setStatus(const Glib::ustring& value);
|
||||||
|
void setReady();
|
||||||
|
void invalidate();
|
||||||
void setCustomIcon(const std::string& id);
|
void setCustomIcon(const std::string& id);
|
||||||
void getUpdatedProperties();
|
void getUpdatedProperties();
|
||||||
void processUpdatedProperties(Glib::RefPtr<Gio::AsyncResult>& result);
|
void processUpdatedProperties(Glib::RefPtr<Gio::AsyncResult>& result);
|
||||||
@@ -69,8 +79,13 @@ class Item : public sigc::trackable {
|
|||||||
const Glib::VariantContainerBase& arguments);
|
const Glib::VariantContainerBase& arguments);
|
||||||
|
|
||||||
void updateImage();
|
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> 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);
|
Glib::RefPtr<Gdk::Pixbuf> getIconByName(const std::string& name, int size);
|
||||||
double getScaledIconSize();
|
double getScaledIconSize();
|
||||||
static void onMenuDestroyed(Item* self, GObject* old_menu_pointer);
|
static void onMenuDestroyed(Item* self, GObject* old_menu_pointer);
|
||||||
@@ -86,8 +101,13 @@ class Item : public sigc::trackable {
|
|||||||
gdouble distance_scrolled_y_ = 0;
|
gdouble distance_scrolled_y_ = 0;
|
||||||
// visibility of items with Status == Passive
|
// visibility of items with Status == Passive
|
||||||
bool show_passive_ = false;
|
bool show_passive_ = false;
|
||||||
|
bool ready_ = false;
|
||||||
|
Glib::ustring status_ = "active";
|
||||||
|
|
||||||
const Bar& bar_;
|
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::DBus::Proxy> proxy_;
|
||||||
Glib::RefPtr<Gio::Cancellable> cancellable_;
|
Glib::RefPtr<Gio::Cancellable> cancellable_;
|
||||||
|
|||||||
@@ -19,9 +19,9 @@ class Tray : public AModule {
|
|||||||
private:
|
private:
|
||||||
void onAdd(std::unique_ptr<Item>& item);
|
void onAdd(std::unique_ptr<Item>& item);
|
||||||
void onRemove(std::unique_ptr<Item>& item);
|
void onRemove(std::unique_ptr<Item>& item);
|
||||||
|
void queueUpdate();
|
||||||
|
|
||||||
static inline std::size_t nb_hosts_ = 0;
|
static inline std::size_t nb_hosts_ = 0;
|
||||||
bool show_passive_ = false;
|
|
||||||
Gtk::Box box_;
|
Gtk::Box box_;
|
||||||
SNI::Watcher::singleton watcher_;
|
SNI::Watcher::singleton watcher_;
|
||||||
SNI::Host host_;
|
SNI::Host host_;
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ class BarIpcClient {
|
|||||||
void onModeUpdate(bool visible_by_modifier);
|
void onModeUpdate(bool visible_by_modifier);
|
||||||
void onUrgencyUpdate(bool visible_by_urgency);
|
void onUrgencyUpdate(bool visible_by_urgency);
|
||||||
void update();
|
void update();
|
||||||
bool isModuleEnabled(std::string name);
|
bool isModuleEnabled(const std::string& name);
|
||||||
|
|
||||||
Bar& bar_;
|
Bar& bar_;
|
||||||
util::JsonParser parser_;
|
util::JsonParser parser_;
|
||||||
|
|||||||
@@ -12,6 +12,8 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "ipc.hpp"
|
#include "ipc.hpp"
|
||||||
|
#include "util/SafeSignal.hpp"
|
||||||
|
#include "util/scoped_fd.hpp"
|
||||||
#include "util/sleeper_thread.hpp"
|
#include "util/sleeper_thread.hpp"
|
||||||
|
|
||||||
namespace waybar::modules::sway {
|
namespace waybar::modules::sway {
|
||||||
@@ -27,8 +29,8 @@ class Ipc {
|
|||||||
std::string payload;
|
std::string payload;
|
||||||
};
|
};
|
||||||
|
|
||||||
sigc::signal<void, const struct ipc_response &> signal_event;
|
::waybar::SafeSignal<const struct ipc_response&> signal_event;
|
||||||
sigc::signal<void, const struct ipc_response &> signal_cmd;
|
::waybar::SafeSignal<const struct ipc_response&> signal_cmd;
|
||||||
|
|
||||||
void sendCmd(uint32_t type, const std::string& payload = "");
|
void sendCmd(uint32_t type, const std::string& payload = "");
|
||||||
void subscribe(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 send(int fd, uint32_t type, const std::string& payload = "");
|
||||||
struct ipc_response recv(int fd);
|
struct ipc_response recv(int fd);
|
||||||
|
|
||||||
int fd_;
|
util::ScopedFd fd_;
|
||||||
int fd_event_;
|
util::ScopedFd fd_event_;
|
||||||
std::mutex mutex_;
|
std::mutex mutex_;
|
||||||
util::SleeperThread thread_;
|
util::SleeperThread thread_;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ class Language : public ALabel, public sigc::trackable {
|
|||||||
void onEvent(const struct Ipc::ipc_response&);
|
void onEvent(const struct Ipc::ipc_response&);
|
||||||
void onCmd(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;
|
auto init_layouts_map(const std::vector<std::string>& used_layouts) -> void;
|
||||||
|
|
||||||
const static std::string XKB_LAYOUT_NAMES_KEY;
|
const static std::string XKB_LAYOUT_NAMES_KEY;
|
||||||
|
|||||||
@@ -24,10 +24,11 @@ class Workspaces : public AModule, public sigc::trackable {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr std::string_view workspace_switch_cmd_ = "workspace {} \"{}\"";
|
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_ =
|
static constexpr std::string_view persistent_workspace_switch_cmd_ =
|
||||||
R"(workspace {} "{}"; move workspace to output "{}"; workspace {} "{}")";
|
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);
|
static int windowRewritePriorityFunction(std::string const& window_rule);
|
||||||
|
|
||||||
void onCmd(const struct Ipc::ipc_response&);
|
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 getIcon(const std::string&, const Json::Value&);
|
||||||
std::string getCycleWorkspace(std::vector<Json::Value>::iterator, bool prev) const;
|
std::string getCycleWorkspace(std::vector<Json::Value>::iterator, bool prev) const;
|
||||||
uint16_t getWorkspaceIndex(const std::string& name) 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;
|
bool handleScroll(GdkEventScroll* /*unused*/) override;
|
||||||
|
|
||||||
const Bar& bar_;
|
const Bar& bar_;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include <giomm/dbusproxy.h>
|
#include <giomm/dbusproxy.h>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "ALabel.hpp"
|
#include "ALabel.hpp"
|
||||||
|
|
||||||
@@ -11,23 +12,42 @@ namespace waybar::modules {
|
|||||||
class SystemdFailedUnits : public ALabel {
|
class SystemdFailedUnits : public ALabel {
|
||||||
public:
|
public:
|
||||||
SystemdFailedUnits(const std::string&, const Json::Value&);
|
SystemdFailedUnits(const std::string&, const Json::Value&);
|
||||||
virtual ~SystemdFailedUnits();
|
virtual ~SystemdFailedUnits() = default;
|
||||||
auto update() -> void override;
|
auto update() -> void override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool hide_on_ok;
|
struct FailedUnit {
|
||||||
std::string format_ok;
|
std::string name;
|
||||||
|
std::string description;
|
||||||
|
std::string load_state;
|
||||||
|
std::string active_state;
|
||||||
|
std::string sub_state;
|
||||||
|
std::string scope;
|
||||||
|
};
|
||||||
|
|
||||||
bool update_pending;
|
bool hide_on_ok_;
|
||||||
std::string system_state, user_state, overall_state;
|
std::string format_ok_;
|
||||||
uint32_t nr_failed_system, nr_failed_user, nr_failed;
|
std::string tooltip_format_;
|
||||||
std::string last_status;
|
std::string tooltip_format_ok_;
|
||||||
Glib::RefPtr<Gio::DBus::Proxy> system_proxy, user_proxy;
|
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,
|
void notify_cb(const Glib::ustring& sender_name, const Glib::ustring& signal_name,
|
||||||
const Glib::VariantContainerBase& arguments);
|
const Glib::VariantContainerBase& arguments);
|
||||||
void RequestFailedUnits();
|
void RequestFailedUnits();
|
||||||
|
void RequestFailedUnitsList();
|
||||||
void RequestSystemState();
|
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();
|
void updateData();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ class Temperature : public ALabel {
|
|||||||
Temperature(const std::string&, const Json::Value&);
|
Temperature(const std::string&, const Json::Value&);
|
||||||
virtual ~Temperature() = default;
|
virtual ~Temperature() = default;
|
||||||
auto update() -> void override;
|
auto update() -> void override;
|
||||||
|
void suspend() override;
|
||||||
|
void resume() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
float getTemperature();
|
float getTemperature();
|
||||||
|
|||||||
@@ -12,6 +12,8 @@
|
|||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
#include "util/scoped_fd.hpp"
|
||||||
|
|
||||||
namespace waybar::modules::wayfire {
|
namespace waybar::modules::wayfire {
|
||||||
|
|
||||||
using EventHandler = std::function<void(const std::string& event)>;
|
using EventHandler = std::function<void(const std::string& event)>;
|
||||||
@@ -71,25 +73,9 @@ struct State {
|
|||||||
auto update_view(const Json::Value& view) -> void;
|
auto update_view(const Json::Value& view) -> void;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Sock {
|
using Sock = util::ScopedFd;
|
||||||
int fd;
|
|
||||||
|
|
||||||
Sock(int fd) : fd{fd} {}
|
class IPC : public std::enable_shared_from_this<IPC> {
|
||||||
~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 {
|
|
||||||
static std::weak_ptr<IPC> instance;
|
static std::weak_ptr<IPC> instance;
|
||||||
Json::CharReaderBuilder reader_builder;
|
Json::CharReaderBuilder reader_builder;
|
||||||
Json::StreamWriterBuilder writer_builder;
|
Json::StreamWriterBuilder writer_builder;
|
||||||
@@ -98,7 +84,7 @@ class IPC {
|
|||||||
State state;
|
State state;
|
||||||
std::mutex state_mutex;
|
std::mutex state_mutex;
|
||||||
|
|
||||||
IPC() { start(); }
|
IPC() = default;
|
||||||
|
|
||||||
static auto connect() -> Sock;
|
static auto connect() -> Sock;
|
||||||
auto receive(Sock& sock) -> Json::Value;
|
auto receive(Sock& sock) -> Json::Value;
|
||||||
|
|||||||
@@ -33,8 +33,8 @@ class Taskbar;
|
|||||||
|
|
||||||
class Task {
|
class Task {
|
||||||
public:
|
public:
|
||||||
Task(const waybar::Bar &, const Json::Value &, Taskbar *,
|
Task(const waybar::Bar&, const Json::Value&, Taskbar*, struct zwlr_foreign_toplevel_handle_v1*,
|
||||||
struct zwlr_foreign_toplevel_handle_v1 *, struct wl_seat *);
|
struct wl_seat*);
|
||||||
~Task();
|
~Task();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include <glibmm/dispatcher.h>
|
#include <glibmm/dispatcher.h>
|
||||||
#include <sigc++/signal.h>
|
#include <sigc++/signal.h>
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
@@ -11,11 +12,6 @@
|
|||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#ifdef __OpenBSD__
|
|
||||||
#define SIGRTMIN SIGUSR1 - 1
|
|
||||||
#define SIGRTMAX SIGUSR1 + 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace waybar {
|
namespace waybar {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -27,6 +23,12 @@ struct SafeSignal : sigc::signal<void(std::decay_t<Args>...)> {
|
|||||||
public:
|
public:
|
||||||
SafeSignal() { dp_.connect(sigc::mem_fun(*this, &SafeSignal::handle_event)); }
|
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>
|
template <typename... EmitArgs>
|
||||||
void emit(EmitArgs&&... args) {
|
void emit(EmitArgs&&... args) {
|
||||||
if (main_tid_ == std::this_thread::get_id()) {
|
if (main_tid_ == std::this_thread::get_id()) {
|
||||||
@@ -41,6 +43,9 @@ struct SafeSignal : sigc::signal<void(std::decay_t<Args>...)> {
|
|||||||
} else {
|
} else {
|
||||||
{
|
{
|
||||||
std::unique_lock lock(mutex_);
|
std::unique_lock lock(mutex_);
|
||||||
|
if (max_queued_events_ != 0 && queue_.size() >= max_queued_events_) {
|
||||||
|
queue_.pop();
|
||||||
|
}
|
||||||
queue_.emplace(std::forward<EmitArgs>(args)...);
|
queue_.emplace(std::forward<EmitArgs>(args)...);
|
||||||
}
|
}
|
||||||
dp_.emit();
|
dp_.emit();
|
||||||
@@ -60,6 +65,15 @@ struct SafeSignal : sigc::signal<void(std::decay_t<Args>...)> {
|
|||||||
using signal_t::emit_reverse;
|
using signal_t::emit_reverse;
|
||||||
using signal_t::make_slot;
|
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() {
|
void handle_event() {
|
||||||
for (std::unique_lock lock(mutex_); !queue_.empty(); lock.lock()) {
|
for (std::unique_lock lock(mutex_); !queue_.empty(); lock.lock()) {
|
||||||
auto args = queue_.front();
|
auto args = queue_.front();
|
||||||
@@ -72,6 +86,7 @@ struct SafeSignal : sigc::signal<void(std::decay_t<Args>...)> {
|
|||||||
Glib::Dispatcher dp_;
|
Glib::Dispatcher dp_;
|
||||||
std::mutex mutex_;
|
std::mutex mutex_;
|
||||||
std::queue<arg_tuple_t> queue_;
|
std::queue<arg_tuple_t> queue_;
|
||||||
|
std::size_t max_queued_events_ = 4096;
|
||||||
const std::thread::id main_tid_ = std::this_thread::get_id();
|
const std::thread::id main_tid_ = std::this_thread::get_id();
|
||||||
// cache functor for signal emission to avoid recreating it on each event
|
// cache functor for signal emission to avoid recreating it on each event
|
||||||
const slot_t cached_fn_ = make_slot();
|
const slot_t cached_fn_ = make_slot();
|
||||||
|
|||||||
@@ -14,6 +14,11 @@
|
|||||||
|
|
||||||
namespace waybar::util {
|
namespace waybar::util {
|
||||||
|
|
||||||
|
enum class PulseaudioTarget {
|
||||||
|
Sink,
|
||||||
|
Source,
|
||||||
|
};
|
||||||
|
|
||||||
class AudioBackend {
|
class AudioBackend {
|
||||||
private:
|
private:
|
||||||
static void subscribeCb(pa_context*, pa_subscription_event_type_t, uint32_t, void*);
|
static void subscribeCb(pa_context*, pa_subscription_event_type_t, uint32_t, void*);
|
||||||
@@ -92,6 +97,10 @@ class AudioBackend {
|
|||||||
void toggleSourceMute();
|
void toggleSourceMute();
|
||||||
void toggleSourceMute(bool);
|
void toggleSourceMute(bool);
|
||||||
|
|
||||||
|
uint16_t getVolume(PulseaudioTarget) const;
|
||||||
|
bool getMuted(PulseaudioTarget) const;
|
||||||
|
void unmute(PulseaudioTarget);
|
||||||
|
|
||||||
bool isBluetooth();
|
bool isBluetooth();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ extern std::list<pid_t> reap;
|
|||||||
|
|
||||||
namespace waybar::util::command {
|
namespace waybar::util::command {
|
||||||
|
|
||||||
|
constexpr int kExecFailureExitCode = 127;
|
||||||
|
|
||||||
struct res {
|
struct res {
|
||||||
int exit_code;
|
int exit_code;
|
||||||
std::string out;
|
std::string out;
|
||||||
@@ -59,6 +61,7 @@ inline int close(FILE* fp, pid_t pid) {
|
|||||||
spdlog::debug("Cmd continued");
|
spdlog::debug("Cmd continued");
|
||||||
} else if (ret == -1) {
|
} else if (ret == -1) {
|
||||||
spdlog::debug("waitpid failed: {}", strerror(errno));
|
spdlog::debug("waitpid failed: {}", strerror(errno));
|
||||||
|
break;
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -109,11 +112,13 @@ inline FILE* open(const std::string& cmd, int& pid, const std::string& output_na
|
|||||||
::close(fd[0]);
|
::close(fd[0]);
|
||||||
dup2(fd[1], 1);
|
dup2(fd[1], 1);
|
||||||
setpgid(child_pid, child_pid);
|
setpgid(child_pid, child_pid);
|
||||||
if (output_name != "") {
|
if (!output_name.empty()) {
|
||||||
setenv("WAYBAR_OUTPUT_NAME", output_name.c_str(), 1);
|
setenv("WAYBAR_OUTPUT_NAME", output_name.c_str(), 1);
|
||||||
}
|
}
|
||||||
execlp("/bin/sh", "sh", "-c", cmd.c_str(), (char*)0);
|
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 {
|
} else {
|
||||||
::close(fd[1]);
|
::close(fd[1]);
|
||||||
}
|
}
|
||||||
@@ -138,7 +143,7 @@ inline struct res execNoRead(const std::string& cmd) {
|
|||||||
return {WEXITSTATUS(stat), ""};
|
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;
|
if (cmd == "") return -1;
|
||||||
|
|
||||||
pid_t pid = fork();
|
pid_t pid = fork();
|
||||||
@@ -157,8 +162,13 @@ inline int32_t forkExec(const std::string& cmd) {
|
|||||||
err = pthread_sigmask(SIG_UNBLOCK, &mask, nullptr);
|
err = pthread_sigmask(SIG_UNBLOCK, &mask, nullptr);
|
||||||
if (err != 0) spdlog::error("pthread_sigmask in forkExec failed: {}", strerror(err));
|
if (err != 0) spdlog::error("pthread_sigmask in forkExec failed: {}", strerror(err));
|
||||||
setpgid(pid, pid);
|
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);
|
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 {
|
} else {
|
||||||
reap_mtx.lock();
|
reap_mtx.lock();
|
||||||
reap.push_back(pid);
|
reap.push_back(pid);
|
||||||
@@ -169,4 +179,6 @@ inline int32_t forkExec(const std::string& cmd) {
|
|||||||
return pid;
|
return pid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline int32_t forkExec(const std::string& cmd) { return forkExec(cmd, ""); }
|
||||||
|
|
||||||
} // namespace waybar::util::command
|
} // namespace waybar::util::command
|
||||||
|
|||||||
@@ -14,12 +14,14 @@ struct pollfd;
|
|||||||
namespace waybar {
|
namespace waybar {
|
||||||
class CssReloadHelper {
|
class CssReloadHelper {
|
||||||
public:
|
public:
|
||||||
CssReloadHelper(std::string cssFile, std::function<void()> callback);
|
CssReloadHelper(std::string cssFile, std::function<void(const std::string&)> callback);
|
||||||
|
|
||||||
virtual ~CssReloadHelper() = default;
|
virtual ~CssReloadHelper() = default;
|
||||||
|
|
||||||
virtual void monitorChanges();
|
virtual void monitorChanges();
|
||||||
|
|
||||||
|
virtual void changeCssFile(const std::string& newCssFile);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::vector<std::string> parseImports(const std::string& cssFile);
|
std::vector<std::string> parseImports(const std::string& cssFile);
|
||||||
|
|
||||||
@@ -42,7 +44,7 @@ class CssReloadHelper {
|
|||||||
private:
|
private:
|
||||||
std::string m_cssFile;
|
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;
|
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 <codecvt>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <locale>
|
#include <locale>
|
||||||
|
#include <memory>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
|
|
||||||
#if (FMT_VERSION >= 90000)
|
#if (FMT_VERSION >= 90000)
|
||||||
@@ -26,19 +27,25 @@ class JsonParser {
|
|||||||
Json::Value root;
|
Json::Value root;
|
||||||
|
|
||||||
// replace all occurrences of "\x" with "\u00", because JSON doesn't allow "\x" escape sequences
|
// 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;
|
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);
|
throw std::runtime_error("Error parsing JSON: " + errs);
|
||||||
}
|
}
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Json::CharReaderBuilder m_readerBuilder;
|
|
||||||
|
|
||||||
static std::string replaceHexadecimalEscape(const std::string& str) {
|
static std::string replaceHexadecimalEscape(const std::string& str) {
|
||||||
static std::regex re("\\\\x");
|
static std::regex re("\\\\x");
|
||||||
return std::regex_replace(str, re, "\\u00");
|
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
|
#pragma once
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
@@ -31,8 +32,8 @@ class SleeperThread {
|
|||||||
|
|
||||||
SleeperThread(std::function<void()> func)
|
SleeperThread(std::function<void()> func)
|
||||||
: thread_{[this, func] {
|
: thread_{[this, func] {
|
||||||
while (do_run_) {
|
while (do_run_.load(std::memory_order_relaxed)) {
|
||||||
signal_ = false;
|
signal_.store(false, std::memory_order_relaxed);
|
||||||
func();
|
func();
|
||||||
}
|
}
|
||||||
}} {
|
}} {
|
||||||
@@ -42,9 +43,18 @@ class SleeperThread {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SleeperThread& operator=(std::function<void()> func) {
|
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] {
|
thread_ = std::thread([this, func] {
|
||||||
while (do_run_) {
|
while (do_run_.load(std::memory_order_relaxed)) {
|
||||||
signal_ = false;
|
signal_.store(false, std::memory_order_relaxed);
|
||||||
func();
|
func();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -56,24 +66,34 @@ class SleeperThread {
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isRunning() const { return do_run_; }
|
bool isRunning() const { return do_run_.load(std::memory_order_relaxed); }
|
||||||
|
|
||||||
auto sleep() {
|
auto sleep() {
|
||||||
std::unique_lock lk(mutex_);
|
std::unique_lock lk(mutex_);
|
||||||
CancellationGuard cancel_lock;
|
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) {
|
auto sleep_for(std::chrono::system_clock::duration dur) {
|
||||||
std::unique_lock lk(mutex_);
|
std::unique_lock lk(mutex_);
|
||||||
CancellationGuard cancel_lock;
|
CancellationGuard cancel_lock;
|
||||||
|
|
||||||
|
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();
|
constexpr auto max_time_point = std::chrono::steady_clock::time_point::max();
|
||||||
auto wait_end = max_time_point;
|
auto wait_end = max_time_point;
|
||||||
auto now = std::chrono::steady_clock::now();
|
auto now = std::chrono::steady_clock::now();
|
||||||
if (now < max_time_point - dur) {
|
if (now < max_time_point - dur) {
|
||||||
wait_end = now + 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(
|
auto sleep_until(
|
||||||
@@ -81,22 +101,30 @@ class SleeperThread {
|
|||||||
time_point) {
|
time_point) {
|
||||||
std::unique_lock lk(mutex_);
|
std::unique_lock lk(mutex_);
|
||||||
CancellationGuard cancel_lock;
|
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() {
|
void wake_up() {
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lck(mutex_);
|
std::lock_guard<std::mutex> lck(mutex_);
|
||||||
signal_ = true;
|
signal_.store(true, std::memory_order_relaxed);
|
||||||
}
|
}
|
||||||
condvar_.notify_all();
|
condvar_.notify_all();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto stop() {
|
void stop() {
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lck(mutex_);
|
std::lock_guard<std::mutex> lck(mutex_);
|
||||||
signal_ = true;
|
signal_.store(true, std::memory_order_relaxed);
|
||||||
do_run_ = false;
|
do_run_.store(false, std::memory_order_relaxed);
|
||||||
}
|
}
|
||||||
condvar_.notify_all();
|
condvar_.notify_all();
|
||||||
auto handle = thread_.native_handle();
|
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() {
|
~SleeperThread() {
|
||||||
connection_.disconnect();
|
connection_.disconnect();
|
||||||
stop();
|
stop();
|
||||||
@@ -118,9 +157,10 @@ class SleeperThread {
|
|||||||
std::thread thread_;
|
std::thread thread_;
|
||||||
std::condition_variable condvar_;
|
std::condition_variable condvar_;
|
||||||
std::mutex mutex_;
|
std::mutex mutex_;
|
||||||
bool do_run_ = true;
|
std::atomic<bool> do_run_ = true;
|
||||||
bool signal_ = false;
|
std::atomic<bool> signal_ = false;
|
||||||
sigc::connection connection_;
|
sigc::connection connection_;
|
||||||
|
bool is_paused_{false};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace waybar::util
|
} // 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 ++
|
default: false ++
|
||||||
Option to use the battery design capacity instead of its current maximal capacity.
|
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*: ++
|
*interval*: ++
|
||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
default: 60 ++
|
default: 60 ++
|
||||||
@@ -91,6 +96,11 @@ The *battery* module displays the current capacity and state (eg. charging) of y
|
|||||||
typeof: string ++
|
typeof: string ++
|
||||||
Command to execute when scrolling up on the module.
|
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*: ++
|
*on-scroll-down*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
Command to execute when scrolling down on the module.
|
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>-<state>*
|
||||||
- *on-<status>-<capacity>*
|
- *on-<status>-<capacity>*
|
||||||
|
- *on-<status>*
|
||||||
|
|
||||||
Where:
|
Where:
|
||||||
|
|
||||||
@@ -198,7 +209,9 @@ Where:
|
|||||||
"events": {
|
"events": {
|
||||||
"on-discharging-warning": "notify-send -u normal 'Low Battery'",
|
"on-discharging-warning": "notify-send -u normal 'Low Battery'",
|
||||||
"on-discharging-critical": "notify-send -u critical 'Very 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": "{capacity}% {icon}",
|
||||||
"format-icons": ["", "", "", "", ""],
|
"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.
|
*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
|
# FILES
|
||||||
|
|
||||||
@@ -32,6 +34,10 @@ libcava lives in:
|
|||||||
:[ string
|
:[ string
|
||||||
:[
|
:[
|
||||||
:< Path where cava configuration file is placed to
|
:< Path where cava configuration file is placed to
|
||||||
|
|[ *method* \[output\]
|
||||||
|
:[ string
|
||||||
|
:[
|
||||||
|
:< Manages which frontend Waybar cava module should use. Values: raw, sdl_glsl
|
||||||
|[ *framerate*
|
|[ *framerate*
|
||||||
:[ integer
|
:[ integer
|
||||||
:[ 30
|
:[ 30
|
||||||
@@ -43,7 +49,7 @@ libcava lives in:
|
|||||||
|[ *sensitivity*
|
|[ *sensitivity*
|
||||||
:[ integer
|
:[ integer
|
||||||
:[ 100
|
:[ 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*
|
|[ *bars*
|
||||||
:[ integer
|
:[ integer
|
||||||
:[ 12
|
:[ 12
|
||||||
@@ -68,7 +74,7 @@ libcava lives in:
|
|||||||
:[ string
|
:[ string
|
||||||
:[
|
:[
|
||||||
:[ Widget's text after sleep_timer elapsed (hide_on_silence has to be false)
|
:[ Widget's text after sleep_timer elapsed (hide_on_silence has to be false)
|
||||||
|[ *method*
|
|[ *method* \[input\]
|
||||||
:[ string
|
:[ string
|
||||||
:[ pulse
|
:[ pulse
|
||||||
:[ Audio capturing method. Possible methods are: pipewire, pulse, alsa, fifo, sndio or shmem
|
:[ Audio capturing method. Possible methods are: pipewire, pulse, alsa, fifo, sndio or shmem
|
||||||
@@ -105,9 +111,9 @@ libcava lives in:
|
|||||||
:[ false
|
:[ false
|
||||||
:[ Disables or enables the so-called "Monstercat smoothing" with or without "waves"
|
:[ Disables or enables the so-called "Monstercat smoothing" with or without "waves"
|
||||||
|[ *noise_reduction*
|
|[ *noise_reduction*
|
||||||
:[ double
|
:[ integer
|
||||||
:[ 0.77
|
:[ 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
|
:[ 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*
|
|[ *input_delay*
|
||||||
:[ integer
|
:[ integer
|
||||||
:[ 2
|
:[ 2
|
||||||
@@ -119,11 +125,11 @@ libcava lives in:
|
|||||||
|[ *data_format*
|
|[ *data_format*
|
||||||
:[ string
|
:[ string
|
||||||
:[ asci
|
:[ 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*
|
|[ *raw_target*
|
||||||
:[ string
|
:[ string
|
||||||
:[ /dev/stdout
|
:[ /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*
|
|[ *menu*
|
||||||
:[ string
|
:[ string
|
||||||
:[
|
:[
|
||||||
@@ -136,6 +142,50 @@ libcava lives in:
|
|||||||
:[ array
|
:[ array
|
||||||
:[
|
:[
|
||||||
:[ The actions corresponding to the buttons of the menu.
|
:[ 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:
|
Configuration can be provided as:
|
||||||
- The only cava configuration file which is provided through *cava_config*. The rest configuration can be skipped
|
- 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
|
- iniparser
|
||||||
- fftw3
|
- fftw3
|
||||||
|
- epoxy (GLSL frontend only)
|
||||||
|
|
||||||
# SOLVING ISSUES
|
# 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*
|
||||||
- *#cava.silent* Applied after no sound has been detected for sleep_timer seconds
|
- *#cava.silent* Applied after no sound has been detected for sleep_timer seconds
|
||||||
- *#cava.updated* Applied when a new frame is shown
|
- *#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": {
|
"clock": {
|
||||||
"format": "{:%H:%M} ",
|
"format": "{:%H:%M} ",
|
||||||
"format-alt": "{:%A, %B %d, %Y (%R)} ",
|
"format-alt": "{:%A, %B %d, %Y (%R)} ",
|
||||||
"tooltip-format": "<tt><small>{calendar}</small></tt>",
|
"tooltip-format": "<tt><small>{calendar}</small></tt>",
|
||||||
"calendar": {
|
"calendar": {
|
||||||
"mode" : "year",
|
"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}"
|
"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
|
# STYLE
|
||||||
|
|
||||||
- *#clock*
|
- *#clock*
|
||||||
@@ -287,7 +299,7 @@ Example of working config
|
|||||||
```
|
```
|
||||||
"clock": {
|
"clock": {
|
||||||
"format": "{:%H:%M} ",
|
"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>",
|
"tooltip-format": "\n<span size='9pt' font='WenQuanYi Zen Hei Mono'>{calendar}</span>",
|
||||||
"calendar": {
|
"calendar": {
|
||||||
"mode" : "year",
|
"mode" : "year",
|
||||||
|
|||||||
@@ -92,6 +92,12 @@ The *cpu* module displays the current CPU utilization.
|
|||||||
|
|
||||||
*{load}*: Current CPU load.
|
*{load}*: Current CPU load.
|
||||||
|
|
||||||
|
*{load1}*: CPU load average over the last minute.
|
||||||
|
|
||||||
|
*{load5}*: CPU load average over the last 5 minutes.
|
||||||
|
|
||||||
|
*{load15}*: CPU load average over the last 15 minutes.
|
||||||
|
|
||||||
*{usage}*: Current overall CPU usage.
|
*{usage}*: Current overall CPU usage.
|
||||||
|
|
||||||
*{usage*{n}*}*: Current CPU core n usage. Cores are numbered from zero, so first core will be {usage0} and 4th will be {usage3}.
|
*{usage*{n}*}*: Current CPU core n usage. Cores are numbered from zero, so first core will be {usage0} and 4th will be {usage3}.
|
||||||
|
|||||||
@@ -234,7 +234,6 @@ $text\\n$tooltip\\n$class*
|
|||||||
```
|
```
|
||||||
"custom/pacman": {
|
"custom/pacman": {
|
||||||
"format": "{text} ",
|
"format": "{text} ",
|
||||||
"interval": 3600, // every hour
|
|
||||||
"exec": "checkupdates | wc -l", // # of updates
|
"exec": "checkupdates | wc -l", // # of updates
|
||||||
"exec-if": "exit 0", // always run; consider advanced run conditions
|
"exec-if": "exit 0", // always run; consider advanced run conditions
|
||||||
"on-click": "termite -e 'sudo pacman -Syu'; pkill -SIGRTMIN+8 waybar", // update system
|
"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
|
# STYLE
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ Addressed by *dwl/tags*
|
|||||||
|
|
||||||
- *#tags button*
|
- *#tags button*
|
||||||
- *#tags button.occupied*
|
- *#tags button.occupied*
|
||||||
|
- *#tags button.empty*
|
||||||
- *#tags button.focused*
|
- *#tags button.focused*
|
||||||
- *#tags button.urgent*
|
- *#tags button.urgent*
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ Feral Gamemode optimizations.
|
|||||||
|
|
||||||
*glyph*: ++
|
*glyph*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
default: ++
|
default: ++
|
||||||
The string icon to display. Only visible if *use-icon* is set to false.
|
The string icon to display. Only visible if *use-icon* is set to false.
|
||||||
|
|
||||||
*icon-name*: ++
|
*icon-name*: ++
|
||||||
@@ -82,7 +82,7 @@ Feral Gamemode optimizations.
|
|||||||
"gamemode": {
|
"gamemode": {
|
||||||
"format": "{glyph}",
|
"format": "{glyph}",
|
||||||
"format-alt": "{glyph} {count}",
|
"format-alt": "{glyph} {count}",
|
||||||
"glyph": "",
|
"glyph": "",
|
||||||
"hide-not-running": true,
|
"hide-not-running": true,
|
||||||
"use-icon": true,
|
"use-icon": true,
|
||||||
"icon-name": "input-gaming-symbolic",
|
"icon-name": "input-gaming-symbolic",
|
||||||
|
|||||||
@@ -38,6 +38,20 @@ Addressed by *hyprland/language*
|
|||||||
typeof: array ++
|
typeof: array ++
|
||||||
The actions corresponding to the buttons of the menu.
|
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*: ++
|
*expand*: ++
|
||||||
typeof: bool ++
|
typeof: bool ++
|
||||||
default: false ++
|
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
|
# STYLE
|
||||||
|
|
||||||
- *#language*
|
- *#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.
|
Otherwise, the workspace will open on the monitor where it was previously assigned.
|
||||||
Analog to using `focusworkspaceoncurrentmonitor` dispatcher instead of `workspace` in Hyprland.
|
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*: ++
|
*ignore-workspaces*: ++
|
||||||
typeof: array ++
|
typeof: array ++
|
||||||
default: [] ++
|
default: [] ++
|
||||||
|
|||||||
@@ -26,7 +26,8 @@ The *image* module displays an image from a path.
|
|||||||
*interval*: ++
|
*interval*: ++
|
||||||
typeof: integer or float ++
|
typeof: integer or float ++
|
||||||
The interval (in seconds) to re-render the image. ++
|
The interval (in seconds) to re-render the image. ++
|
||||||
Minimum value is 0.001 (1ms). Values smaller than 1ms will be set to 1ms. ++
|
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. ++
|
This is useful if the contents of *path* changes. ++
|
||||||
If no *interval* is defined, the image will only be rendered once.
|
If no *interval* is defined, the image will only be rendered once.
|
||||||
|
|
||||||
|
|||||||
+12
-6
@@ -102,23 +102,29 @@ Addressed by *memory*
|
|||||||
default: false ++
|
default: false ++
|
||||||
Enables this module to consume all left over space dynamically.
|
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
|
# FORMAT REPLACEMENTS
|
||||||
|
|
||||||
*{percentage}*: Percentage of memory in use.
|
*{percentage}*: Percentage of memory in use.
|
||||||
|
|
||||||
*{swapPercentage}*: Percentage of swap 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
|
*{swapState}*: Signals if swap is activated or not
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,17 @@ The volume can be controlled by dragging the slider across the bar or clicking o
|
|||||||
default: false ++
|
default: false ++
|
||||||
Enables this module to consume all left over space dynamically.
|
Enables this module to consume all left over space dynamically.
|
||||||
|
|
||||||
|
*zero-on-mute*: ++
|
||||||
|
typeof: bool ++
|
||||||
|
default: true ++
|
||||||
|
`true` = The slider will be set to `min` when the source/sink is muted. ++
|
||||||
|
`false` = The slider will continue to show the unmuted volume level when the source/sink is muted.
|
||||||
|
|
||||||
|
*unmute-on-volume-change*: ++
|
||||||
|
typeof: bool ++
|
||||||
|
default: true ++
|
||||||
|
Specifies whether to unmute a muted souce/sink when its volume is changed by the user moving the slider.
|
||||||
|
|
||||||
# EXAMPLES
|
# EXAMPLES
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -41,7 +52,9 @@ The volume can be controlled by dragging the slider across the bar or clicking o
|
|||||||
"pulseaudio/slider": {
|
"pulseaudio/slider": {
|
||||||
"min": 0,
|
"min": 0,
|
||||||
"max": 100,
|
"max": 100,
|
||||||
"orientation": "horizontal"
|
"orientation": "horizontal",
|
||||||
|
"zero-on-mute": false,
|
||||||
|
"unmute-on-volume-change": false
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -52,6 +65,9 @@ The slider is a component with multiple CSS Nodes, of which the following are ex
|
|||||||
*#pulseaudio-slider*: ++
|
*#pulseaudio-slider*: ++
|
||||||
Controls the style of the box *around* the slider and bar.
|
Controls the style of the box *around* the slider and bar.
|
||||||
|
|
||||||
|
*#pulseaudio-slider.muted*: ++
|
||||||
|
Controls the style when the audio source/sink is muted.
|
||||||
|
|
||||||
*#pulseaudio-slider slider*: ++
|
*#pulseaudio-slider slider*: ++
|
||||||
Controls the style of the slider handle.
|
Controls the style of the slider handle.
|
||||||
|
|
||||||
@@ -77,12 +93,16 @@ The slider is a component with multiple CSS Nodes, of which the following are ex
|
|||||||
min-height: 80px;
|
min-height: 80px;
|
||||||
min-width: 10px;
|
min-width: 10px;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
background-color: black;
|
background: black;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pulseaudio-slider highlight {
|
#pulseaudio-slider highlight {
|
||||||
min-width: 10px;
|
min-width: 10px;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
background-color: green;
|
background: green;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pulseaudio-slider.muted highlight {
|
||||||
|
background-color: orange;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -97,7 +97,11 @@ Additionally, you can control the volume by scrolling *up* or *down* while the c
|
|||||||
|
|
||||||
*reverse-scrolling*: ++
|
*reverse-scrolling*: ++
|
||||||
typeof: bool ++
|
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*: ++
|
*tooltip*: ++
|
||||||
typeof: bool ++
|
typeof: bool ++
|
||||||
@@ -173,9 +177,9 @@ to be selected when the corresponding audio device is muted. This applies to *de
|
|||||||
"format-icons": {
|
"format-icons": {
|
||||||
"alsa_output.pci-0000_00_1f.3.analog-stereo": "",
|
"alsa_output.pci-0000_00_1f.3.analog-stereo": "",
|
||||||
"alsa_output.pci-0000_00_1f.3.analog-stereo-muted": "",
|
"alsa_output.pci-0000_00_1f.3.analog-stereo-muted": "",
|
||||||
"headphones": "",
|
"headphone": "",
|
||||||
"handsfree": "",
|
"hands-free": "",
|
||||||
"headset": "",
|
"headset": "",
|
||||||
"phone": "",
|
"phone": "",
|
||||||
"phone-muted": "",
|
"phone-muted": "",
|
||||||
"portable": "",
|
"portable": "",
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ Addressed by *river/mode*
|
|||||||
|
|
||||||
```
|
```
|
||||||
"river/mode": {
|
"river/mode": {
|
||||||
"format": " {}"
|
"format": " {}"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -8,13 +8,27 @@ waybar-styles - using stylesheets for waybar
|
|||||||
|
|
||||||
Waybar uses Cascading Style Sheets (CSS) to configure its appearance.
|
Waybar uses Cascading Style Sheets (CSS) to configure its appearance.
|
||||||
|
|
||||||
It uses the first file found in this search order:
|
When the system appearance is light, Waybar first looks for *style-light.css*.
|
||||||
|
When the system appearance is dark, Waybar first looks for *style-dark.css*.
|
||||||
|
If no appearance-specific stylesheet is found, it falls back to *style.css*.
|
||||||
|
|
||||||
- *$XDG_CONFIG_HOME/waybar/style.css*
|
Waybar uses the first file found in this search order for each stylesheet name:
|
||||||
- *~/.config/waybar/style.css*
|
|
||||||
- *~/waybar/style.css*
|
- *$XDG_CONFIG_HOME/waybar/<stylesheet>*
|
||||||
- */etc/xdg/waybar/style.css*
|
- *~/.config/waybar/<stylesheet>*
|
||||||
- *@sysconfdir@/xdg/waybar/style.css*
|
- *~/waybar/<stylesheet>*
|
||||||
|
- */etc/xdg/waybar/<stylesheet>*
|
||||||
|
- *@sysconfdir@/xdg/waybar/<stylesheet>*
|
||||||
|
|
||||||
|
For example, a light theme is loaded from the first available file among:
|
||||||
|
|
||||||
|
- *$XDG_CONFIG_HOME/waybar/style-light.css*
|
||||||
|
- *~/.config/waybar/style-light.css*
|
||||||
|
- *~/waybar/style-light.css*
|
||||||
|
- */etc/xdg/waybar/style-light.css*
|
||||||
|
- *@sysconfdir@/xdg/waybar/style-light.css*
|
||||||
|
|
||||||
|
If no file is found there, Waybar repeats the same search using *style.css*.
|
||||||
|
|
||||||
# EXAMPLE
|
# EXAMPLE
|
||||||
|
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ Addressed by *sway/mode*
|
|||||||
|
|
||||||
```
|
```
|
||||||
"sway/mode": {
|
"sway/mode": {
|
||||||
"format": " {}",
|
"format": " {}",
|
||||||
"max-length": 50
|
"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": "<span size='larger'>{name}</span> {windows}",
|
||||||
"format-window-separator": " | ",
|
"format-window-separator": " | ",
|
||||||
"window-rewrite-default": "{name}",
|
"window-rewrite-default": "{name}",
|
||||||
"window-format": "<span color='#e0e0e0'>{name}</span>",
|
|
||||||
"window-rewrite": {
|
"window-rewrite": {
|
||||||
"class<firefox>": "",
|
"class<firefox>": "",
|
||||||
"class<kitty>": "k",
|
"class<kitty>": "k",
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ Addressed by *systemd-failed-units*
|
|||||||
|
|
||||||
*format-ok*: ++
|
*format-ok*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
This format is used when there is no failing units.
|
This format is used when there are no failing units.
|
||||||
|
|
||||||
*user*: ++
|
*user*: ++
|
||||||
typeof: bool ++
|
typeof: bool ++
|
||||||
@@ -34,15 +34,30 @@ Addressed by *systemd-failed-units*
|
|||||||
*hide-on-ok*: ++
|
*hide-on-ok*: ++
|
||||||
typeof: bool ++
|
typeof: bool ++
|
||||||
default: *true* ++
|
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*: ++
|
*menu*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
Action that popups the menu.
|
Action that pops up the menu.
|
||||||
|
|
||||||
*menu-file*: ++
|
*menu-file*: ++
|
||||||
typeof: string ++
|
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*
|
GtkMenu with id *menu*
|
||||||
|
|
||||||
*menu-actions*: ++
|
*menu-actions*: ++
|
||||||
@@ -62,11 +77,23 @@ Addressed by *systemd-failed-units*
|
|||||||
|
|
||||||
*{nr_failed}*: Number of total 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
|
# EXAMPLES
|
||||||
|
|
||||||
@@ -77,6 +104,8 @@ Addressed by *systemd-failed-units*
|
|||||||
"format-ok": "✓",
|
"format-ok": "✓",
|
||||||
"system": true,
|
"system": true,
|
||||||
"user": false,
|
"user": false,
|
||||||
|
"tooltip-format": "{nr_failed} failed units:\n{failed_units_list}",
|
||||||
|
"tooltip-unit-format": "{scope}: {name} ({active_state})",
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,12 @@ Addressed by *temperature*
|
|||||||
The path of the hwmon-directory of the device, e.g. */sys/devices/pci0000:00/0000:00:18.3/hwmon*. (Note that the subdirectory *hwmon/hwmon#*, where *#* is a number is not part of the path!) Has to be used together with *input-filename*.
|
The path of the hwmon-directory of the device, e.g. */sys/devices/pci0000:00/0000:00:18.3/hwmon*. (Note that the subdirectory *hwmon/hwmon#*, where *#* is a number is not part of the path!) Has to be used together with *input-filename*.
|
||||||
This can also be an array of strings, for which, it just works like *hwmon-path*.
|
This can also be an array of strings, for which, it just works like *hwmon-path*.
|
||||||
|
|
||||||
|
*hwmon-name*: ++
|
||||||
|
typeof: string ++
|
||||||
|
Select a hwmon device by its name (from /sys/class/hwmon/\*/name), e.g. *amdgpu*.
|
||||||
|
Requires *input-filename* to be set.
|
||||||
|
Cannot be used together with *hwmon-path* or *hwmon-path-abs*.
|
||||||
|
|
||||||
*input-filename*: ++
|
*input-filename*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
The temperature filename of your *hwmon-path-abs*, e.g. *temp1_input*
|
The temperature filename of your *hwmon-path-abs*, e.g. *temp1_input*
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ The *wireplumber* module displays the current volume reported by WirePlumber.
|
|||||||
```
|
```
|
||||||
"wireplumber#sink": {
|
"wireplumber#sink": {
|
||||||
"format": "{volume}% {icon}",
|
"format": "{volume}% {icon}",
|
||||||
"format-muted": "",
|
"format-muted": "",
|
||||||
"format-icons": ["", "", ""],
|
"format-icons": ["", "", ""],
|
||||||
"on-click": "helvum",
|
"on-click": "helvum",
|
||||||
"on-click-right": "wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle",
|
"on-click-right": "wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle",
|
||||||
|
|||||||
@@ -61,6 +61,11 @@ Addressed by *wlr/taskbar*
|
|||||||
typeof: string ++
|
typeof: string ++
|
||||||
The alignment of the text within the module's box, allowing options 'left', 'right', or 'center' to define the positioning.
|
The alignment of the text within the module's box, allowing options 'left', 'right', or 'center' to define the positioning.
|
||||||
|
|
||||||
|
*expand*: ++
|
||||||
|
typeof: bool ++
|
||||||
|
default: false ++
|
||||||
|
If set to true, task buttons stretch to fill the available space in the taskbar and long titles are ellipsized to fit. Only takes effect on a horizontal bar; on a vertical bar the buttons keep their content-based size. If set to false, buttons are sized to their content.
|
||||||
|
|
||||||
*on-click*: ++
|
*on-click*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
The action which should be triggered when clicking on the application button with the left mouse button.
|
The action which should be triggered when clicking on the application button with the left mouse button.
|
||||||
|
|||||||
+8
-2
@@ -19,7 +19,7 @@ Valid locations for this file are:
|
|||||||
A good starting point is the default configuration found at https://github.com/Alexays/Waybar/blob/master/resources/config.jsonc
|
A good starting point is the default configuration found at https://github.com/Alexays/Waybar/blob/master/resources/config.jsonc
|
||||||
Also, a minimal example configuration can be found at the bottom of this man page.
|
Also, a minimal example configuration can be found at the bottom of this man page.
|
||||||
|
|
||||||
The visual display elements for waybar use a CSS stylesheet, see *waybar-styles(5)* for details.
|
The visual display elements for waybar use a CSS stylesheet. Waybar supports *style.css* and the appearance-specific *style-light.css* and *style-dark.css* files; see *waybar-styles(5)* for details.
|
||||||
|
|
||||||
# BAR CONFIGURATION
|
# BAR CONFIGURATION
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ The visual display elements for waybar use a CSS stylesheet, see *waybar-styles(
|
|||||||
*expand-right* ++
|
*expand-right* ++
|
||||||
typeof: bool ++
|
typeof: bool ++
|
||||||
default: false ++
|
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* ++
|
*layer* ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
@@ -168,6 +168,7 @@ The visual display elements for waybar use a CSS stylesheet, see *waybar-styles(
|
|||||||
# MODULE FORMAT
|
# MODULE FORMAT
|
||||||
|
|
||||||
You can use PangoMarkupFormat (See https://developer.gnome.org/pango/stable/PangoMarkupFormat.html#PangoMarkupFormat).
|
You can use PangoMarkupFormat (See https://developer.gnome.org/pango/stable/PangoMarkupFormat.html#PangoMarkupFormat).
|
||||||
|
Tooltip appearance is generally controlled globally via CSS and cannot be truly scoped per module. Some modules (such as the clock) provide limited workarounds for customization.
|
||||||
|
|
||||||
e.g.
|
e.g.
|
||||||
|
|
||||||
@@ -363,6 +364,11 @@ A group may hide all but one element, showing them only on mouse hover. In order
|
|||||||
default: false ++
|
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.
|
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*: ++
|
*transition-left-to-right*: ++
|
||||||
typeof: bool ++
|
typeof: bool ++
|
||||||
default: true ++
|
default: true ++
|
||||||
|
|||||||
+17
-6
@@ -1,6 +1,6 @@
|
|||||||
project(
|
project(
|
||||||
'waybar', 'cpp', 'c',
|
'waybar', 'cpp', 'c',
|
||||||
version: '0.14.0',
|
version: '0.15.0',
|
||||||
license: 'MIT',
|
license: 'MIT',
|
||||||
meson_version: '>= 0.59.0',
|
meson_version: '>= 0.59.0',
|
||||||
default_options : [
|
default_options : [
|
||||||
@@ -185,7 +185,9 @@ src_files = files(
|
|||||||
'src/util/gtk_icon.cpp',
|
'src/util/gtk_icon.cpp',
|
||||||
'src/util/icon_loader.cpp',
|
'src/util/icon_loader.cpp',
|
||||||
'src/util/regex_collection.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(
|
man_files = files(
|
||||||
@@ -497,16 +499,24 @@ else
|
|||||||
man_files += files('man/waybar-clock.5.scd')
|
man_files += files('man/waybar-clock.5.scd')
|
||||||
endif
|
endif
|
||||||
|
|
||||||
cava = dependency('cava',
|
cava = dependency('libcava',
|
||||||
version : '>=0.10.4',
|
version : '>=0.10.7',
|
||||||
required: get_option('cava'),
|
required: get_option('cava'),
|
||||||
fallback : ['cava', 'cava_dep'],
|
fallback : ['libcava', 'cava_dep'],
|
||||||
not_found_message: 'cava is not found. Building waybar without cava')
|
not_found_message: 'cava is not found. Building waybar without cava')
|
||||||
|
|
||||||
|
eproxy = dependency('epoxy', required: false)
|
||||||
|
|
||||||
if cava.found()
|
if cava.found()
|
||||||
add_project_arguments('-DHAVE_LIBCAVA', language: 'cpp')
|
add_project_arguments('-DHAVE_LIBCAVA', language: 'cpp')
|
||||||
src_files += files('src/modules/cava/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')
|
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
|
endif
|
||||||
|
|
||||||
if libgps.found()
|
if libgps.found()
|
||||||
@@ -554,6 +564,7 @@ executable(
|
|||||||
tz_dep,
|
tz_dep,
|
||||||
xkbregistry,
|
xkbregistry,
|
||||||
cava,
|
cava,
|
||||||
|
eproxy,
|
||||||
libgps
|
libgps
|
||||||
],
|
],
|
||||||
include_directories: inc_dirs,
|
include_directories: inc_dirs,
|
||||||
|
|||||||
+1
-1
@@ -12,7 +12,7 @@ option('dbusmenu-gtk', type: 'feature', value: 'auto', description: 'Enable supp
|
|||||||
option('man-pages', type: 'feature', value: 'auto', description: 'Generate and install man pages')
|
option('man-pages', type: 'feature', value: 'auto', description: 'Generate and install man pages')
|
||||||
option('mpd', type: 'feature', value: 'auto', description: 'Enable support for the Music Player Daemon')
|
option('mpd', type: 'feature', value: 'auto', description: 'Enable support for the Music Player Daemon')
|
||||||
option('rfkill', type: 'feature', value: 'auto', description: 'Enable support for RFKILL')
|
option('rfkill', type: 'feature', value: 'auto', description: 'Enable support for RFKILL')
|
||||||
option('sndio', type: 'feature', value: 'auto', description: 'Enable support for sndio')
|
option('sndio', type: 'feature', value: 'auto', description: 'Enable support for volume control via sndio')
|
||||||
option('logind', type: 'feature', value: 'auto', description: 'Enable support for logind')
|
option('logind', type: 'feature', value: 'auto', description: 'Enable support for logind')
|
||||||
option('tests', type: 'feature', value: 'auto', description: 'Enable tests')
|
option('tests', type: 'feature', value: 'auto', description: 'Enable tests')
|
||||||
option('experimental', type : 'boolean', value : false, description: 'Enable experimental features')
|
option('experimental', type : 'boolean', value : false, description: 'Enable experimental features')
|
||||||
|
|||||||
+6
-5
@@ -6,16 +6,17 @@
|
|||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
libcava = rec {
|
libcava = rec {
|
||||||
version = "0.10.4";
|
version = "0.10.7";
|
||||||
src = pkgs.fetchFromGitHub {
|
src = pkgs.fetchFromGitHub {
|
||||||
owner = "LukashonakV";
|
owner = "LukashonakV";
|
||||||
repo = "cava";
|
repo = "cava";
|
||||||
tag = version;
|
# NOTE: Needs to match the cava.wrap
|
||||||
hash = "sha256-9eTDqM+O1tA/3bEfd1apm8LbEcR9CVgELTIspSVPMKM=";
|
tag = "${version}";
|
||||||
|
hash = "sha256-zkyj1vBzHtoypX4Bxdh1Vmwh967DKKxN751v79hzmgQ=";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
(waybar.overrideAttrs (oldAttrs: {
|
waybar.overrideAttrs (oldAttrs: {
|
||||||
inherit version;
|
inherit version;
|
||||||
|
|
||||||
src = lib.cleanSourceWith {
|
src = lib.cleanSourceWith {
|
||||||
@@ -43,4 +44,4 @@ in
|
|||||||
patchShebangs .
|
patchShebangs .
|
||||||
popd
|
popd
|
||||||
'';
|
'';
|
||||||
}))
|
})
|
||||||
|
|||||||
@@ -128,7 +128,7 @@
|
|||||||
"critical-threshold": 80,
|
"critical-threshold": 80,
|
||||||
// "format-critical": "{temperatureC}°C {icon}",
|
// "format-critical": "{temperatureC}°C {icon}",
|
||||||
"format": "{temperatureC}°C {icon}",
|
"format": "{temperatureC}°C {icon}",
|
||||||
"format-icons": ["", "", ""]
|
"format-icons": ["", "", ""]
|
||||||
},
|
},
|
||||||
"backlight": {
|
"backlight": {
|
||||||
// "device": "acpi_video1",
|
// "device": "acpi_video1",
|
||||||
@@ -143,7 +143,7 @@
|
|||||||
},
|
},
|
||||||
"format": "{capacity}% {icon}",
|
"format": "{capacity}% {icon}",
|
||||||
"format-full": "{capacity}% {icon}",
|
"format-full": "{capacity}% {icon}",
|
||||||
"format-charging": "{capacity}% ",
|
"format-charging": "{capacity}% ",
|
||||||
"format-plugged": "{capacity}% ",
|
"format-plugged": "{capacity}% ",
|
||||||
"format-alt": "{time} {icon}",
|
"format-alt": "{time} {icon}",
|
||||||
// "format-good": "", // An empty format will hide the module
|
// "format-good": "", // An empty format will hide the module
|
||||||
@@ -167,9 +167,9 @@
|
|||||||
"network": {
|
"network": {
|
||||||
// "interface": "wlp2*", // (Optional) To force the use of this interface
|
// "interface": "wlp2*", // (Optional) To force the use of this interface
|
||||||
"format-wifi": "{essid} ({signalStrength}%) ",
|
"format-wifi": "{essid} ({signalStrength}%) ",
|
||||||
"format-ethernet": "{ipaddr}/{cidr} ",
|
"format-ethernet": "{ipaddr}/{cidr} ",
|
||||||
"tooltip-format": "{ifname} via {gwaddr} ",
|
"tooltip-format": "{ifname} via {gwaddr} ",
|
||||||
"format-linked": "{ifname} (No IP) ",
|
"format-linked": "{ifname} (No IP) ",
|
||||||
"format-disconnected": "Disconnected ⚠",
|
"format-disconnected": "Disconnected ⚠",
|
||||||
"format-alt": "{ifname}: {ipaddr}/{cidr}"
|
"format-alt": "{ifname}: {ipaddr}/{cidr}"
|
||||||
},
|
},
|
||||||
@@ -177,14 +177,14 @@
|
|||||||
// "scroll-step": 1, // %, can be a float
|
// "scroll-step": 1, // %, can be a float
|
||||||
"format": "{volume}% {icon} {format_source}",
|
"format": "{volume}% {icon} {format_source}",
|
||||||
"format-bluetooth": "{volume}% {icon} {format_source}",
|
"format-bluetooth": "{volume}% {icon} {format_source}",
|
||||||
"format-bluetooth-muted": " {icon} {format_source}",
|
"format-bluetooth-muted": " {icon} {format_source}",
|
||||||
"format-muted": " {format_source}",
|
"format-muted": " {format_source}",
|
||||||
"format-source": "{volume}% ",
|
"format-source": "{volume}% ",
|
||||||
"format-source-muted": "",
|
"format-source-muted": "",
|
||||||
"format-icons": {
|
"format-icons": {
|
||||||
"headphone": "",
|
"headphone": "",
|
||||||
"hands-free": "",
|
"hands-free": "",
|
||||||
"headset": "",
|
"headset": "",
|
||||||
"phone": "",
|
"phone": "",
|
||||||
"portable": "",
|
"portable": "",
|
||||||
"car": "",
|
"car": "",
|
||||||
|
|||||||
@@ -112,17 +112,19 @@ class PlayerManager:
|
|||||||
logger.debug(f"Metadata changed for player {player.props.player_name}")
|
logger.debug(f"Metadata changed for player {player.props.player_name}")
|
||||||
player_name = player.props.player_name
|
player_name = player.props.player_name
|
||||||
artist = player.get_artist()
|
artist = player.get_artist()
|
||||||
artist = artist.replace("&", "&")
|
artist = artist and artist.replace("&", "&")
|
||||||
title = player.get_title()
|
title = player.get_title()
|
||||||
title = title.replace("&", "&")
|
title = title and title.replace("&", "&")
|
||||||
|
|
||||||
track_info = ""
|
track_info = ""
|
||||||
if player_name == "spotify" and "mpris:trackid" in metadata.keys() and ":ad:" in player.props.metadata["mpris:trackid"]:
|
if player_name == "spotify" and "mpris:trackid" in metadata.keys() and ":ad:" in player.props.metadata["mpris:trackid"]:
|
||||||
track_info = "Advertisement"
|
track_info = "Advertisement"
|
||||||
elif artist is not None and title is not None:
|
elif artist is not None and title is not None:
|
||||||
track_info = f"{artist} - {title}"
|
track_info = f"{artist} - {title}"
|
||||||
else:
|
elif title is not None:
|
||||||
track_info = title
|
track_info = title
|
||||||
|
elif artist is not None:
|
||||||
|
track_info = artist
|
||||||
|
|
||||||
if track_info:
|
if track_info:
|
||||||
if player.props.status == "Playing":
|
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,
|
std::optional<std::string> getFileBySuffix(const std::string& dir, const std::string& suffix,
|
||||||
bool check_lower_case) {
|
bool check_lower_case) {
|
||||||
if (!std::filesystem::exists(dir)) {
|
try {
|
||||||
return {};
|
|
||||||
}
|
|
||||||
for (const auto& entry : std::filesystem::recursive_directory_iterator(dir)) {
|
for (const auto& entry : std::filesystem::recursive_directory_iterator(dir)) {
|
||||||
if (entry.is_regular_file()) {
|
if (entry.is_regular_file()) {
|
||||||
std::string filename = entry.path().filename().string();
|
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 {};
|
return {};
|
||||||
}
|
}
|
||||||
@@ -151,19 +151,24 @@ void AAppIconLabel::updateAppIconName(const std::string& app_identifier,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void AAppIconLabel::updateAppIcon() {
|
void AAppIconLabel::updateAppIcon() {
|
||||||
if (update_app_icon_) {
|
if (update_app_icon_ || (!iconEnabled() && image_.get_visible())) {
|
||||||
update_app_icon_ = false;
|
update_app_icon_ = false;
|
||||||
if (app_icon_name_.empty()) {
|
if (app_icon_name_.empty()) {
|
||||||
image_.set_visible(false);
|
image_.set_visible(false);
|
||||||
} else if (app_icon_name_.front() == '/') {
|
} 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();
|
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(),
|
auto surface = Gdk::Cairo::create_surface_from_pixbuf(pixbuf, image_.get_scale_factor(),
|
||||||
image_.get_window());
|
image_.get_window());
|
||||||
image_.set(surface);
|
image_.set(surface);
|
||||||
image_.set_visible(true);
|
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 {
|
} else {
|
||||||
image_.set_from_icon_name(app_icon_name_, Gtk::ICON_SIZE_INVALID);
|
image_.set_from_icon_name(app_icon_name_, Gtk::ICON_SIZE_INVALID);
|
||||||
image_.set_visible(true);
|
image_.set_visible(true);
|
||||||
|
|||||||
+37
-5
@@ -26,7 +26,7 @@ ALabel::ALabel(const Json::Value& config, const std::string& name, const std::st
|
|||||||
: std::chrono::milliseconds(
|
: std::chrono::milliseconds(
|
||||||
(config_["interval"].isNumeric()
|
(config_["interval"].isNumeric()
|
||||||
? std::max(1L, // Minimum 1ms due to millisecond precision
|
? std::max(1L, // Minimum 1ms due to millisecond precision
|
||||||
static_cast<long>(config_["interval"].asDouble()) * 1000)
|
static_cast<long>(config_["interval"].asDouble() * 1000))
|
||||||
: 1000 * (long)interval))),
|
: 1000 * (long)interval))),
|
||||||
default_format_(format_) {
|
default_format_(format_) {
|
||||||
label_.set_name(name);
|
label_.set_name(name);
|
||||||
@@ -91,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
|
// Make the GtkBuilder and check for errors in his parsing
|
||||||
if (gtk_builder_add_from_string(builder, fileContent.str().c_str(), -1, nullptr) == 0U) {
|
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);
|
throw std::runtime_error("Error found in the file " + menuFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
menu_ = gtk_builder_get_object(builder, "menu");
|
menu_ = gtk_builder_get_object(builder, "menu");
|
||||||
if (menu_ == nullptr) {
|
if (menu_ == nullptr) {
|
||||||
|
g_object_unref(builder);
|
||||||
throw std::runtime_error("Failed to get 'menu' object from GtkBuilder");
|
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*>();
|
submenus_ = std::map<std::string, GtkMenuItem*>();
|
||||||
menuActionsMap_ = std::map<std::string, std::string>();
|
menuActionsMap_ = std::map<std::string, std::string>();
|
||||||
|
|
||||||
@@ -105,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();
|
for (Json::Value::const_iterator it = config_["menu-actions"].begin();
|
||||||
it != config_["menu-actions"].end(); ++it) {
|
it != config_["menu-actions"].end(); ++it) {
|
||||||
std::string key = it.key().asString();
|
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();
|
menuActionsMap_[key] = it->asString();
|
||||||
g_signal_connect(submenus_[key], "activate", G_CALLBACK(handleGtkMenuEvent),
|
g_signal_connect(submenus_[key], "activate", G_CALLBACK(handleGtkMenuEvent),
|
||||||
(gpointer)menuActionsMap_[key].c_str());
|
(gpointer)menuActionsMap_[key].c_str());
|
||||||
}
|
}
|
||||||
|
g_object_unref(builder);
|
||||||
} catch (std::runtime_error& e) {
|
} catch (std::runtime_error& e) {
|
||||||
spdlog::warn("Error while creating the menu : {}. Menu popup not activated.", e.what());
|
spdlog::warn("Error while creating the menu : {}. Menu popup not activated.", e.what());
|
||||||
}
|
}
|
||||||
@@ -129,6 +139,26 @@ ALabel::ALabel(const Json::Value& config, const std::string& name, const std::st
|
|||||||
|
|
||||||
auto ALabel::update() -> void { AModule::update(); }
|
auto ALabel::update() -> void { AModule::update(); }
|
||||||
|
|
||||||
|
bool ALabel::setLabelMarkup(const Glib::ustring& markup) {
|
||||||
|
if (last_label_markup_ == markup) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
label_.set_markup(markup);
|
||||||
|
last_label_markup_ = markup;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ALabel::setTooltipMarkup(const Glib::ustring& markup) {
|
||||||
|
if (last_tooltip_markup_ == markup) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
label_.set_tooltip_markup(markup);
|
||||||
|
last_tooltip_markup_ = markup;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
std::string ALabel::getIcon(uint16_t percentage, const std::string& alt, uint16_t max) {
|
std::string ALabel::getIcon(uint16_t percentage, const std::string& alt, uint16_t max) {
|
||||||
auto format_icons = config_["format-icons"];
|
auto format_icons = config_["format-icons"];
|
||||||
if (format_icons.isObject()) {
|
if (format_icons.isObject()) {
|
||||||
@@ -141,7 +171,8 @@ std::string ALabel::getIcon(uint16_t percentage, const std::string& alt, uint16_
|
|||||||
if (format_icons.isArray()) {
|
if (format_icons.isArray()) {
|
||||||
auto size = format_icons.size();
|
auto size = format_icons.size();
|
||||||
if (size != 0U) {
|
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];
|
format_icons = format_icons[idx];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -167,7 +198,8 @@ std::string ALabel::getIcon(uint16_t percentage, const std::vector<std::string>&
|
|||||||
if (format_icons.isArray()) {
|
if (format_icons.isArray()) {
|
||||||
auto size = format_icons.size();
|
auto size = format_icons.size();
|
||||||
if (size != 0U) {
|
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];
|
format_icons = format_icons[idx];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -190,7 +222,7 @@ bool waybar::ALabel::handleToggle(GdkEventButton* const& e) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ALabel::handleGtkMenuEvent(GtkMenuItem* /*menuitem*/, gpointer data) {
|
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) {
|
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},
|
isTooltip{config_["tooltip"].isBool() ? config_["tooltip"].asBool() : true},
|
||||||
isExpand{config_["expand"].isBool() ? config_["expand"].asBool() : false},
|
isExpand{config_["expand"].isBool() ? config_["expand"].asBool() : false},
|
||||||
distance_scrolled_y_(0.0),
|
distance_scrolled_y_(0.0),
|
||||||
distance_scrolled_x_(0.0) {
|
distance_scrolled_x_(0.0),
|
||||||
|
cursor_timeout_conn_() {
|
||||||
// Configure module action Map
|
// Configure module action Map
|
||||||
const Json::Value actions{config_["actions"]};
|
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) {
|
for (Json::Value::const_iterator it = actions.begin(); it != actions.end(); ++it) {
|
||||||
if (it.key().isString() && it->isString())
|
if (it.key().isString() && it->isString())
|
||||||
if (!eventActionMap_.contains(it.key().asString())) {
|
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
|
// Respect user configuration of cursor
|
||||||
if (config_.isMember("cursor")) {
|
if (config_.isMember("cursor")) {
|
||||||
if (config_["cursor"].isBool() && config_["cursor"].asBool()) {
|
if (config_["cursor"].isBool()) {
|
||||||
|
if (config_["cursor"].asBool()) {
|
||||||
setCursor(Gdk::HAND2);
|
setCursor(Gdk::HAND2);
|
||||||
|
} else {
|
||||||
|
setCursor(Gdk::ARROW);
|
||||||
|
}
|
||||||
} else if (config_["cursor"].isInt()) {
|
} else if (config_["cursor"].isInt()) {
|
||||||
setCursor(Gdk::CursorType(config_["cursor"].asInt()));
|
setCursor(Gdk::CursorType(config_["cursor"].asInt()));
|
||||||
} else {
|
} else {
|
||||||
@@ -83,11 +91,18 @@ AModule::AModule(const Json::Value& config, const std::string& name, const std::
|
|||||||
}
|
}
|
||||||
|
|
||||||
AModule::~AModule() {
|
AModule::~AModule() {
|
||||||
|
if (cursor_timeout_conn_.connected()) {
|
||||||
|
cursor_timeout_conn_.disconnect();
|
||||||
|
}
|
||||||
for (const auto& pid : pid_children_) {
|
for (const auto& pid : pid_children_) {
|
||||||
if (pid != -1) {
|
if (pid != -1) {
|
||||||
killpg(pid, SIGTERM);
|
killpg(pid, SIGTERM);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (menu_ != nullptr) {
|
||||||
|
g_object_unref(menu_);
|
||||||
|
menu_ = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto AModule::update() -> void {
|
auto AModule::update() -> void {
|
||||||
@@ -114,7 +129,7 @@ void AModule::setCursor(Gdk::CursorType const& c) {
|
|||||||
} else {
|
} else {
|
||||||
// window may not be accessible yet, in this case,
|
// window may not be accessible yet, in this case,
|
||||||
// schedule another call for setting the cursor in 1 sec
|
// 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]() {
|
[this, c]() {
|
||||||
setCursor(c);
|
setCursor(c);
|
||||||
return false;
|
return false;
|
||||||
@@ -166,9 +181,9 @@ bool AModule::handleUserEvent(GdkEventButton* const& e) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check that a menu has been configured
|
// 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
|
// 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
|
// Popup the menu
|
||||||
gtk_widget_show_all(GTK_WIDGET(menu_));
|
gtk_widget_show_all(GTK_WIDGET(menu_));
|
||||||
gtk_menu_popup_at_pointer(GTK_MENU(menu_), reinterpret_cast<GdkEvent*>(e));
|
gtk_menu_popup_at_pointer(GTK_MENU(menu_), reinterpret_cast<GdkEvent*>(e));
|
||||||
|
|||||||
+72
-5
@@ -172,6 +172,10 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config)
|
|||||||
right_.set_spacing(spacing);
|
right_.set_spacing(spacing);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (config.isMember("height") && !config["height"].isUInt()) {
|
||||||
|
spdlog::warn("Invalid type for 'height', expected unsigned integer");
|
||||||
|
}
|
||||||
|
|
||||||
height_ = config["height"].isUInt() ? config["height"].asUInt() : 0;
|
height_ = config["height"].isUInt() ? config["height"].asUInt() : 0;
|
||||||
width_ = config["width"].isUInt() ? config["width"].asUInt() : 0;
|
width_ = config["width"].isUInt() ? config["width"].asUInt() : 0;
|
||||||
|
|
||||||
@@ -229,7 +233,8 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config)
|
|||||||
gtk_layer_init_for_window(gtk_window);
|
gtk_layer_init_for_window(gtk_window);
|
||||||
gtk_layer_set_keyboard_mode(gtk_window, GTK_LAYER_SHELL_KEYBOARD_MODE_NONE);
|
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_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_LEFT, margins_.left);
|
||||||
gtk_layer_set_margin(gtk_window, GTK_LAYER_SHELL_EDGE_RIGHT, margins_.right);
|
gtk_layer_set_margin(gtk_window, GTK_LAYER_SHELL_EDGE_RIGHT, margins_.right);
|
||||||
@@ -262,6 +267,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_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 HAVE_SWAY
|
||||||
if (auto ipc = config["ipc"]; ipc.isBool() && ipc.asBool()) {
|
if (auto ipc = config["ipc"]; ipc.isBool() && ipc.asBool()) {
|
||||||
bar_id = Client::inst()->bar_id;
|
bar_id = Client::inst()->bar_id;
|
||||||
@@ -308,6 +323,19 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config)
|
|||||||
setupWidgets();
|
setupWidgets();
|
||||||
window.show_all();
|
window.show_all();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If gtk-layer-shell's synchronous wait for the initial configure timed out, show_all() can
|
||||||
|
* return with a configured but not-yet-presented surface. Kick GTK/layer-shell once control has
|
||||||
|
* returned to the main loop, when any late initial configure has been dispatched and widgets have
|
||||||
|
* had a chance to allocate/draw.
|
||||||
|
*/
|
||||||
|
Glib::signal_idle().connect(sigc::track_obj([this] {
|
||||||
|
window.queue_resize();
|
||||||
|
window.queue_draw();
|
||||||
|
forceLayerCommit();
|
||||||
|
return false;
|
||||||
|
}, *this));
|
||||||
|
|
||||||
if (spdlog::should_log(spdlog::level::debug)) {
|
if (spdlog::should_log(spdlog::level::debug)) {
|
||||||
// Unfortunately, this function isn't in the C++ bindings, so we have to call the C version.
|
// Unfortunately, this function isn't in the C++ bindings, so we have to call the C version.
|
||||||
char* gtk_tree = gtk_style_context_to_string(
|
char* gtk_tree = gtk_style_context_to_string(
|
||||||
@@ -373,7 +401,14 @@ void waybar::Bar::setMode(const struct bar_mode& mode) {
|
|||||||
* gtk-layer-shell schedules a commit on the next frame event in GTK, but this could fail in
|
* gtk-layer-shell schedules a commit on the next frame event in GTK, but this could fail in
|
||||||
* certain scenarios, such as fully occluded bar.
|
* certain scenarios, such as fully occluded bar.
|
||||||
*/
|
*/
|
||||||
|
forceLayerCommit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void waybar::Bar::forceLayerCommit() {
|
||||||
|
auto* gtk_window = window.gobj();
|
||||||
|
if (gtk_window != nullptr && gtk_widget_get_realized(GTK_WIDGET(gtk_window))) {
|
||||||
gtk_layer_try_force_commit(gtk_window);
|
gtk_layer_try_force_commit(gtk_window);
|
||||||
|
}
|
||||||
wl_display_flush(Client::inst()->wl_display);
|
wl_display_flush(Client::inst()->wl_display);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -540,13 +575,15 @@ void waybar::Bar::getModules(const Factory& factory, const std::string& pos,
|
|||||||
auto vertical = (group != nullptr ? group->getBox().get_orientation()
|
auto vertical = (group != nullptr ? group->getBox().get_orientation()
|
||||||
: box_.get_orientation()) == Gtk::ORIENTATION_VERTICAL;
|
: box_.get_orientation()) == Gtk::ORIENTATION_VERTICAL;
|
||||||
|
|
||||||
auto group_config = config[ref];
|
const Json::Value& group_config = config[ref];
|
||||||
if (group_config["modules"].isNull()) {
|
if (group_config["modules"].isNull()) {
|
||||||
spdlog::warn("Group definition '{}' has not been found, group will be hidden", ref);
|
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);
|
auto group_module =
|
||||||
getModules(factory, ref, group_module);
|
std::make_unique<waybar::Group>(id_name, class_name, group_config, vertical);
|
||||||
module = group_module;
|
|
||||||
|
getModules(factory, ref, group_module.get());
|
||||||
|
module = group_module.release();
|
||||||
} else {
|
} else {
|
||||||
module = factory.makeModule(ref, pos);
|
module = factory.makeModule(ref, pos);
|
||||||
}
|
}
|
||||||
@@ -649,6 +686,17 @@ void waybar::Bar::onConfigure(GdkEventConfigure* ev) {
|
|||||||
|
|
||||||
configureGlobalOffset(ev->width, ev->height);
|
configureGlobalOffset(ev->width, ev->height);
|
||||||
spdlog::info(BAR_SIZE_MSG, ev->width, ev->height, output->name);
|
spdlog::info(BAR_SIZE_MSG, ev->width, ev->height, output->name);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* gtk-layer-shell waits for the compositor's initial configure while realizing the window. On a
|
||||||
|
* busy compositor (common during session startup) that wait can time out even though the initial
|
||||||
|
* configure arrives shortly afterwards. In that case GTK may not schedule the frame commit that
|
||||||
|
* presents the first layer-surface buffer, leaving an otherwise configured bar invisible. Force a
|
||||||
|
* commit after every configure so late initial configures, and later compositor-driven resizes,
|
||||||
|
* always result in a submitted surface state.
|
||||||
|
*/
|
||||||
|
window.queue_draw();
|
||||||
|
forceLayerCommit();
|
||||||
}
|
}
|
||||||
|
|
||||||
void waybar::Bar::configureGlobalOffset(int width, int height) {
|
void waybar::Bar::configureGlobalOffset(int width, int height) {
|
||||||
@@ -693,3 +741,22 @@ void waybar::Bar::configureGlobalOffset(int width, int height) {
|
|||||||
void waybar::Bar::onOutputGeometryChanged() {
|
void waybar::Bar::onOutputGeometryChanged() {
|
||||||
configureGlobalOffset(window.get_width(), window.get_height());
|
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 "idle-inhibit-unstable-v1-client-protocol.h"
|
||||||
#include "util/clara.hpp"
|
#include "util/clara.hpp"
|
||||||
#include "util/format.hpp"
|
#include "util/format.hpp"
|
||||||
|
#include "util/hex_checker.hpp"
|
||||||
|
|
||||||
waybar::Client* waybar::Client::inst() {
|
waybar::Client* waybar::Client::inst() {
|
||||||
static auto* c = new Client();
|
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,
|
void waybar::Client::handleGlobal(void* data, struct wl_registry* registry, uint32_t name,
|
||||||
const char* interface, uint32_t version) {
|
const char* interface, uint32_t version) {
|
||||||
auto* client = static_cast<Client*>(data);
|
auto* client = static_cast<Client*>(data);
|
||||||
|
|
||||||
if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0 &&
|
if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0 &&
|
||||||
version >= ZXDG_OUTPUT_V1_NAME_SINCE_VERSION) {
|
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(
|
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));
|
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) {
|
} 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*>(
|
client->idle_inhibit_manager = static_cast<struct zwp_idle_inhibit_manager_v1*>(
|
||||||
wl_registry_bind(registry, name, &zwp_idle_inhibit_manager_v1_interface, 1));
|
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();
|
output.xdg_output.reset();
|
||||||
spdlog::debug("Output detection done: {} ({})", output.name, output.identifier);
|
spdlog::debug("Output detection done: {} ({})", output.name, output.identifier);
|
||||||
|
|
||||||
auto configs = client->getOutputConfigs(output);
|
client->pending_outputs_.push_back(&output);
|
||||||
if (!configs.empty()) {
|
|
||||||
for (const auto &config : configs) {
|
if (!client->bars_scheduled_) {
|
||||||
client->bars.emplace_back(std::make_unique<Bar>(&output, config));
|
client->bars_scheduled_ = true;
|
||||||
}
|
|
||||||
|
Glib::signal_idle().connect_once([client]() {
|
||||||
|
client->createBarsBatch();
|
||||||
|
}, Glib::PRIORITY_HIGH_IDLE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (const std::exception& e) {
|
} 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*/,
|
void waybar::Client::handleOutputName(void* data, struct zxdg_output_v1* /*xdg_output*/,
|
||||||
const char* name) {
|
const char* name) {
|
||||||
auto* client = waybar::Client::inst();
|
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 {
|
auto waybar::Client::setupCss(const std::string& css_file) -> void {
|
||||||
css_provider_ = Gtk::CssProvider::create();
|
auto screen = Gdk::Screen::get_default();
|
||||||
style_context_ = Gtk::StyleContext::create();
|
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)) {
|
if (!css_provider_->load_from_path(css_file)) {
|
||||||
|
css_provider_.reset();
|
||||||
throw std::runtime_error("Can't open style file");
|
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);
|
GTK_STYLE_PROVIDER_PRIORITY_USER);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,13 +258,22 @@ void waybar::Client::bindInterfaces() {
|
|||||||
if (xdg_output_manager == nullptr) {
|
if (xdg_output_manager == nullptr) {
|
||||||
throw std::runtime_error("Failed to acquire required resources.");
|
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
|
// add existing outputs and subscribe to updates
|
||||||
for (auto i = 0; i < gdk_display->get_n_monitors(); ++i) {
|
for (auto i = 0; i < gdk_display->get_n_monitors(); ++i) {
|
||||||
auto monitor = gdk_display->get_monitor(i);
|
auto monitor = gdk_display->get_monitor(i);
|
||||||
handleMonitorAdded(monitor);
|
handleMonitorAdded(monitor);
|
||||||
}
|
}
|
||||||
gdk_display->signal_monitor_added().connect(sigc::mem_fun(*this, &Client::handleMonitorAdded));
|
monitor_added_connection_ = gdk_display->signal_monitor_added().connect(
|
||||||
gdk_display->signal_monitor_removed().connect(
|
sigc::mem_fun(*this, &Client::handleMonitorAdded));
|
||||||
|
monitor_removed_connection_ = gdk_display->signal_monitor_removed().connect(
|
||||||
sigc::mem_fun(*this, &Client::handleMonitorRemoved));
|
sigc::mem_fun(*this, &Client::handleMonitorRemoved));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -272,9 +328,10 @@ int waybar::Client::main(int argc, char *argv[]) {
|
|||||||
}
|
}
|
||||||
m_cssFile = getStyle(style_opt);
|
m_cssFile = getStyle(style_opt);
|
||||||
setupCss(m_cssFile);
|
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) {
|
portal->signal_appearance_changed().connect([&](waybar::Appearance appearance) {
|
||||||
auto css_file = getStyle(style_opt, appearance);
|
auto css_file = getStyle(style_opt, appearance);
|
||||||
|
m_cssReloadHelper->changeCssFile(css_file);
|
||||||
setupCss(css_file);
|
setupCss(css_file);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
+2
-6
@@ -108,15 +108,13 @@
|
|||||||
#ifdef HAVE_LIBWIREPLUMBER
|
#ifdef HAVE_LIBWIREPLUMBER
|
||||||
#include "modules/wireplumber.hpp"
|
#include "modules/wireplumber.hpp"
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_LIBCAVA
|
|
||||||
#include "modules/cava/cava.hpp"
|
|
||||||
#endif
|
|
||||||
#ifdef HAVE_SYSTEMD_MONITOR
|
#ifdef HAVE_SYSTEMD_MONITOR
|
||||||
#include "modules/systemd_failed_units.hpp"
|
#include "modules/systemd_failed_units.hpp"
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_LIBGPS
|
#ifdef HAVE_LIBGPS
|
||||||
#include "modules/gps.hpp"
|
#include "modules/gps.hpp"
|
||||||
#endif
|
#endif
|
||||||
|
#include "modules/cava/cava_frontend.hpp"
|
||||||
#include "modules/cffi.hpp"
|
#include "modules/cffi.hpp"
|
||||||
#include "modules/custom.hpp"
|
#include "modules/custom.hpp"
|
||||||
#include "modules/image.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]);
|
return new waybar::modules::Wireplumber(id, config_[name]);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_LIBCAVA
|
|
||||||
if (ref == "cava") {
|
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
|
#ifdef HAVE_SYSTEMD_MONITOR
|
||||||
if (ref == "systemd-failed-units") {
|
if (ref == "systemd-failed-units") {
|
||||||
return new waybar::modules::SystemdFailedUnits(id, config_[name]);
|
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,
|
Group::Group(const std::string& name, const std::string& id, const Json::Value& config,
|
||||||
bool vertical)
|
bool vertical)
|
||||||
: AModule(config, name, id, true, true),
|
: AModule(config, name, id, true, false),
|
||||||
box{vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0},
|
box{vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0},
|
||||||
revealer_box{vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0} {
|
revealer_box{vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0} {
|
||||||
box.set_name(name_);
|
box.set_name(name_);
|
||||||
@@ -64,11 +64,19 @@ Group::Group(const std::string& name, const std::string& id, const Json::Value&
|
|||||||
: true);
|
: true);
|
||||||
click_to_reveal = drawer_config["click-to-reveal"].asBool();
|
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);
|
auto transition_type = getPreferredTransitionType(vertical);
|
||||||
|
|
||||||
revealer.set_transition_type(transition_type);
|
revealer.set_transition_type(transition_type);
|
||||||
revealer.set_transition_duration(transition_duration);
|
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");
|
revealer.get_style_context()->add_class("drawer");
|
||||||
|
|
||||||
@@ -124,6 +132,11 @@ auto Group::update() -> void {
|
|||||||
// noop
|
// noop
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Group::handleScroll(GdkEventScroll* e) {
|
||||||
|
// no scroll.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
Gtk::Box& Group::getBox() { return is_drawer ? (is_first_widget ? box : revealer_box) : box; }
|
Gtk::Box& Group::getBox() { return is_drawer ? (is_first_widget ? box : revealer_box) : box; }
|
||||||
|
|
||||||
void Group::addWidget(Gtk::Widget& widget) {
|
void Group::addWidget(Gtk::Widget& widget) {
|
||||||
|
|||||||
+13
-5
@@ -33,7 +33,10 @@ static void writeSignalToPipe(int signum) {
|
|||||||
// to `signal_handler`.
|
// to `signal_handler`.
|
||||||
static void catchSignals(waybar::SafeSignal<int>& signal_handler) {
|
static void catchSignals(waybar::SafeSignal<int>& signal_handler) {
|
||||||
int fd[2];
|
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];
|
int signal_pipe_read_fd = fd[0];
|
||||||
signal_pipe_write_fd = fd[1];
|
signal_pipe_write_fd = fd[1];
|
||||||
@@ -52,9 +55,11 @@ static void catchSignals(waybar::SafeSignal<int>& signal_handler) {
|
|||||||
std::signal(SIGINT, writeSignalToPipe);
|
std::signal(SIGINT, writeSignalToPipe);
|
||||||
std::signal(SIGCHLD, writeSignalToPipe);
|
std::signal(SIGCHLD, writeSignalToPipe);
|
||||||
|
|
||||||
|
#ifdef SIGRTMIN
|
||||||
for (int sig = SIGRTMIN + 1; sig <= SIGRTMAX; ++sig) {
|
for (int sig = SIGRTMIN + 1; sig <= SIGRTMAX; ++sig) {
|
||||||
std::signal(sig, writeSignalToPipe);
|
std::signal(sig, writeSignalToPipe);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
int signum;
|
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
|
// If this signal should restart or close the bar, this function will write
|
||||||
// `true` or `false`, respectively, into `reload`.
|
// `true` or `false`, respectively, into `reload`.
|
||||||
static void handleSignalMainThread(int signum, bool& reload) {
|
static void handleSignalMainThread(int signum, bool& reload) {
|
||||||
|
#ifdef SIGRTMIN
|
||||||
if (signum >= SIGRTMIN + 1 && signum <= SIGRTMAX) {
|
if (signum >= SIGRTMIN + 1 && signum <= SIGRTMAX) {
|
||||||
for (auto& bar : waybar::Client::inst()->bars) {
|
for (auto& bar : waybar::Client::inst()->bars) {
|
||||||
bar->handleSignal(signum);
|
bar->handleSignal(signum);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
switch (signum) {
|
switch (signum) {
|
||||||
case SIGUSR1:
|
case SIGUSR1:
|
||||||
@@ -137,15 +144,16 @@ static void handleSignalMainThread(int signum, bool& reload) {
|
|||||||
break;
|
break;
|
||||||
case SIGCHLD:
|
case SIGCHLD:
|
||||||
spdlog::debug("Received SIGCHLD in signalThread");
|
spdlog::debug("Received SIGCHLD in signalThread");
|
||||||
if (!reap.empty()) {
|
{
|
||||||
reap_mtx.lock();
|
std::lock_guard<std::mutex> lock(reap_mtx);
|
||||||
for (auto it = reap.begin(); it != reap.end(); ++it) {
|
for (auto it = reap.begin(); it != reap.end();) {
|
||||||
if (waitpid(*it, nullptr, WNOHANG) == *it) {
|
if (waitpid(*it, nullptr, WNOHANG) == *it) {
|
||||||
spdlog::debug("Reaped child with PID: {}", *it);
|
spdlog::debug("Reaped child with PID: {}", *it);
|
||||||
it = reap.erase(it);
|
it = reap.erase(it);
|
||||||
|
} else {
|
||||||
|
++it;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
reap_mtx.unlock();
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -58,11 +58,11 @@ auto waybar::modules::Backlight::update() -> void {
|
|||||||
tooltip_format = config_["tooltip-format"].asString();
|
tooltip_format = config_["tooltip-format"].asString();
|
||||||
}
|
}
|
||||||
if (!tooltip_format.empty()) {
|
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("percent", percent),
|
||||||
fmt::arg("icon", getIcon(percent))));
|
fmt::arg("icon", getIcon(percent))));
|
||||||
} else {
|
} else {
|
||||||
label_.set_tooltip_text(desc);
|
label_.set_tooltip_markup(desc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+109
-75
@@ -7,7 +7,10 @@
|
|||||||
#if defined(__FreeBSD__)
|
#if defined(__FreeBSD__)
|
||||||
#include <sys/sysctl.h>
|
#include <sys/sysctl.h>
|
||||||
#endif
|
#endif
|
||||||
|
#include <libudev.h>
|
||||||
|
#include <poll.h>
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
|
#include <sys/signalfd.h>
|
||||||
|
|
||||||
waybar::modules::Battery::Battery(const std::string& id, const Bar& bar, const Json::Value& config)
|
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) {
|
: 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) {
|
if (battery_watch_fd_ == -1) {
|
||||||
throw std::runtime_error("Unable to listen batteries.");
|
throw std::runtime_error("Unable to listen batteries.");
|
||||||
}
|
}
|
||||||
|
udev_ = std::unique_ptr<udev, util::UdevDeleter>(udev_new());
|
||||||
global_watch_fd_ = inotify_init1(IN_CLOEXEC);
|
if (udev_ == nullptr) {
|
||||||
if (global_watch_fd_ == -1) {
|
throw std::runtime_error("udev_new failed");
|
||||||
throw std::runtime_error("Unable to listen batteries.");
|
|
||||||
}
|
}
|
||||||
|
mon_ = std::unique_ptr<udev_monitor, util::UdevMonitorDeleter>(
|
||||||
// Watch the directory for any added or removed batteries
|
udev_monitor_new_from_netlink(udev_.get(), "kernel"));
|
||||||
global_watch = inotify_add_watch(global_watch_fd_, data_dir_.c_str(), IN_CREATE | IN_DELETE);
|
if (mon_ == nullptr) {
|
||||||
if (global_watch < 0) {
|
throw std::runtime_error("udev monitor new failed");
|
||||||
throw std::runtime_error("Could not watch for battery plug/unplug");
|
|
||||||
}
|
}
|
||||||
|
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
|
#endif
|
||||||
spdlog::debug("battery: worker interval is {}", interval_.count());
|
spdlog::debug("battery: worker interval is {}", interval_.count());
|
||||||
worker();
|
worker();
|
||||||
@@ -36,11 +43,6 @@ waybar::modules::Battery::~Battery() {
|
|||||||
#if defined(__linux__)
|
#if defined(__linux__)
|
||||||
std::lock_guard<std::mutex> guard(battery_list_mutex_);
|
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) {
|
for (auto it = batteries_.cbegin(), next_it = it; it != batteries_.cend(); it = next_it) {
|
||||||
++next_it;
|
++next_it;
|
||||||
auto watch_id = (*it).second;
|
auto watch_id = (*it).second;
|
||||||
@@ -77,12 +79,18 @@ void waybar::modules::Battery::worker() {
|
|||||||
dp.emit();
|
dp.emit();
|
||||||
};
|
};
|
||||||
thread_battery_update_ = [this] {
|
thread_battery_update_ = [this] {
|
||||||
struct inotify_event event = {0};
|
poll_fds_[0].revents = 0;
|
||||||
int nbytes = read(global_watch_fd_, &event, sizeof(event));
|
poll_fds_[0].events = POLLIN;
|
||||||
if (nbytes != sizeof(event) || event.mask & IN_IGNORED) {
|
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();
|
thread_.stop();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if ((poll_fds_[0].revents & POLLIN) != 0) {
|
||||||
|
signalfd_siginfo signal_info;
|
||||||
|
read(poll_fds_[0].fd, &signal_info, sizeof(signal_info));
|
||||||
|
}
|
||||||
refreshBatteries();
|
refreshBatteries();
|
||||||
dp.emit();
|
dp.emit();
|
||||||
};
|
};
|
||||||
@@ -109,17 +117,16 @@ void waybar::modules::Battery::refreshBatteries() {
|
|||||||
if (((bat_defined && dir_name == config_["bat"].asString()) || !bat_defined) &&
|
if (((bat_defined && dir_name == config_["bat"].asString()) || !bat_defined) &&
|
||||||
(fs::exists(node.path() / "capacity") || fs::exists(node.path() / "charge_now")) &&
|
(fs::exists(node.path() / "capacity") || fs::exists(node.path() / "charge_now")) &&
|
||||||
fs::exists(node.path() / "uevent") &&
|
fs::exists(node.path() / "uevent") &&
|
||||||
(fs::exists(node.path() / "status") || bat_compatibility) &&
|
(fs::exists(node.path() / "status") || bat_compatibility)) {
|
||||||
fs::exists(node.path() / "type")) {
|
|
||||||
std::string type;
|
std::string type;
|
||||||
std::ifstream(node.path() / "type") >> type;
|
if (std::ifstream{node.path() / "type"} >> type && !type.compare("Battery")) {
|
||||||
|
|
||||||
if (!type.compare("Battery")) {
|
|
||||||
// Ignore non-system power supplies unless explicitly requested
|
// Ignore non-system power supplies unless explicitly requested
|
||||||
if (!bat_defined && fs::exists(node.path() / "scope")) {
|
if (!bat_defined) {
|
||||||
std::string scope;
|
std::string scope;
|
||||||
std::ifstream(node.path() / "scope") >> scope;
|
// for hotplug-in device, access it is always unstable because you may remove the
|
||||||
if (g_ascii_strcasecmp(scope.data(), "device") == 0) {
|
// 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;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -131,7 +138,9 @@ void waybar::modules::Battery::refreshBatteries() {
|
|||||||
auto event_path = (node.path() / "uevent");
|
auto event_path = (node.path() / "uevent");
|
||||||
auto wd = inotify_add_watch(battery_watch_fd_, event_path.c_str(), IN_ACCESS);
|
auto wd = inotify_add_watch(battery_watch_fd_, event_path.c_str(), IN_ACCESS);
|
||||||
if (wd < 0) {
|
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;
|
batteries_[node.path()] = wd;
|
||||||
}
|
}
|
||||||
@@ -144,7 +153,7 @@ void waybar::modules::Battery::refreshBatteries() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (fs::filesystem_error& e) {
|
} catch (fs::filesystem_error& e) {
|
||||||
throw std::runtime_error(e.what());
|
spdlog::warn("Battery directory tracking failed: {}", e.what());
|
||||||
}
|
}
|
||||||
if (warnFirstTime_ && batteries_.empty()) {
|
if (warnFirstTime_ && batteries_.empty()) {
|
||||||
if (config_["bat"].isString()) {
|
if (config_["bat"].isString()) {
|
||||||
@@ -265,11 +274,11 @@ waybar::modules::Battery::getInfos() {
|
|||||||
auto bat = item.first;
|
auto bat = item.first;
|
||||||
std::string _status;
|
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);
|
std::getline(std::ifstream(adapter_ / "status"), _status);
|
||||||
} else {
|
}
|
||||||
std::getline(std::ifstream(bat / "status"), _status);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Some battery will report current and charge in μA/μAh.
|
// Some battery will report current and charge in μA/μAh.
|
||||||
@@ -278,64 +287,74 @@ waybar::modules::Battery::getInfos() {
|
|||||||
uint32_t current_now = 0;
|
uint32_t current_now = 0;
|
||||||
int32_t _current_now_int = 0;
|
int32_t _current_now_int = 0;
|
||||||
bool current_now_exists = false;
|
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;
|
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;
|
current_now_exists = true;
|
||||||
std::ifstream(bat / "current_avg") >> _current_now_int;
|
}
|
||||||
}
|
}
|
||||||
// Documentation ABI allows a negative value when discharging, positive
|
// Documentation ABI allows a negative value when discharging, positive
|
||||||
// value when charging.
|
// value when charging.
|
||||||
current_now = std::abs(_current_now_int);
|
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;
|
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;
|
time_to_full_now_exists = true;
|
||||||
std::ifstream(bat / "time_to_full_now") >> time_to_full_now;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t voltage_now = 0;
|
uint32_t voltage_now = 0;
|
||||||
bool voltage_now_exists = false;
|
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;
|
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;
|
voltage_now_exists = true;
|
||||||
std::ifstream(bat / "voltage_avg") >> voltage_now;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t charge_full = 0;
|
uint32_t charge_full = 0;
|
||||||
bool charge_full_exists = false;
|
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;
|
charge_full_exists = true;
|
||||||
std::ifstream(bat / "charge_full") >> charge_full;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t charge_full_design = 0;
|
uint32_t charge_full_design = 0;
|
||||||
bool charge_full_design_exists = false;
|
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;
|
charge_full_design_exists = true;
|
||||||
std::ifstream(bat / "charge_full_design") >> charge_full_design;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t charge_now = 0;
|
uint32_t charge_now = 0;
|
||||||
bool charge_now_exists = false;
|
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;
|
charge_now_exists = true;
|
||||||
std::ifstream(bat / "charge_now") >> charge_now;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t power_now = 0;
|
uint32_t power_now = 0;
|
||||||
int32_t _power_now_int = 0;
|
int32_t _power_now_int = 0;
|
||||||
bool power_now_exists = false;
|
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;
|
power_now_exists = true;
|
||||||
std::ifstream(bat / "power_now") >> _power_now_int;
|
}
|
||||||
}
|
}
|
||||||
// Some drivers (example: Qualcomm) exposes use a negative value when
|
// Some drivers (example: Qualcomm) exposes use a negative value when
|
||||||
// discharging, positive value when charging.
|
// discharging, positive value when charging.
|
||||||
@@ -343,28 +362,31 @@ waybar::modules::Battery::getInfos() {
|
|||||||
|
|
||||||
uint32_t energy_now = 0;
|
uint32_t energy_now = 0;
|
||||||
bool energy_now_exists = false;
|
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;
|
energy_now_exists = true;
|
||||||
std::ifstream(bat / "energy_now") >> energy_now;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t energy_full = 0;
|
uint32_t energy_full = 0;
|
||||||
bool energy_full_exists = false;
|
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;
|
energy_full_exists = true;
|
||||||
std::ifstream(bat / "energy_full") >> energy_full;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t energy_full_design = 0;
|
uint32_t energy_full_design = 0;
|
||||||
bool energy_full_design_exists = false;
|
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;
|
energy_full_design_exists = true;
|
||||||
std::ifstream(bat / "energy_full_design") >> energy_full_design;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t cycleCount = 0;
|
uint16_t cycleCount = 0;
|
||||||
if (fs::exists(bat / "cycle_count")) {
|
if (std::ifstream f{bat / "cycle_count"}) {
|
||||||
std::ifstream(bat / "cycle_count") >> cycleCount;
|
f >> cycleCount;
|
||||||
}
|
}
|
||||||
if (charge_full_design >= largestDesignCapacity) {
|
if (charge_full_design >= largestDesignCapacity) {
|
||||||
largestDesignCapacity = charge_full_design;
|
largestDesignCapacity = charge_full_design;
|
||||||
@@ -394,9 +416,10 @@ waybar::modules::Battery::getInfos() {
|
|||||||
} else if (energy_now_exists && energy_full_exists && energy_full != 0) {
|
} else if (energy_now_exists && energy_full_exists && energy_full != 0) {
|
||||||
capacity_exists = true;
|
capacity_exists = true;
|
||||||
capacity = 100 * (uint64_t)energy_now / (uint64_t)energy_full;
|
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;
|
capacity_exists = true;
|
||||||
std::ifstream(bat / "capacity") >> capacity;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!voltage_now_exists) {
|
if (!voltage_now_exists) {
|
||||||
@@ -576,17 +599,17 @@ waybar::modules::Battery::getInfos() {
|
|||||||
|
|
||||||
float calculated_capacity{0.0f};
|
float calculated_capacity{0.0f};
|
||||||
if (total_capacity_exists) {
|
if (total_capacity_exists) {
|
||||||
if (total_capacity > 0.0f)
|
if (total_capacity > 0.0f) {
|
||||||
calculated_capacity = (float)total_capacity / batteries_.size();
|
calculated_capacity = (float)total_capacity / batteries_.size();
|
||||||
else if (total_energy_full_exists && total_energy_exists) {
|
} else if (total_energy_full_exists && total_energy_exists) {
|
||||||
if (total_energy_full > 0.0f)
|
if (total_energy_full > 0.0f) {
|
||||||
calculated_capacity = ((float)total_energy * 100.0f / (float)total_energy_full);
|
calculated_capacity = ((float)total_energy * 100.0f / (float)total_energy_full);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Handle weighted-average
|
// Handle weighted-average
|
||||||
if ((config_["weighted-average"].isBool() ? config_["weighted-average"].asBool() : false) &&
|
if (weightedAverage_ && total_energy_exists && total_energy_full_exists) {
|
||||||
total_energy_exists && total_energy_full_exists) {
|
|
||||||
if (total_energy_full > 0.0f)
|
if (total_energy_full > 0.0f)
|
||||||
calculated_capacity = ((float)total_energy * 100.0f / (float)total_energy_full);
|
calculated_capacity = ((float)total_energy * 100.0f / (float)total_energy_full);
|
||||||
}
|
}
|
||||||
@@ -678,12 +701,20 @@ auto waybar::modules::Battery::update() -> void {
|
|||||||
if (status == "Unknown") {
|
if (status == "Unknown") {
|
||||||
status = getAdapterStatus(capacity);
|
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;
|
auto status_pretty = status;
|
||||||
|
|
||||||
// Transform to lowercase and replace space with dash
|
// Transform to lowercase and replace space with dash
|
||||||
std::ranges::transform(status.begin(), status.end(), status.begin(),
|
std::ranges::transform(status.begin(), status.end(), status.begin(),
|
||||||
[](char ch) { return ch == ' ' ? '-' : std::tolower(ch); });
|
[](char ch) { return ch == ' ' ? '-' : std::tolower(ch); });
|
||||||
auto format = format_;
|
auto format = format_;
|
||||||
auto state = getState(capacity, true);
|
|
||||||
processEvents(state, status, capacity);
|
processEvents(state, status, capacity);
|
||||||
setBarClass(state);
|
setBarClass(state);
|
||||||
auto time_remaining_formatted = formatTimeRemaining(time_remaining);
|
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()) {
|
if (!events.isObject() || events.empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
std::string event_name = fmt::format("on-{}-{}", status == "discharging" ? status : "charging",
|
auto exec = [](Json::Value const& event) {
|
||||||
state.empty() ? std::to_string(capacity) : state);
|
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) {
|
if (last_event_ != event_name) {
|
||||||
spdlog::debug("battery: triggering event {}", event_name);
|
spdlog::debug("battery: triggering event {}", event_name);
|
||||||
if (events[event_name].isString()) {
|
exec(events[event_name]);
|
||||||
std::string exec = events[event_name].asString();
|
if (!last_event_.empty() && last_event_[3] != event_name[3]) {
|
||||||
// Execute the command if it is not empty
|
exec(events[status_name]);
|
||||||
if (!exec.empty()) {
|
|
||||||
util::command::exec(exec, "");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
last_event_ = event_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_);
|
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-added", G_CALLBACK(onObjectAdded), this);
|
||||||
g_signal_connect(manager_.get(), "object-removed", G_CALLBACK(onObjectRemoved), this);
|
g_signal_connect(manager_.get(), "object-removed", G_CALLBACK(onObjectRemoved), this);
|
||||||
g_signal_connect(manager_.get(), "interface-proxy-properties-changed",
|
g_signal_connect(manager_.get(), "interface-proxy-properties-changed",
|
||||||
G_CALLBACK(onInterfaceProxyPropertiesChanged), this);
|
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),
|
g_signal_connect(manager_.get(), "interface-removed", G_CALLBACK(onInterfaceAddedOrRemoved),
|
||||||
this);
|
this);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef WANT_RFKILL
|
#ifdef WANT_RFKILL
|
||||||
rfkill_.on_update.connect(sigc::hide(sigc::mem_fun(*this, &Bluetooth::update)));
|
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);
|
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::runtime(tooltip_format), fmt::arg("status", state_),
|
||||||
fmt::arg("num_connections", connected_devices_.size()),
|
fmt::arg("num_connections", connected_devices_.size()),
|
||||||
fmt::arg("controller_address", cur_controller_ ? cur_controller_->address : "null"),
|
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> {
|
auto waybar::modules::Bluetooth::findCurController() -> std::optional<ControllerInfo> {
|
||||||
std::optional<ControllerInfo> controller_info;
|
std::optional<ControllerInfo> controller_info;
|
||||||
|
|
||||||
|
if (!manager_) {
|
||||||
|
return controller_info;
|
||||||
|
}
|
||||||
|
|
||||||
GList* objects = g_dbus_object_manager_get_objects(manager_.get());
|
GList* objects = g_dbus_object_manager_get_objects(manager_.get());
|
||||||
for (GList* l = objects; l != NULL; l = l->next) {
|
for (GList* l = objects; l != NULL; l = l->next) {
|
||||||
GDBusObject* object = G_DBUS_OBJECT(l->data);
|
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,
|
auto waybar::modules::Bluetooth::findConnectedDevices(const std::string& cur_controller_path,
|
||||||
std::vector<DeviceInfo>& connected_devices)
|
std::vector<DeviceInfo>& connected_devices)
|
||||||
-> void {
|
-> void {
|
||||||
|
if (!manager_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
GList* objects = g_dbus_object_manager_get_objects(manager_.get());
|
GList* objects = g_dbus_object_manager_get_objects(manager_.get());
|
||||||
for (GList* l = objects; l != NULL; l = l->next) {
|
for (GList* l = objects; l != NULL; l = l->next) {
|
||||||
GDBusObject* object = G_DBUS_OBJECT(l->data);
|
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;
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user