Merge branch 'master' of https://github.com/Alexays/Waybar
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
|
||||
#include <glibmm/markup.h>
|
||||
#include <gtkmm/label.h>
|
||||
#include <json/json.h>
|
||||
|
||||
#include <deque>
|
||||
#include <vector>
|
||||
|
||||
#include "AModule.hpp"
|
||||
|
||||
namespace waybar {
|
||||
|
||||
enum class GraphType { LINE, BAR, GAUGE };
|
||||
|
||||
class AGraph : public AModule {
|
||||
public:
|
||||
AGraph(const Json::Value&, const std::string&, const std::string&, uint16_t interval = 0,
|
||||
bool enable_click = false, bool enable_scroll = false);
|
||||
virtual ~AGraph() = default;
|
||||
auto update() -> void override;
|
||||
|
||||
protected:
|
||||
Gtk::DrawingArea graph_;
|
||||
std::deque<int> values_;
|
||||
uint16_t datapoints_ = 20;
|
||||
GraphType graph_type_ = GraphType::LINE;
|
||||
|
||||
void addValue(const int n);
|
||||
|
||||
const std::chrono::milliseconds interval_;
|
||||
|
||||
bool onDraw(const Cairo::RefPtr<Cairo::Context>& cr);
|
||||
|
||||
std::map<std::string, GtkMenuItem*> submenus_;
|
||||
std::map<std::string, std::string> menuActionsMap_;
|
||||
static void handleGtkMenuEvent(GtkMenuItem* menuitem, gpointer data);
|
||||
|
||||
private:
|
||||
void drawFilledArea(const Cairo::RefPtr<Cairo::Context>& cr,
|
||||
const std::vector<std::pair<double, double>>& points, double height,
|
||||
const Gdk::RGBA& bg_color);
|
||||
|
||||
void drawLine(const Cairo::RefPtr<Cairo::Context>& cr,
|
||||
const std::vector<std::pair<double, double>>& points, const Gdk::RGBA& fg_color);
|
||||
|
||||
void drawPath(const Cairo::RefPtr<Cairo::Context>& cr,
|
||||
const std::vector<std::pair<double, double>>& points);
|
||||
|
||||
void drawBars(const Cairo::RefPtr<Cairo::Context>& cr, double width, double height,
|
||||
int current_value, const Gdk::RGBA& fg_color);
|
||||
|
||||
void drawGauge(const Cairo::RefPtr<Cairo::Context>& cr, double width, double height,
|
||||
int current_value, const Gdk::RGBA& fg_color);
|
||||
};
|
||||
|
||||
} // namespace waybar
|
||||
@@ -14,10 +14,14 @@ class AIconLabel : public ALabel {
|
||||
bool enable_click = false, bool enable_scroll = false);
|
||||
virtual ~AIconLabel() = default;
|
||||
auto update() -> void override;
|
||||
static std::tuple<std::string, std::string> extractIcon(const std::string& input);
|
||||
|
||||
protected:
|
||||
Gtk::Image image_;
|
||||
Gtk::Box box_;
|
||||
unsigned app_icon_size_{24};
|
||||
|
||||
bool label_contains_icon{false};
|
||||
|
||||
bool iconEnabled() const;
|
||||
};
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <fmt/args.h>
|
||||
#include <fmt/format.h>
|
||||
#include <glibmm/markup.h>
|
||||
#include <gtkmm/label.h>
|
||||
#include <gtkmm/tooltip.h>
|
||||
#include <json/json.h>
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "AModule.hpp"
|
||||
|
||||
namespace waybar {
|
||||
@@ -25,12 +32,68 @@ class ALabel : public AModule {
|
||||
bool alt_ = false;
|
||||
std::string default_format_;
|
||||
|
||||
bool setLabelMarkup(const Glib::ustring& markup);
|
||||
bool setTooltipMarkup(const Glib::ustring& markup);
|
||||
|
||||
// resolveTooltipFormat() / resolveFormat() are inherited from AModule.
|
||||
|
||||
// Combined label + tooltip helper. Builds a single fmt argument store from
|
||||
// `args`, renders `labelFormat` into the label and the resolved tooltip format
|
||||
// into the tooltip, both through the dedup-aware setters. Honors the `tooltip`
|
||||
// toggle. This replaces the label/tooltip formatting boilerplate that modules
|
||||
// used to duplicate. `state` selects `tooltip-format-<state>` when non-empty.
|
||||
template <typename... Args>
|
||||
void updateLabelAndTooltipForState(const std::string& state, const std::string& labelFormat,
|
||||
const std::string& tooltipDefault, Args&&... args) {
|
||||
fmt::dynamic_format_arg_store<fmt::format_context> store;
|
||||
(store.push_back(std::forward<Args>(args)), ...);
|
||||
setLabelMarkup(fmt::vformat(labelFormat, store));
|
||||
if (tooltipEnabled()) {
|
||||
setTooltipMarkup(fmt::vformat(resolveTooltipFormat(tooltipDefault, state), store));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void updateLabelAndTooltip(const std::string& labelFormat, const std::string& tooltipDefault,
|
||||
Args&&... args) {
|
||||
updateLabelAndTooltipForState("", labelFormat, tooltipDefault, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
// Overloads accepting a pre-built argument store, for modules that must
|
||||
// assemble a dynamic set of format arguments (e.g. per-core CPU stats) that
|
||||
// cannot be expressed through a fixed variadic call.
|
||||
// A non-const reference is used so this overload is preferred over the
|
||||
// variadic template above (which would otherwise bind the store as a single
|
||||
// forwarded argument).
|
||||
void updateLabelAndTooltipForState(const std::string& state, const std::string& labelFormat,
|
||||
const std::string& tooltipDefault,
|
||||
fmt::dynamic_format_arg_store<fmt::format_context>& store) {
|
||||
setLabelMarkup(fmt::vformat(labelFormat, store));
|
||||
if (tooltipEnabled()) {
|
||||
setTooltipMarkup(fmt::vformat(resolveTooltipFormat(tooltipDefault, state), store));
|
||||
}
|
||||
}
|
||||
|
||||
void updateLabelAndTooltip(const std::string& labelFormat, const std::string& tooltipDefault,
|
||||
fmt::dynamic_format_arg_store<fmt::format_context>& store) {
|
||||
updateLabelAndTooltipForState("", labelFormat, tooltipDefault, store);
|
||||
}
|
||||
|
||||
bool handleToggle(GdkEventButton* const& e) override;
|
||||
void copyToClipboard(const std::string&);
|
||||
virtual std::string getState(uint8_t value, bool lesser = false);
|
||||
|
||||
std::map<std::string, GtkMenuItem*> submenus_;
|
||||
std::map<std::string, std::string> menuActionsMap_;
|
||||
static void handleGtkMenuEvent(GtkMenuItem* menuitem, gpointer data);
|
||||
|
||||
private:
|
||||
// Raw UTF-8 bytes, not Glib::ustring: ustring::operator== collates with
|
||||
// g_utf8_collate(), which gives private-use codepoints (nerd-font icons)
|
||||
// no collation weight, so two different icons compare equal.
|
||||
std::optional<std::string> last_label_markup_;
|
||||
std::optional<std::string> last_tooltip_markup_;
|
||||
Glib::RefPtr<Gtk::Tooltip> active_tooltip_;
|
||||
};
|
||||
|
||||
} // namespace waybar
|
||||
|
||||
+58
-2
@@ -1,11 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <fmt/args.h>
|
||||
#include <fmt/format.h>
|
||||
#include <glibmm/dispatcher.h>
|
||||
#include <glibmm/markup.h>
|
||||
#include <gtkmm.h>
|
||||
#include <gtkmm/eventbox.h>
|
||||
#include <json/json.h>
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "IModule.hpp"
|
||||
|
||||
namespace waybar {
|
||||
@@ -15,6 +20,7 @@ class AModule : public IModule {
|
||||
static constexpr const char* MODULE_CLASS = "module";
|
||||
|
||||
~AModule() override;
|
||||
sigc::signal<void, AModule*> signal_updated;
|
||||
auto update() -> void override;
|
||||
virtual auto refresh(int shouldRefresh) -> void {};
|
||||
operator Gtk::Widget&() override;
|
||||
@@ -25,6 +31,10 @@ class AModule : public IModule {
|
||||
|
||||
bool expandEnabled() const;
|
||||
|
||||
virtual void suspend() {};
|
||||
virtual void resume() {};
|
||||
bool shouldSuspend() const { return disable_on_sleep_; }
|
||||
|
||||
protected:
|
||||
// Don't need to make an object directly
|
||||
// Derived classes are able to use it
|
||||
@@ -36,11 +46,47 @@ class AModule : public IModule {
|
||||
SCROLL_DIR getScrollDir(GdkEventScroll* e);
|
||||
bool tooltipEnabled() const;
|
||||
|
||||
// --- Generic format/tooltip resolution (config-only, usable by any module,
|
||||
// ALabel-derived or not). Prefers `<key>-<state>`, then `<key>`, then default.
|
||||
std::string resolveFormat(const std::string& defaultFormat, const std::string& state = "") const {
|
||||
if (!state.empty() && config_["format-" + state].isString()) {
|
||||
return config_["format-" + state].asString();
|
||||
}
|
||||
if (config_["format"].isString()) {
|
||||
return config_["format"].asString();
|
||||
}
|
||||
return defaultFormat;
|
||||
}
|
||||
std::string resolveTooltipFormat(const std::string& defaultFormat,
|
||||
const std::string& state = "") const {
|
||||
if (!state.empty() && config_["tooltip-format-" + state].isString()) {
|
||||
return config_["tooltip-format-" + state].asString();
|
||||
}
|
||||
if (config_["tooltip-format"].isString()) {
|
||||
return config_["tooltip-format"].asString();
|
||||
}
|
||||
return defaultFormat;
|
||||
}
|
||||
|
||||
// Generic tooltip for any widget: honors the `tooltip` toggle and
|
||||
// `tooltip-format`, formats with the given args and applies it. Lets modules
|
||||
// that are not ALabel-derived (e.g. gamemode) reuse the shared logic.
|
||||
template <typename... Args>
|
||||
void updateTooltip(Gtk::Widget& widget, const std::string& defaultFormat, Args&&... args) {
|
||||
if (!tooltipEnabled()) {
|
||||
return;
|
||||
}
|
||||
widget.set_tooltip_markup(
|
||||
fmt::format(fmt::runtime(resolveTooltipFormat(defaultFormat)), std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
std::vector<int> pid_children_;
|
||||
const std::string name_;
|
||||
const Json::Value& config_;
|
||||
Gtk::EventBox event_box_;
|
||||
|
||||
virtual void setCursor(std::string const& c);
|
||||
// Backward-compat overload for legacy numeric Gdk::CursorType configs (pre-0.16)
|
||||
virtual void setCursor(Gdk::CursorType const& c);
|
||||
|
||||
virtual bool handleToggle(GdkEventButton* const& ev);
|
||||
@@ -48,8 +94,17 @@ class AModule : public IModule {
|
||||
virtual bool handleMouseLeave(GdkEventCrossing* const& ev);
|
||||
virtual bool handleScroll(GdkEventScroll*);
|
||||
virtual bool handleRelease(GdkEventButton* const& ev);
|
||||
|
||||
bool disable_on_sleep_{false};
|
||||
GObject* menu_ = nullptr;
|
||||
|
||||
// Maps a configured event name (e.g. "on-click-middle") to a built-in module
|
||||
// action name. Populated from the `actions` config section, and by modules
|
||||
// that interpret on-click* config values as internal actions (e.g.
|
||||
// wlr/taskbar). Entries here are dispatched through doAction() instead of
|
||||
// being run as shell commands.
|
||||
std::map<std::string, std::string> eventActionMap_;
|
||||
|
||||
private:
|
||||
bool handleUserEvent(GdkEventButton* const& ev);
|
||||
const bool isTooltip;
|
||||
@@ -57,7 +112,7 @@ class AModule : public IModule {
|
||||
bool hasUserEvents_;
|
||||
gdouble distance_scrolled_y_;
|
||||
gdouble distance_scrolled_x_;
|
||||
std::map<std::string, std::string> eventActionMap_;
|
||||
sigc::connection cursor_timeout_conn_;
|
||||
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_RELEASE), "on-click-release"},
|
||||
@@ -78,7 +133,8 @@ class AModule : public IModule {
|
||||
{std::make_pair(9, GdkEventType::GDK_BUTTON_PRESS), "on-click-forward"},
|
||||
{std::make_pair(9, GdkEventType::GDK_BUTTON_RELEASE), "on-click-forward-release"},
|
||||
{std::make_pair(9, GdkEventType::GDK_2BUTTON_PRESS), "on-double-click-forward"},
|
||||
{std::make_pair(9, GdkEventType::GDK_3BUTTON_PRESS), "on-triple-click-forward"}};
|
||||
{std::make_pair(9, GdkEventType::GDK_3BUTTON_PRESS), "on-triple-click-forward"},
|
||||
{std::make_pair(10, GdkEventType::GDK_BUTTON_PRESS), "on-click-copy"}};
|
||||
};
|
||||
|
||||
} // namespace waybar
|
||||
|
||||
@@ -24,6 +24,8 @@ struct waybar_output {
|
||||
Glib::RefPtr<Gdk::Monitor> monitor;
|
||||
std::string name;
|
||||
std::string identifier;
|
||||
int32_t width;
|
||||
int32_t height;
|
||||
|
||||
std::unique_ptr<struct zxdg_output_v1, decltype(&zxdg_output_v1_destroy)> xdg_output = {
|
||||
nullptr, &zxdg_output_v1_destroy};
|
||||
@@ -75,6 +77,8 @@ class Bar : public sigc::trackable {
|
||||
util::KillSignalAction getOnSigusr1Action();
|
||||
util::KillSignalAction getOnSigusr2Action();
|
||||
|
||||
void toggleSuspend(bool suspend);
|
||||
|
||||
struct waybar_output* output;
|
||||
Json::Value config;
|
||||
struct wl_surface* surface;
|
||||
@@ -99,6 +103,7 @@ class Bar : public sigc::trackable {
|
||||
void setMode(const bar_mode&);
|
||||
void setPassThrough(bool passthrough);
|
||||
void setPosition(Gtk::PositionType position);
|
||||
void forceLayerCommit();
|
||||
void onConfigure(GdkEventConfigure* ev);
|
||||
void configureGlobalOffset(int width, int height);
|
||||
void onOutputGeometryChanged();
|
||||
@@ -126,6 +131,10 @@ class Bar : public sigc::trackable {
|
||||
|
||||
waybar::util::KillSignalAction onSigusr1 = util::SIGNALACTION_DEFAULT_SIGUSR1;
|
||||
waybar::util::KillSignalAction onSigusr2 = util::SIGNALACTION_DEFAULT_SIGUSR2;
|
||||
|
||||
/* Disconnected in ~Bar before the modules are destroyed (#5182). */
|
||||
sigc::connection map_conn_;
|
||||
sigc::connection unmap_conn_;
|
||||
};
|
||||
|
||||
} // namespace waybar
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
struct zwp_idle_inhibitor_v1;
|
||||
struct zwp_idle_inhibit_manager_v1;
|
||||
struct ext_idle_notifier_v1;
|
||||
|
||||
namespace waybar {
|
||||
|
||||
@@ -32,6 +33,7 @@ class Client {
|
||||
struct wl_registry* registry = nullptr;
|
||||
struct zxdg_output_manager_v1* xdg_output_manager = nullptr;
|
||||
struct zwp_idle_inhibit_manager_v1* idle_inhibit_manager = nullptr;
|
||||
struct ext_idle_notifier_v1* idle_notifier = nullptr;
|
||||
std::vector<std::unique_ptr<Bar>> bars;
|
||||
Config config;
|
||||
std::string bar_id;
|
||||
@@ -48,7 +50,9 @@ class Client {
|
||||
static void handleGlobal(void* data, struct wl_registry* registry, uint32_t name,
|
||||
const char* interface, uint32_t version);
|
||||
static void handleGlobalRemove(void* data, struct wl_registry* registry, uint32_t name);
|
||||
static void handleOutputLogicalSize(void*, struct zxdg_output_v1*, int32_t, int32_t);
|
||||
static void handleOutputDone(void*, struct zxdg_output_v1*);
|
||||
void createBarsBatch();
|
||||
static void handleOutputName(void*, struct zxdg_output_v1*, const char*);
|
||||
static void handleOutputDescription(void*, struct zxdg_output_v1*, const char*);
|
||||
void handleMonitorAdded(Glib::RefPtr<Gdk::Monitor> monitor);
|
||||
@@ -65,6 +69,8 @@ class Client {
|
||||
std::map<int, bool> signal_toggle_state;
|
||||
sigc::connection monitor_added_connection_;
|
||||
sigc::connection monitor_removed_connection_;
|
||||
std::list<waybar_output*> pending_outputs_;
|
||||
bool bars_scheduled_ = false;
|
||||
};
|
||||
|
||||
} // namespace waybar
|
||||
|
||||
+2
-1
@@ -29,7 +29,8 @@ class Config {
|
||||
|
||||
Json::Value& getConfig() { return config_; }
|
||||
|
||||
std::vector<Json::Value> getOutputConfigs(const std::string& name, const std::string& identifier);
|
||||
std::vector<Json::Value> getOutputConfigs(const std::string& name, const std::string& identifier,
|
||||
int32_t width, int32_t height);
|
||||
|
||||
private:
|
||||
void setupConfig(Json::Value& dst, const std::string& config_file, int depth);
|
||||
|
||||
+11
-2
@@ -12,15 +12,17 @@
|
||||
namespace waybar {
|
||||
|
||||
class Group : public AModule {
|
||||
sigc::connection reveal_timeout_;
|
||||
|
||||
public:
|
||||
Group(const std::string&, const std::string&, const Json::Value&, bool);
|
||||
~Group() override = default;
|
||||
~Group() override;
|
||||
auto update() -> void override;
|
||||
operator Gtk::Widget&() override;
|
||||
auto refresh(int sig) -> void override;
|
||||
|
||||
virtual Gtk::Box& getBox();
|
||||
void addWidget(Gtk::Widget& widget);
|
||||
void addWidget(AModule* module);
|
||||
|
||||
protected:
|
||||
Gtk::Box box;
|
||||
@@ -30,6 +32,9 @@ class Group : public AModule {
|
||||
bool is_drawer = false;
|
||||
bool click_to_reveal = false;
|
||||
std::optional<int> toggle_signal;
|
||||
std::string always_visible_class;
|
||||
bool empty_if_drawer_empty = false;
|
||||
int reveal_delay = 0;
|
||||
std::string add_class_to_drawer_children;
|
||||
bool handleMouseEnter(GdkEventCrossing* const& ev) override;
|
||||
bool handleMouseLeave(GdkEventCrossing* const& ev) override;
|
||||
@@ -45,6 +50,10 @@ class Group : public AModule {
|
||||
hide_group();
|
||||
}
|
||||
}
|
||||
void manage_visibility(AModule* module);
|
||||
void show_widget(Gtk::Widget& widget);
|
||||
void hide_widget(Gtk::Widget& widget);
|
||||
void hide_current_widget_if_inactive();
|
||||
};
|
||||
|
||||
} // namespace waybar
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <poll.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -51,6 +52,11 @@ class Battery : public ALabel {
|
||||
bool warnFirstTime_{true};
|
||||
bool weightedAverage_{true};
|
||||
const Bar& bar_;
|
||||
bool smoothPowerEnable_{false};
|
||||
double time_constant_s_{260.0};
|
||||
double smooth_power_{0.0}; // µW
|
||||
std::chrono::steady_clock::time_point last_t_{std::chrono::steady_clock::now()};
|
||||
std::string old_status_raw_{""};
|
||||
|
||||
util::SleeperThread thread_;
|
||||
util::SleeperThread thread_battery_update_;
|
||||
|
||||
@@ -41,6 +41,7 @@ class Bluetooth : public ALabel {
|
||||
bool services_resolved;
|
||||
// NOTE: experimental feature in bluez
|
||||
std::optional<unsigned char> battery_percentage;
|
||||
std::optional<unsigned char> battery_percentage_peripheral;
|
||||
};
|
||||
|
||||
public:
|
||||
@@ -59,6 +60,12 @@ class Bluetooth : public ALabel {
|
||||
gpointer) -> void;
|
||||
|
||||
auto getDeviceBatteryPercentage(GDBusObject*) -> std::optional<unsigned char>;
|
||||
auto getDeviceGattBatteryLevels(GDBusObject*, std::optional<unsigned char>&,
|
||||
std::optional<unsigned char>&) -> void;
|
||||
static auto processBatteryServiceCharacteristics(GList*, const std::string&, const std::string&,
|
||||
const std::string&,
|
||||
std::optional<unsigned char>&,
|
||||
std::optional<unsigned char>&) -> void;
|
||||
auto getDeviceProperties(GDBusObject*, DeviceInfo&) -> bool;
|
||||
auto getControllerProperties(GDBusObject*, ControllerInfo&) -> bool;
|
||||
|
||||
|
||||
@@ -1,36 +1,64 @@
|
||||
#pragma once
|
||||
|
||||
#include <epoxy/gl.h>
|
||||
#include <gtkmm/glarea.h>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <array>
|
||||
|
||||
#include <sigc++/connection.h>
|
||||
|
||||
#include "AModule.hpp"
|
||||
#include "cava_backend.hpp"
|
||||
|
||||
namespace waybar::modules::cava {
|
||||
|
||||
class CavaGLSL final : public AModule, public Gtk::GLArea {
|
||||
class CavaGLSL final : public AModule {
|
||||
public:
|
||||
CavaGLSL(const std::string&, const Json::Value&);
|
||||
~CavaGLSL() = default;
|
||||
~CavaGLSL();
|
||||
auto doAction(const std::string& name) -> void override;
|
||||
|
||||
private:
|
||||
using Action = void (CavaGLSL::*)();
|
||||
|
||||
Gtk::GLArea gl_area_;
|
||||
std::shared_ptr<CavaBackend> backend_;
|
||||
struct ::cava::config_params prm_;
|
||||
int frame_counter{0};
|
||||
// Cached config params (deep-copied strings to avoid dangling char* on backend reload)
|
||||
int sdl_width_{0};
|
||||
int sdl_height_{0};
|
||||
int bar_width_{0};
|
||||
int bar_spacing_{0};
|
||||
int gradient_count_{0};
|
||||
std::string vertex_shader_;
|
||||
std::string fragment_shader_;
|
||||
std::string bcolor_;
|
||||
std::string color_;
|
||||
std::array<std::string, 8> gradient_colors_;
|
||||
int frame_counter_{0};
|
||||
bool silence_{false};
|
||||
bool hide_on_silence_{false};
|
||||
bool mapped_{false};
|
||||
// Cava method
|
||||
auto onUpdate(const ::cava::audio_raw& input) -> void;
|
||||
void pauseResume();
|
||||
auto onUpdate(const CavaBackend::AudioRaw& input) -> void;
|
||||
auto onSilence() -> void;
|
||||
// Member variable to store the shared pointer
|
||||
std::shared_ptr<::cava::audio_raw> m_data_;
|
||||
GLuint shaderProgram_;
|
||||
auto onBackendConfigChanged() -> void;
|
||||
void cacheConfigParams(const ::cava::config_params& src);
|
||||
// Member variable to store audio data
|
||||
CavaBackend::AudioRaw m_data_;
|
||||
GLuint shaderProgram_{0};
|
||||
// OpenGL variables
|
||||
GLuint fbo_;
|
||||
GLuint texture_;
|
||||
GLuint fbo_{0};
|
||||
GLuint texture_{0};
|
||||
GLuint vbo_{0};
|
||||
GLuint ibo_{0};
|
||||
GLuint vao_{0};
|
||||
GLint uniform_bars_;
|
||||
GLint uniform_previous_bars_;
|
||||
GLint uniform_bars_count_;
|
||||
GLint uniform_time_;
|
||||
GLint uniform_input_texture_;
|
||||
// Methods
|
||||
void onRealize();
|
||||
bool onRender(const Glib::RefPtr<Gdk::GLContext>& context);
|
||||
@@ -39,5 +67,13 @@ class CavaGLSL final : public AModule, public Gtk::GLArea {
|
||||
void initSurface();
|
||||
void initGLSL();
|
||||
GLuint loadShader(const std::string& fileName, GLenum type);
|
||||
void cleanupGL();
|
||||
|
||||
// ModuleActionMap
|
||||
static const std::map<std::string, Action> actionMap_;
|
||||
|
||||
sigc::connection audio_raw_update_conn_;
|
||||
sigc::connection silence_conn_;
|
||||
sigc::connection config_changed_conn_;
|
||||
};
|
||||
} // namespace waybar::modules::cava
|
||||
|
||||
@@ -1,30 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include <sigc++/connection.h>
|
||||
|
||||
#include "ALabel.hpp"
|
||||
#include "cava_backend.hpp"
|
||||
|
||||
namespace waybar::modules::cava {
|
||||
|
||||
class Cava final : public ALabel, public sigc::trackable {
|
||||
class CavaRaw final : public ALabel {
|
||||
public:
|
||||
Cava(const std::string&, const Json::Value&);
|
||||
~Cava() = default;
|
||||
CavaRaw(const std::string&, const Json::Value&);
|
||||
~CavaRaw();
|
||||
auto doAction(const std::string& name) -> void override;
|
||||
|
||||
private:
|
||||
using Action = void (CavaRaw::*)();
|
||||
|
||||
std::shared_ptr<CavaBackend> backend_;
|
||||
// Text to display
|
||||
Glib::ustring label_text_{""};
|
||||
Glib::ustring label_text_;
|
||||
bool silence_{false};
|
||||
bool hide_on_silence_{false};
|
||||
std::string format_silent_{""};
|
||||
int ascii_range_{0};
|
||||
std::string format_silent_;
|
||||
// Cava method
|
||||
void pause_resume();
|
||||
void pauseResume();
|
||||
auto onUpdate(const std::string& input) -> void;
|
||||
auto onSilence() -> void;
|
||||
// ModuleActionMap
|
||||
static inline std::map<const std::string, void (waybar::modules::cava::Cava::* const)()>
|
||||
actionMap_{{"mode", &waybar::modules::cava::Cava::pause_resume}};
|
||||
static const std::map<std::string, Action> actionMap_;
|
||||
|
||||
sigc::connection update_conn_;
|
||||
sigc::connection silence_conn_;
|
||||
};
|
||||
} // namespace waybar::modules::cava
|
||||
|
||||
@@ -1,8 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <json/json.h>
|
||||
#include <sigc++/sigc++.h>
|
||||
|
||||
#include "util/SafeSignal.hpp"
|
||||
#include "util/sleeper_thread.hpp"
|
||||
|
||||
namespace cava {
|
||||
@@ -21,7 +30,6 @@ extern "C" {
|
||||
} // namespace cava
|
||||
|
||||
namespace waybar::modules::cava {
|
||||
using namespace std::literals::chrono_literals;
|
||||
|
||||
class CavaBackend final {
|
||||
public:
|
||||
@@ -29,19 +37,38 @@ class CavaBackend final {
|
||||
|
||||
virtual ~CavaBackend();
|
||||
// Methods
|
||||
int getAsciiRange();
|
||||
int getAsciiRange() const;
|
||||
void doPauseResume();
|
||||
void Update();
|
||||
const struct ::cava::config_params* getPrm();
|
||||
std::chrono::milliseconds getFrameTimeMilsec();
|
||||
void update();
|
||||
const ::cava::config_params& getPrm() const;
|
||||
std::chrono::milliseconds getFrameTimeMilsec() const;
|
||||
|
||||
struct AudioRaw {
|
||||
std::vector<float> bars_raw;
|
||||
std::vector<float> previous_bars_raw;
|
||||
int number_of_bars = 0;
|
||||
|
||||
AudioRaw() = default;
|
||||
explicit AudioRaw(const ::cava::audio_raw& raw) {
|
||||
number_of_bars = raw.number_of_bars;
|
||||
if (raw.bars_raw != nullptr && number_of_bars > 0) {
|
||||
bars_raw.assign(raw.bars_raw, raw.bars_raw + number_of_bars);
|
||||
}
|
||||
if (raw.previous_bars_raw != nullptr && number_of_bars > 0) {
|
||||
previous_bars_raw.assign(raw.previous_bars_raw, raw.previous_bars_raw + number_of_bars);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Signal accessor
|
||||
using type_signal_update = sigc::signal<void(const std::string&)>;
|
||||
type_signal_update signal_update();
|
||||
using type_signal_audio_raw_update = sigc::signal<void(const ::cava::audio_raw&)>;
|
||||
type_signal_audio_raw_update signal_audio_raw_update();
|
||||
using type_signal_silence = sigc::signal<void()>;
|
||||
type_signal_silence signal_silence();
|
||||
using SignalUpdate = SafeSignal<const std::string&>;
|
||||
SignalUpdate& signalUpdate();
|
||||
using SignalAudioRawUpdate = SafeSignal<AudioRaw>;
|
||||
SignalAudioRawUpdate& signalAudioRawUpdate();
|
||||
using SignalSilence = SafeSignal<>;
|
||||
SignalSilence& signalSilence();
|
||||
using SignalConfigChanged = SafeSignal<>;
|
||||
SignalConfigChanged& signalConfigChanged();
|
||||
|
||||
private:
|
||||
CavaBackend(const Json::Value& config);
|
||||
@@ -49,36 +76,78 @@ class CavaBackend final {
|
||||
util::SleeperThread out_thread_;
|
||||
|
||||
// Cava API to read audio source
|
||||
::cava::ptr input_source_{NULL};
|
||||
::cava::ptr input_source_{nullptr};
|
||||
|
||||
struct ::cava::error_s error_{}; // cava errors
|
||||
struct ::cava::config_params prm_{}; // cava parameters
|
||||
struct ::cava::audio_raw audio_raw_{}; // cava handled raw audio data(is based on audio_data)
|
||||
struct ::cava::audio_data audio_data_{}; // cava audio data
|
||||
struct ::cava::cava_plan* plan_{NULL}; //{new cava_plan{}};
|
||||
struct ::cava::cava_plan* plan_{nullptr}; //{new cava_plan{}};
|
||||
|
||||
std::chrono::seconds fetch_input_delay_{4};
|
||||
// Delay to handle audio source
|
||||
std::chrono::milliseconds frame_time_milsec_{1s};
|
||||
|
||||
const Json::Value& config_;
|
||||
struct AdaptiveDelay {
|
||||
std::chrono::milliseconds delay;
|
||||
std::chrono::seconds delta{0};
|
||||
|
||||
explicit AdaptiveDelay(std::chrono::milliseconds initial = std::chrono::seconds(1))
|
||||
: delay(initial) {}
|
||||
|
||||
bool increase() {
|
||||
if (delta == std::chrono::seconds{0}) {
|
||||
delta += std::chrono::seconds{1};
|
||||
delay += delta;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool decrease() {
|
||||
if (delta > std::chrono::seconds{0}) {
|
||||
delay -= delta;
|
||||
delta -= std::chrono::seconds{1};
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::chrono::milliseconds current() const { return delay; }
|
||||
|
||||
void reset(std::chrono::milliseconds new_delay) {
|
||||
delay = new_delay;
|
||||
delta = std::chrono::seconds{0};
|
||||
}
|
||||
};
|
||||
|
||||
AdaptiveDelay adaptive_delay_;
|
||||
|
||||
Json::Value config_;
|
||||
int re_paint_{0};
|
||||
bool silence_{false};
|
||||
bool silence_prev_{false};
|
||||
std::chrono::seconds suspend_silence_delay_{0};
|
||||
int sleep_counter_{0};
|
||||
std::string output_{};
|
||||
// Methods
|
||||
void invoke();
|
||||
void execute();
|
||||
bool isSilence();
|
||||
bool isSilent();
|
||||
void doUpdate(bool force = false);
|
||||
void loadConfig();
|
||||
void freeBackend();
|
||||
|
||||
// Signal
|
||||
type_signal_update m_signal_update_;
|
||||
type_signal_audio_raw_update m_signal_audio_raw_;
|
||||
type_signal_silence m_signal_silence_;
|
||||
SignalUpdate m_signal_update_;
|
||||
SignalAudioRawUpdate m_signal_audio_raw_;
|
||||
SignalSilence m_signal_silence_;
|
||||
SignalConfigChanged m_signal_config_changed_;
|
||||
|
||||
std::atomic<bool> shutdown_{false};
|
||||
bool audio_raw_initialized_{false};
|
||||
mutable std::recursive_mutex state_mutex_;
|
||||
|
||||
// Synchronization for joining read_thread_ during destruction
|
||||
bool read_thread_exited_{false};
|
||||
mutable std::mutex read_thread_exit_mutex_;
|
||||
std::condition_variable read_thread_exit_cv_;
|
||||
};
|
||||
} // namespace waybar::modules::cava
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#ifdef HAVE_LIBCAVA
|
||||
#include "cavaRaw.hpp"
|
||||
#include "cava_backend.hpp"
|
||||
@@ -9,16 +11,16 @@
|
||||
#endif
|
||||
|
||||
namespace waybar::modules::cava {
|
||||
AModule* getModule(const std::string& id, const Json::Value& config) {
|
||||
inline std::unique_ptr<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) {
|
||||
switch (backend_->getPrm().output) {
|
||||
#ifdef HAVE_LIBCAVAGLSL
|
||||
case ::cava::output_method::OUTPUT_SDL_GLSL:
|
||||
return new waybar::modules::cava::CavaGLSL(id, config);
|
||||
return std::make_unique<waybar::modules::cava::CavaGLSL>(id, config);
|
||||
#endif
|
||||
default:
|
||||
return new waybar::modules::cava::Cava(id, config);
|
||||
return std::make_unique<waybar::modules::cava::CavaRaw>(id, config);
|
||||
}
|
||||
#else
|
||||
throw std::runtime_error("Unknown module");
|
||||
|
||||
@@ -12,6 +12,7 @@ const std::string kOrdPlaceholder{"ordinal_date"};
|
||||
|
||||
enum class CldMode { MONTH, YEAR };
|
||||
enum class WS { LEFT, RIGHT, HIDDEN };
|
||||
enum class WeekNumbering { LOCALE, ISO, MONDAY, SUNDAY };
|
||||
|
||||
class Clock final : public ALabel {
|
||||
public:
|
||||
@@ -51,6 +52,7 @@ class Clock final : public ALabel {
|
||||
date::day cldBaseDay_{0}; // calendar Cached day. Is used when today is changing(midnight)
|
||||
std::string cldText_{""}; // calendar text to print
|
||||
bool iso8601Calendar_{false}; // whether the calendar is in ISO8601
|
||||
WeekNumbering weekNumbering_{WeekNumbering::LOCALE}; // week number calculation method
|
||||
CldMode cldMode_{CldMode::MONTH};
|
||||
auto get_calendar(const date::year_month_day& today, const date::year_month_day& ymd,
|
||||
const date::time_zone* tz) -> const std::string;
|
||||
@@ -80,6 +82,7 @@ class Clock final : public ALabel {
|
||||
void cldShift_reset();
|
||||
void tz_up();
|
||||
void tz_down();
|
||||
void action_exec(const std::string& action);
|
||||
// Module Action Map
|
||||
static inline std::map<const std::string, void (waybar::modules::Clock::* const)()> actionMap_{
|
||||
{"mode", &waybar::modules::Clock::cldModeSwitch},
|
||||
@@ -88,6 +91,9 @@ class Clock final : public ALabel {
|
||||
{"shift_reset", &waybar::modules::Clock::cldShift_reset},
|
||||
{"tz_up", &waybar::modules::Clock::tz_up},
|
||||
{"tz_down", &waybar::modules::Clock::tz_down}};
|
||||
static inline std::map<const std::string,
|
||||
void (waybar::modules::Clock::* const)(const std::string& action)>
|
||||
actionWithArgsMap_{{"exec", &waybar::modules::Clock::action_exec}};
|
||||
};
|
||||
|
||||
} // namespace waybar::modules
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <fstream>
|
||||
#include <numeric>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "AGraph.hpp"
|
||||
#include "util/sleeper_thread.hpp"
|
||||
|
||||
namespace waybar::modules {
|
||||
|
||||
class CpuGraph : public AGraph {
|
||||
public:
|
||||
CpuGraph(const std::string&, const Json::Value&);
|
||||
virtual ~CpuGraph() = default;
|
||||
auto update() -> void override;
|
||||
|
||||
private:
|
||||
static constexpr const char* MODERATE_CLASS = "cpu-moderate";
|
||||
static constexpr const char* HIGH_CLASS = "cpu-high";
|
||||
static constexpr const char* INTENSIVE_CLASS = "cpu-intensive";
|
||||
|
||||
std::vector<std::tuple<size_t, size_t>> prev_times_;
|
||||
util::SleeperThread thread_;
|
||||
};
|
||||
|
||||
} // namespace waybar::modules
|
||||
@@ -3,16 +3,18 @@
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include <csignal>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "ALabel.hpp"
|
||||
#include "AIconLabel.hpp"
|
||||
#include "util/command.hpp"
|
||||
#include "util/command_line_stream.hpp"
|
||||
#include "util/json.hpp"
|
||||
#include "util/sleeper_thread.hpp"
|
||||
|
||||
namespace waybar::modules {
|
||||
|
||||
class Custom : public ALabel {
|
||||
class Custom : public AIconLabel {
|
||||
public:
|
||||
Custom(const std::string&, const std::string&, const Json::Value&, const std::string&);
|
||||
virtual ~Custom();
|
||||
@@ -22,6 +24,9 @@ class Custom : public ALabel {
|
||||
private:
|
||||
void delayWorker();
|
||||
void continuousWorker();
|
||||
void startContinuousProcess(bool throw_on_failure);
|
||||
void handleContinuousProcessExit(int exit_code);
|
||||
void scheduleContinuousRestart();
|
||||
void waitingWorker();
|
||||
void parseOutputRaw();
|
||||
void parseOutputJson();
|
||||
@@ -36,13 +41,16 @@ class Custom : public ALabel {
|
||||
std::string alt_;
|
||||
std::string tooltip_;
|
||||
std::string last_tooltip_markup_;
|
||||
std::string image_path_;
|
||||
std::string image_name_;
|
||||
unsigned app_icon_size_{24};
|
||||
const bool tooltip_format_enabled_;
|
||||
std::vector<std::string> class_;
|
||||
int percentage_;
|
||||
FILE* fp_;
|
||||
int pid_;
|
||||
util::command::res output_;
|
||||
util::JsonParser parser_;
|
||||
std::unique_ptr<util::command::LineStream> continuous_stream_;
|
||||
sigc::connection restart_connection_;
|
||||
|
||||
util::SleeperThread thread_;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include <csignal>
|
||||
#include <string>
|
||||
|
||||
#include "AGraph.hpp"
|
||||
#include "util/command.hpp"
|
||||
#include "util/json.hpp"
|
||||
#include "util/sleeper_thread.hpp"
|
||||
|
||||
namespace waybar::modules {
|
||||
|
||||
class CustomGraph : public AGraph {
|
||||
public:
|
||||
CustomGraph(const std::string&, const std::string&, const Json::Value&, const std::string&);
|
||||
virtual ~CustomGraph();
|
||||
auto update() -> void override;
|
||||
void refresh(int /*signal*/) override;
|
||||
|
||||
private:
|
||||
void delayWorker();
|
||||
void continuousWorker();
|
||||
void waitingWorker();
|
||||
void parseOutputRaw();
|
||||
void parseOutputJson();
|
||||
void handleEvent();
|
||||
bool handleScroll(GdkEventScroll* e) override;
|
||||
bool handleToggle(GdkEventButton* const& e) override;
|
||||
|
||||
const std::string name_;
|
||||
const std::string output_name_;
|
||||
std::string text_;
|
||||
std::string id_;
|
||||
std::string alt_;
|
||||
std::string tooltip_;
|
||||
const bool tooltip_format_enabled_;
|
||||
std::vector<std::string> class_;
|
||||
int percentage_;
|
||||
FILE* fp_;
|
||||
int pid_;
|
||||
util::command::res output_;
|
||||
util::JsonParser parser_;
|
||||
|
||||
util::SleeperThread thread_;
|
||||
};
|
||||
|
||||
} // namespace waybar::modules
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <sys/statvfs.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
|
||||
#include "ALabel.hpp"
|
||||
#include "util/format.hpp"
|
||||
@@ -19,7 +20,9 @@ class Disk : public ALabel {
|
||||
|
||||
private:
|
||||
util::SleeperThread thread_;
|
||||
std::string path_;
|
||||
std::string header_;
|
||||
std::vector<std::string> paths_;
|
||||
std::string separator_;
|
||||
std::string unit_;
|
||||
|
||||
float calc_specific_divisor(const std::string& divisor);
|
||||
|
||||
@@ -21,6 +21,8 @@ class Tags : public waybar::AModule {
|
||||
void handle_primary_clicked(uint32_t tag);
|
||||
bool handle_button_press(GdkEventButton* event_button, uint32_t tag);
|
||||
|
||||
void handle_active_output(zdwl_ipc_output_v2* zdwl_output_v2, uint32_t active);
|
||||
|
||||
struct zdwl_ipc_manager_v2* status_manager_;
|
||||
struct wl_seat* seat_;
|
||||
|
||||
@@ -28,6 +30,7 @@ class Tags : public waybar::AModule {
|
||||
const waybar::Bar& bar_;
|
||||
Gtk::Box box_;
|
||||
std::vector<Gtk::Button> buttons_;
|
||||
bool hide_vacant_;
|
||||
struct zdwl_ipc_output_v2* output_status_;
|
||||
};
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ class Window : public AAppIconLabel, public sigc::trackable {
|
||||
void handle_layout(const uint32_t layout);
|
||||
void handle_title(const char* title);
|
||||
void handle_appid(const char* ppid);
|
||||
void handle_active(const uint32_t active);
|
||||
void handle_layout_symbol(const char* layout_symbol);
|
||||
void handle_frame();
|
||||
|
||||
@@ -30,6 +31,9 @@ class Window : public AAppIconLabel, public sigc::trackable {
|
||||
std::string title_;
|
||||
std::string appid_;
|
||||
std::string layout_symbol_;
|
||||
bool active_;
|
||||
bool hide_inactive_;
|
||||
bool hide_empty_;
|
||||
uint32_t layout_;
|
||||
|
||||
struct zdwl_ipc_output_v2* output_status_;
|
||||
|
||||
@@ -37,6 +37,7 @@ class Language : public waybar::ALabel, public EventHandler {
|
||||
util::JsonParser parser_;
|
||||
|
||||
Layout layout_;
|
||||
std::string prev_short_name_; // applied CSS class; touched only in update() (#4665)
|
||||
|
||||
IPC& m_ipc;
|
||||
};
|
||||
|
||||
@@ -26,9 +26,11 @@ class Submap : public waybar::ALabel, public EventHandler {
|
||||
const Bar& bar_;
|
||||
util::JsonParser parser_;
|
||||
std::string submap_;
|
||||
std::string icon_;
|
||||
std::string prev_submap_;
|
||||
bool always_on_ = false;
|
||||
std::string default_submap_ = "Default";
|
||||
std::unordered_map<std::string, std::string> icons_;
|
||||
|
||||
IPC& m_ipc;
|
||||
};
|
||||
|
||||
@@ -30,7 +30,8 @@ class Workspace {
|
||||
public:
|
||||
explicit Workspace(const Json::Value& workspace_data, Workspaces& workspace_manager,
|
||||
const Json::Value& clients_data = Json::Value::nullRef);
|
||||
std::string& selectIcon(std::map<std::string, std::string>& icons_map);
|
||||
~Workspace();
|
||||
std::string& selectString(std::map<std::string, std::string>& string_map);
|
||||
Gtk::Button& button() { return m_button; };
|
||||
|
||||
int id() const { return m_id; };
|
||||
@@ -45,12 +46,22 @@ class Workspace {
|
||||
bool isUrgent() const { return m_isUrgent; };
|
||||
|
||||
bool handleClicked(GdkEventButton* bt) const;
|
||||
|
||||
bool handleEnter(GdkEventCrossing* event);
|
||||
bool handleLeave(GdkEventCrossing* event);
|
||||
|
||||
void startHoverCheck();
|
||||
void stopHoverCheck();
|
||||
bool syncHoverClass();
|
||||
bool pointerInsideButton();
|
||||
|
||||
void setActive(bool value = true) { m_isActive = value; };
|
||||
void setPersistentRule(bool value = true) { m_isPersistentRule = value; };
|
||||
void setPersistentConfig(bool value = true) { m_isPersistentConfig = value; };
|
||||
void setUrgent(bool value = true) { m_isUrgent = value; };
|
||||
void setVisible(bool value = true) { m_isVisible = value; };
|
||||
void setWindows(uint value) { m_windows = value; };
|
||||
void setId(int value) { m_id = value; };
|
||||
void setName(std::string const& value) { m_name = value; };
|
||||
void setOutput(std::string const& value) { m_output = value; };
|
||||
bool containsWindow(WindowAddress const& addr) const {
|
||||
@@ -64,13 +75,14 @@ class Workspace {
|
||||
bool onWindowOpened(WindowCreationPayload const& create_window_payload);
|
||||
std::optional<WindowRepr> closeWindow(WindowAddress const& addr);
|
||||
|
||||
void update(const std::string& workspace_icon);
|
||||
void update(const std::string& workspace_icon, const std::string& workspace_tooltip);
|
||||
|
||||
private:
|
||||
Workspaces& m_workspaceManager;
|
||||
|
||||
int m_id;
|
||||
std::string m_name;
|
||||
std::string m_prevNameClass;
|
||||
std::string m_output;
|
||||
uint m_windows;
|
||||
bool m_isActive = false;
|
||||
@@ -80,6 +92,8 @@ class Workspace {
|
||||
bool m_isUrgent = false;
|
||||
bool m_isVisible = false;
|
||||
|
||||
sigc::connection m_hoverCheckConnection;
|
||||
|
||||
std::vector<WindowRepr> m_windowMap;
|
||||
|
||||
Gtk::Button m_button;
|
||||
|
||||
@@ -39,9 +39,11 @@ class Workspaces : public AModule, public EventHandler {
|
||||
auto allOutputs() const -> bool { return m_allOutputs; }
|
||||
auto showSpecial() const -> bool { return m_showSpecial; }
|
||||
auto activeOnly() const -> bool { return m_activeOnly; }
|
||||
auto hideActive() const -> bool { return m_hideActive; }
|
||||
auto specialVisibleOnly() const -> bool { return m_specialVisibleOnly; }
|
||||
auto persistentOnly() const -> bool { return m_persistentOnly; }
|
||||
auto moveToMonitor() const -> bool { return m_moveToMonitor; }
|
||||
auto uniqueIcons() const -> bool { return m_uniqueIcons; }
|
||||
auto enableTaskbar() const -> bool { return m_enableTaskbar; }
|
||||
auto taskbarWithIcon() const -> bool { return m_taskbarWithIcon; }
|
||||
auto barScroll() const -> bool { return m_barScroll; }
|
||||
@@ -52,16 +54,20 @@ class Workspaces : public AModule, public EventHandler {
|
||||
auto taskbarFormatBefore() const -> std::string { return m_taskbarFormatBefore; }
|
||||
auto taskbarFormatAfter() const -> std::string { return m_taskbarFormatAfter; }
|
||||
auto taskbarIconSize() const -> int { return m_taskbarIconSize; }
|
||||
auto taskbarMaxIcons() const -> int { return m_taskbarMaxIcons; }
|
||||
auto taskbarOrientation() const -> Gtk::Orientation { return m_taskbarOrientation; }
|
||||
auto taskbarReverseDirection() const -> bool { return m_taskbarReverseDirection; }
|
||||
auto onClickWindow() const -> std::string { return m_onClickWindow; }
|
||||
auto getIgnoredWindows() const -> std::vector<std::regex> { return m_ignoreWindows; }
|
||||
auto maxWindows() const -> int { return m_maxWindows; }
|
||||
|
||||
enum class ActiveWindowPosition { NONE, FIRST, LAST };
|
||||
auto activeWindowPosition() const -> ActiveWindowPosition { return m_activeWindowPosition; }
|
||||
|
||||
std::string getRewrite(const std::string& window_class, const std::string& window_title);
|
||||
std::string& getWindowSeparator() { return m_formatWindowSeparator; }
|
||||
auto windowRewriteGroupThreshold() const -> int { return m_windowRewriteGroupThreshold; }
|
||||
auto const& getWindowRewriteGroupFormat() const { return m_windowRewriteGroupFormat; }
|
||||
bool isWorkspaceIgnored(std::string const& workspace_name);
|
||||
|
||||
bool windowRewriteConfigUsesTitle() const { return m_anyWindowRewriteRuleUsesTitle; }
|
||||
@@ -89,6 +95,7 @@ class Workspaces : public AModule, public EventHandler {
|
||||
auto populateIgnoreWorkspacesConfig(const Json::Value& config) -> void;
|
||||
auto populateFormatWindowSeparatorConfig(const Json::Value& config) -> void;
|
||||
auto populateWindowRewriteConfig(const Json::Value& config) -> void;
|
||||
auto populateMaxWindowsConfig(const Json::Value& config) -> void;
|
||||
auto populateWorkspaceTaskbarConfig(const Json::Value& config) -> void;
|
||||
|
||||
void registerIpc();
|
||||
@@ -101,6 +108,7 @@ class Workspaces : public AModule, public EventHandler {
|
||||
Json::Value const& clientsData = Json::Value::nullRef);
|
||||
void onWorkspaceMoved(std::string const& payload);
|
||||
void onWorkspaceRenamed(std::string const& payload);
|
||||
void onWorkspaceIdChanged(std::string const& payload);
|
||||
static std::optional<int> parseWorkspaceId(std::string const& workspaceIdStr);
|
||||
|
||||
// monitor events
|
||||
@@ -146,9 +154,11 @@ class Workspaces : public AModule, public EventHandler {
|
||||
bool m_allOutputs = false;
|
||||
bool m_showSpecial = false;
|
||||
bool m_activeOnly = false;
|
||||
bool m_hideActive = false;
|
||||
bool m_specialVisibleOnly = false;
|
||||
bool m_persistentOnly = false;
|
||||
bool m_moveToMonitor = false;
|
||||
bool m_uniqueIcons = false;
|
||||
bool m_barScroll = false;
|
||||
Json::Value m_persistentWorkspaceConfig;
|
||||
|
||||
@@ -158,21 +168,25 @@ class Workspaces : public AModule, public EventHandler {
|
||||
std::map<WindowAddress, WindowRepr, std::less<>> m_orphanWindowMap;
|
||||
|
||||
enum class SortMethod { ID, NAME, NUMBER, SPECIAL_CENTERED, DEFAULT };
|
||||
util::EnumParser<SortMethod> m_enumParser;
|
||||
SortMethod m_sortBy = SortMethod::DEFAULT;
|
||||
std::map<std::string, SortMethod> m_sortMap = {{"ID", SortMethod::ID},
|
||||
{"NAME", SortMethod::NAME},
|
||||
{"NUMBER", SortMethod::NUMBER},
|
||||
{"SPECIAL-CENTERED", SortMethod::SPECIAL_CENTERED},
|
||||
{"DEFAULT", SortMethod::DEFAULT}};
|
||||
static inline const std::map<std::string, SortMethod> m_sortMap = {
|
||||
{"ID", SortMethod::ID},
|
||||
{"NAME", SortMethod::NAME},
|
||||
{"NUMBER", SortMethod::NUMBER},
|
||||
{"SPECIAL-CENTERED", SortMethod::SPECIAL_CENTERED},
|
||||
{"DEFAULT", SortMethod::DEFAULT}};
|
||||
|
||||
std::string m_formatBefore;
|
||||
std::string m_formatAfter;
|
||||
|
||||
std::map<std::string, std::string> m_iconsMap;
|
||||
std::map<std::string, std::string> m_tooltipMap;
|
||||
bool m_withTooltip = false;
|
||||
util::RegexCollection m_windowRewriteRules;
|
||||
bool m_anyWindowRewriteRuleUsesTitle = false;
|
||||
std::string m_formatWindowSeparator;
|
||||
int m_windowRewriteGroupThreshold = 0;
|
||||
std::string m_windowRewriteGroupFormat = "{icon}×{count}";
|
||||
|
||||
bool m_withIcon;
|
||||
uint64_t m_monitorId;
|
||||
@@ -191,17 +205,18 @@ class Workspaces : public AModule, public EventHandler {
|
||||
std::string m_taskbarFormatBefore;
|
||||
std::string m_taskbarFormatAfter;
|
||||
int m_taskbarIconSize = 16;
|
||||
int m_taskbarMaxIcons = 0; // 0 means unlimited
|
||||
Gtk::Orientation m_taskbarOrientation = Gtk::ORIENTATION_HORIZONTAL;
|
||||
bool m_taskbarReverseDirection = false;
|
||||
util::EnumParser<ActiveWindowPosition> m_activeWindowEnumParser;
|
||||
ActiveWindowPosition m_activeWindowPosition = ActiveWindowPosition::NONE;
|
||||
std::map<std::string, ActiveWindowPosition> m_activeWindowPositionMap = {
|
||||
static inline std::map<std::string, ActiveWindowPosition> m_activeWindowPositionMap = {
|
||||
{"NONE", ActiveWindowPosition::NONE},
|
||||
{"FIRST", ActiveWindowPosition::FIRST},
|
||||
{"LAST", ActiveWindowPosition::LAST},
|
||||
};
|
||||
std::string m_onClickWindow;
|
||||
std::string m_currentActiveWindowAddress;
|
||||
int m_maxWindows = 0;
|
||||
|
||||
std::vector<std::regex> m_ignoreWorkspaces;
|
||||
std::vector<std::regex> m_ignoreWindows;
|
||||
@@ -211,6 +226,10 @@ class Workspaces : public AModule, public EventHandler {
|
||||
Gtk::Box m_box;
|
||||
sigc::connection m_scrollEventConnection_;
|
||||
IPC& m_ipc;
|
||||
|
||||
// Coalesces bursts of Hyprland events into a single UI refresh. Armed and
|
||||
// disconnected only on the GTK main thread (see Workspaces::update).
|
||||
sigc::connection m_debounceTimer;
|
||||
};
|
||||
|
||||
} // namespace waybar::modules::hyprland
|
||||
|
||||
@@ -6,25 +6,42 @@
|
||||
#include "bar.hpp"
|
||||
#include "client.hpp"
|
||||
|
||||
struct ext_idle_notification_v1;
|
||||
|
||||
namespace waybar::modules {
|
||||
|
||||
class IdleInhibitor : public ALabel {
|
||||
sigc::connection timeout_;
|
||||
ext_idle_notification_v1* idle_notification_;
|
||||
uint32_t idle_timeout_ms_;
|
||||
|
||||
public:
|
||||
IdleInhibitor(const std::string&, const waybar::Bar&, const Json::Value&);
|
||||
virtual ~IdleInhibitor();
|
||||
auto update() -> void override;
|
||||
auto refresh(int) -> void override;
|
||||
static std::list<waybar::AModule*> modules;
|
||||
static bool status;
|
||||
static long deactivationTime;
|
||||
|
||||
private:
|
||||
bool handleToggle(GdkEventButton* const& e) override;
|
||||
void toggleStatus();
|
||||
bool handleScroll(GdkEventScroll* e) override;
|
||||
|
||||
void toggleStatus(int force_status = -1);
|
||||
void setupIdleNotification();
|
||||
void teardownIdleNotification();
|
||||
static void handleIdled(void* data, ext_idle_notification_v1* notification);
|
||||
static void handleResumed(void* data, ext_idle_notification_v1* notification);
|
||||
|
||||
const Bar& bar_;
|
||||
struct zwp_idle_inhibitor_v1* idle_inhibitor_;
|
||||
int pid_;
|
||||
|
||||
bool dynamicTimeout = false;
|
||||
double timeout;
|
||||
double timeout_step;
|
||||
bool wait_for_activity_;
|
||||
};
|
||||
|
||||
} // namespace waybar::modules
|
||||
|
||||
@@ -14,6 +14,69 @@
|
||||
|
||||
namespace waybar::modules {
|
||||
|
||||
namespace image {
|
||||
|
||||
class IStrategy {
|
||||
public:
|
||||
virtual ~IStrategy() = default;
|
||||
// Runs on the worker thread before update(). Use it for blocking work (e.g.
|
||||
// spawning a user script) so the GTK main loop isn't stalled. Default no-op.
|
||||
virtual void fetch() {}
|
||||
virtual void update() = 0;
|
||||
};
|
||||
|
||||
class SingleImageStrategy : public IStrategy {
|
||||
public:
|
||||
SingleImageStrategy(const std::string&, const Json::Value&, const std::string&, Gtk::EventBox&,
|
||||
bool);
|
||||
~SingleImageStrategy() override = default;
|
||||
void update() override;
|
||||
|
||||
private:
|
||||
void parseOutputRaw();
|
||||
|
||||
util::command::res output_;
|
||||
Json::Value config_;
|
||||
Gtk::Image image_;
|
||||
std::string path_;
|
||||
std::string tooltip_;
|
||||
int size_;
|
||||
Gtk::Box box_;
|
||||
bool hasTooltip_;
|
||||
};
|
||||
|
||||
class MultipleImageStrategy : public IStrategy {
|
||||
public:
|
||||
MultipleImageStrategy(const std::string&, const Json::Value&, const std::string&, Gtk::EventBox&);
|
||||
~MultipleImageStrategy() override = default;
|
||||
void fetch() override;
|
||||
void update() override;
|
||||
|
||||
private:
|
||||
struct ImageData {
|
||||
std::string path;
|
||||
std::string marker;
|
||||
std::string tooltip;
|
||||
std::string on_click;
|
||||
std::shared_ptr<Gtk::Image> img;
|
||||
std::shared_ptr<Gtk::Button> btn;
|
||||
};
|
||||
|
||||
void setImagesData(const Json::Value&);
|
||||
void setupAndDraw();
|
||||
void resetBoxAndMemory();
|
||||
void handleClick(const Glib::ustring& data);
|
||||
|
||||
Json::Value config_;
|
||||
int size_;
|
||||
Gtk::Box box_;
|
||||
std::vector<ImageData> images_data_;
|
||||
// stdout captured by fetch() on the worker thread and consumed by update()
|
||||
std::string exec_output_;
|
||||
};
|
||||
|
||||
} // namespace image
|
||||
|
||||
class Image : public AModule {
|
||||
public:
|
||||
Image(const std::string&, const Json::Value&);
|
||||
@@ -24,16 +87,11 @@ class Image : public AModule {
|
||||
private:
|
||||
void delayWorker();
|
||||
void handleEvent();
|
||||
void parseOutputRaw();
|
||||
static std::unique_ptr<image::IStrategy> getStrategy(const std::string&, const Json::Value&,
|
||||
const std::string&, Gtk::EventBox&, bool);
|
||||
|
||||
Gtk::Box box_;
|
||||
Gtk::Image image_;
|
||||
std::string path_;
|
||||
std::string tooltip_;
|
||||
int size_;
|
||||
std::chrono::milliseconds interval_;
|
||||
util::command::res output_;
|
||||
|
||||
std::unique_ptr<image::IStrategy> strategy_;
|
||||
util::SleeperThread thread_;
|
||||
};
|
||||
|
||||
|
||||
@@ -36,8 +36,7 @@ class KeyboardState : public AModule {
|
||||
std::string capslock_format_;
|
||||
std::string scrolllock_format_;
|
||||
const std::chrono::seconds interval_;
|
||||
std::string icon_locked_;
|
||||
std::string icon_unlocked_;
|
||||
std::unordered_map<std::string, std::vector<std::string>> key_icon_states_;
|
||||
std::string devices_path_;
|
||||
|
||||
struct libinput* libinput_;
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
// include/modules/mango/backend.hpp
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <list>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "util/json.hpp"
|
||||
|
||||
namespace waybar::modules::mango {
|
||||
|
||||
class EventHandler {
|
||||
public:
|
||||
virtual void onEvent(const Json::Value& ev) = 0;
|
||||
virtual ~EventHandler() = default;
|
||||
};
|
||||
|
||||
class IPC {
|
||||
public:
|
||||
static IPC& getInstance();
|
||||
IPC(const IPC&) = delete;
|
||||
IPC& operator=(const IPC&) = delete;
|
||||
|
||||
void registerForIPC(const std::string& ev, EventHandler* handler);
|
||||
void unregisterForIPC(EventHandler* handler);
|
||||
|
||||
static Json::Value send(const Json::Value& request);
|
||||
static void sendAsync(const Json::Value& request);
|
||||
|
||||
std::unique_lock<std::mutex> lockData() { return std::unique_lock<std::mutex>(data_mutex_); }
|
||||
|
||||
std::unordered_map<std::string, Json::Value> getMonitors() const;
|
||||
Json::Value getMonitor(const std::string& name);
|
||||
Json::Value getActiveClientForMonitor(const std::string& name) const;
|
||||
std::string getKeyboardLayout() const;
|
||||
std::string getKeymode() const;
|
||||
std::string getLayoutSymbolForMonitor(const std::string& name) const;
|
||||
|
||||
private:
|
||||
IPC();
|
||||
~IPC();
|
||||
void startIPC();
|
||||
static int connectToSocket();
|
||||
void parseIPC(const std::string& line);
|
||||
|
||||
void handleMonitorUpdate(const Json::Value& mon);
|
||||
void updateFocusingClient(const Json::Value& client);
|
||||
void updateKeyboardLayout(const std::string& layout);
|
||||
|
||||
static Json::Value sendCommand(const std::string& cmd);
|
||||
|
||||
std::atomic<bool> running_ = true;
|
||||
int sockfd_ = -1;
|
||||
std::thread ipc_thread_;
|
||||
mutable std::mutex data_mutex_;
|
||||
std::unordered_map<std::string, Json::Value> monitors_;
|
||||
std::unordered_map<uint64_t, Json::Value> clients_;
|
||||
uint64_t focusing_client_id_ = 0;
|
||||
std::string keyboard_layout_;
|
||||
std::string keymode_;
|
||||
Json::Value active_client_;
|
||||
std::mutex callback_mutex_;
|
||||
std::list<std::pair<std::string, EventHandler*>> callbacks_;
|
||||
};
|
||||
} // namespace waybar::modules::mango
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
#include "ALabel.hpp"
|
||||
#include "bar.hpp"
|
||||
#include "modules/mango/backend.hpp"
|
||||
|
||||
namespace waybar::modules::mango {
|
||||
|
||||
class Keymode : public ALabel, public EventHandler {
|
||||
public:
|
||||
Keymode(const std::string&, const Bar&, const Json::Value&);
|
||||
~Keymode() override;
|
||||
void update() override;
|
||||
|
||||
private:
|
||||
void onEvent(const Json::Value& ev) override;
|
||||
void doUpdate();
|
||||
|
||||
std::mutex mutex_;
|
||||
const Bar& bar_;
|
||||
std::string last_keymode_;
|
||||
};
|
||||
|
||||
} // namespace waybar::modules::mango
|
||||
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include <xkbcommon/xkbregistry.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "ALabel.hpp"
|
||||
#include "bar.hpp"
|
||||
#include "modules/mango/backend.hpp"
|
||||
|
||||
namespace waybar::modules::mango {
|
||||
|
||||
class Language : public ALabel, public EventHandler {
|
||||
public:
|
||||
Language(const std::string&, const Bar&, const Json::Value&);
|
||||
~Language() override;
|
||||
void update() override;
|
||||
|
||||
private:
|
||||
void updateFromIPC();
|
||||
void onEvent(const Json::Value& ev) override;
|
||||
void doUpdate();
|
||||
|
||||
struct Layout {
|
||||
std::string full_name;
|
||||
std::string short_name;
|
||||
std::string variant;
|
||||
std::string short_description;
|
||||
};
|
||||
|
||||
Layout getLayout(const std::string& fullName);
|
||||
|
||||
std::mutex mutex_;
|
||||
const Bar& bar_;
|
||||
|
||||
std::vector<Layout> layouts_;
|
||||
unsigned current_idx_;
|
||||
std::string last_short_name_;
|
||||
|
||||
struct rxkb_context* rxkb_ctx_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace waybar::modules::mango
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
#include "ALabel.hpp"
|
||||
#include "bar.hpp"
|
||||
#include "modules/mango/backend.hpp"
|
||||
|
||||
namespace waybar::modules::mango {
|
||||
|
||||
class Layout : public ALabel, public EventHandler {
|
||||
public:
|
||||
Layout(const std::string&, const Bar&, const Json::Value&);
|
||||
~Layout() override;
|
||||
void update() override;
|
||||
|
||||
private:
|
||||
void onEvent(const Json::Value& ev) override;
|
||||
void doUpdate();
|
||||
|
||||
std::mutex mutex_;
|
||||
const Bar& bar_;
|
||||
std::string last_symbol_;
|
||||
};
|
||||
|
||||
} // namespace waybar::modules::mango
|
||||
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <gtkmm/button.h>
|
||||
#include <json/value.h>
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include "AAppIconLabel.hpp"
|
||||
#include "bar.hpp"
|
||||
#include "modules/mango/backend.hpp"
|
||||
|
||||
namespace waybar::modules::mango {
|
||||
|
||||
class Window : public AAppIconLabel, public EventHandler {
|
||||
public:
|
||||
Window(const std::string&, const Bar&, const Json::Value&);
|
||||
~Window() override;
|
||||
void update() override;
|
||||
|
||||
private:
|
||||
void onEvent(const Json::Value& ev) override;
|
||||
void doUpdate();
|
||||
void setClass(const std::string& className, bool enable);
|
||||
|
||||
const Bar& bar_;
|
||||
std::string oldAppId_;
|
||||
std::mutex mutex_;
|
||||
};
|
||||
|
||||
} // namespace waybar::modules::mango
|
||||
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <gtkmm/button.h>
|
||||
#include <json/value.h>
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
#include "AModule.hpp"
|
||||
#include "bar.hpp"
|
||||
#include "modules/mango/backend.hpp"
|
||||
|
||||
namespace waybar::modules::mango {
|
||||
|
||||
class Workspaces : public AModule, public EventHandler {
|
||||
public:
|
||||
Workspaces(const std::string&, const Bar&, const Json::Value&);
|
||||
~Workspaces() override;
|
||||
void update() override;
|
||||
|
||||
private:
|
||||
void onEvent(const Json::Value& ev) override;
|
||||
void doUpdate();
|
||||
|
||||
Gtk::Button& addButton(uint64_t idx);
|
||||
void updateButtonState(Gtk::Button& button, const Json::Value& tag, const Json::Value& monitor);
|
||||
std::string getIcon(const std::string& value, const Json::Value& tag);
|
||||
bool handleButtonClick(GdkEventButton* event, uint64_t idx, bool isOverview);
|
||||
|
||||
const Bar& bar_;
|
||||
Gtk::Box box_;
|
||||
|
||||
std::unordered_map<uint64_t, Gtk::Button> buttons_;
|
||||
Gtk::Button* overview_button_ = nullptr;
|
||||
|
||||
std::string on_click_left_;
|
||||
std::string on_click_middle_;
|
||||
std::string on_click_right_;
|
||||
};
|
||||
|
||||
} // namespace waybar::modules::mango
|
||||
@@ -19,8 +19,6 @@ class Memory : public ALabel {
|
||||
private:
|
||||
void parseMeminfo();
|
||||
|
||||
static float calc_divisor(const std::string& divisor);
|
||||
|
||||
std::unordered_map<std::string, unsigned long> meminfo_;
|
||||
|
||||
util::SleeperThread thread_;
|
||||
|
||||
@@ -28,11 +28,14 @@ class MPD : public ALabel {
|
||||
|
||||
unsigned timeout_;
|
||||
|
||||
unsigned playing_interval_;
|
||||
|
||||
detail::unique_connection connection_;
|
||||
|
||||
detail::unique_status status_;
|
||||
mpd_state state_;
|
||||
detail::unique_song song_;
|
||||
std::string ellipsis_;
|
||||
|
||||
public:
|
||||
MPD(const std::string&, const Json::Value&);
|
||||
@@ -45,6 +48,10 @@ class MPD : public ALabel {
|
||||
void setLabel();
|
||||
std::string getStateIcon() const;
|
||||
std::string getOptionIcon(const std::string& optionName, bool activated) const;
|
||||
std::string getArtistStr(bool truncated) const;
|
||||
std::string getAlbumArtistStr(bool truncated) const;
|
||||
std::string getAlbumStr(bool truncated) const;
|
||||
std::string getTitleStr(bool truncated) const;
|
||||
|
||||
// GUI-side methods
|
||||
bool handlePlayPause(GdkEventButton* const&);
|
||||
@@ -54,11 +61,11 @@ class MPD : public ALabel {
|
||||
void tryConnect();
|
||||
void checkErrors(mpd_connection* conn);
|
||||
void fetchState();
|
||||
void queryMPD();
|
||||
|
||||
inline bool stopped() const { return connection_ && state_ == MPD_STATE_STOP; }
|
||||
inline bool playing() const { return connection_ && state_ == MPD_STATE_PLAY; }
|
||||
inline bool paused() const { return connection_ && state_ == MPD_STATE_PAUSE; }
|
||||
inline unsigned playing_interval() const { return playing_interval_; }
|
||||
};
|
||||
|
||||
#if !defined(MPD_NOINLINE)
|
||||
|
||||
@@ -82,6 +82,7 @@ class Idle : public State {
|
||||
class Playing : public State {
|
||||
Context* const ctx_;
|
||||
sigc::connection timer_connection_;
|
||||
sigc::connection idle_connection_;
|
||||
|
||||
public:
|
||||
Playing(Context* const ctx) : ctx_{ctx} {}
|
||||
@@ -98,7 +99,10 @@ class Playing : public State {
|
||||
Playing(Playing const&) = delete;
|
||||
Playing& operator=(Playing const&) = delete;
|
||||
|
||||
void timer() noexcept;
|
||||
void idle() noexcept;
|
||||
bool on_timer();
|
||||
bool on_io(Glib::IOCondition const&);
|
||||
};
|
||||
|
||||
class Paused : public State {
|
||||
@@ -194,10 +198,10 @@ class Context {
|
||||
bool is_paused() const;
|
||||
bool is_stopped() const;
|
||||
constexpr std::size_t interval() const;
|
||||
unsigned playing_interval() const;
|
||||
void tryConnect() const;
|
||||
void checkErrors(mpd_connection*) const;
|
||||
void do_update();
|
||||
void queryMPD() const;
|
||||
void fetchState() const;
|
||||
constexpr mpd_state state() const;
|
||||
void emit() const;
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
namespace detail {
|
||||
using namespace std::literals::chrono_literals;
|
||||
|
||||
inline bool Context::is_connected() const { return mpd_module_->connection_ != nullptr; }
|
||||
inline bool Context::is_playing() const { return mpd_module_->playing(); }
|
||||
inline bool Context::is_paused() const { return mpd_module_->paused(); }
|
||||
inline bool Context::is_stopped() const { return mpd_module_->stopped(); }
|
||||
|
||||
constexpr inline std::size_t Context::interval() const { return mpd_module_->interval_.count(); }
|
||||
constexpr inline std::size_t Context::interval() const { return mpd_module_->interval_ / 1s; }
|
||||
inline unsigned Context::playing_interval() const { return mpd_module_->playing_interval(); }
|
||||
inline void Context::tryConnect() const { mpd_module_->tryConnect(); }
|
||||
inline unique_connection& Context::connection() { return mpd_module_->connection_; }
|
||||
constexpr inline mpd_state Context::state() const { return mpd_module_->state_; }
|
||||
@@ -15,7 +17,6 @@ constexpr inline mpd_state Context::state() const { return mpd_module_->state_;
|
||||
inline void Context::do_update() { mpd_module_->setLabel(); }
|
||||
|
||||
inline void Context::checkErrors(mpd_connection* conn) const { mpd_module_->checkErrors(conn); }
|
||||
inline void Context::queryMPD() const { mpd_module_->queryMPD(); }
|
||||
inline void Context::fetchState() const { mpd_module_->fetchState(); }
|
||||
inline void Context::emit() const { mpd_module_->emit(); }
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ class Mpris : public ALabel {
|
||||
|
||||
std::optional<std::string> artist;
|
||||
std::optional<std::string> album;
|
||||
std::optional<std::string> album_artist;
|
||||
std::optional<std::string> title;
|
||||
std::optional<std::string> length; // as HH:MM:SS
|
||||
std::optional<std::string> position; // same format
|
||||
@@ -76,6 +77,8 @@ class Mpris : public ALabel {
|
||||
std::string player_;
|
||||
std::vector<std::string> ignored_players_;
|
||||
|
||||
bool prefer_album_artist_;
|
||||
|
||||
PlayerctlPlayerManager* manager;
|
||||
PlayerctlPlayer* player;
|
||||
PlayerctlPlayer* last_active_player_ = nullptr;
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
#include "util/rfkill.hpp"
|
||||
#endif
|
||||
|
||||
#define ETH_ALEN 6
|
||||
|
||||
enum ip_addr_pref : uint8_t { IPV4, IPV6, IPV4_6 };
|
||||
|
||||
namespace waybar::modules {
|
||||
@@ -34,6 +36,7 @@ class Network : public ALabel {
|
||||
static int handleEvents(struct nl_msg*, void*);
|
||||
static int handleEventsDone(struct nl_msg*, void*);
|
||||
static int handleScan(struct nl_msg*, void*);
|
||||
static int handleStationGet(struct nl_msg *msg, void *data);
|
||||
|
||||
void askForStateDump(void);
|
||||
|
||||
@@ -48,15 +51,18 @@ class Network : public ALabel {
|
||||
bool matchInterface(const std::string& ifname, const std::vector<std::string>& altnames,
|
||||
std::string& matched) const;
|
||||
auto getInfo() -> void;
|
||||
bool isWireless() const;
|
||||
const std::string getNetworkState() const;
|
||||
void clearIface();
|
||||
std::optional<std::pair<unsigned long long, unsigned long long>> readBandwidthUsage();
|
||||
uint32_t readLinkSpeed() const;
|
||||
|
||||
int ifid_{-1};
|
||||
ip_addr_pref addr_pref_{ip_addr_pref::IPV4};
|
||||
struct sockaddr_nl nladdr_{0};
|
||||
struct nl_sock* sock_{nullptr};
|
||||
struct nl_sock* ev_sock_{nullptr};
|
||||
struct nl_sock* station_sock_{nullptr};
|
||||
int efd_{-1};
|
||||
int ev_fd_{-1};
|
||||
int nl80211_id_{-1};
|
||||
@@ -90,6 +96,7 @@ class Network : public ALabel {
|
||||
uint8_t signal_strength_;
|
||||
std::string signal_strength_app_;
|
||||
uint32_t route_priority;
|
||||
uint32_t link_speed_{0};
|
||||
|
||||
util::SleeperThread thread_;
|
||||
util::SleeperThread thread_timer_;
|
||||
@@ -97,6 +104,8 @@ class Network : public ALabel {
|
||||
util::Rfkill rfkill_{RFKILL_TYPE_WLAN};
|
||||
#endif
|
||||
float frequency_{0};
|
||||
uint32_t tx_bitrate_{0};
|
||||
uint32_t rx_bitrate_{0};
|
||||
};
|
||||
|
||||
} // namespace waybar::modules
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <list>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
@@ -18,6 +19,7 @@ class EventHandler {
|
||||
class IPC {
|
||||
public:
|
||||
IPC();
|
||||
~IPC();
|
||||
|
||||
void registerForIPC(const std::string& ev, EventHandler* ev_handler);
|
||||
void unregisterForIPC(EventHandler* handler);
|
||||
@@ -32,7 +34,7 @@ class IPC {
|
||||
unsigned keyboardLayoutCurrent() const { return keyboardLayoutCurrent_; }
|
||||
|
||||
private:
|
||||
void startIPC();
|
||||
void startIPC(int initial_socketfd);
|
||||
static int connectToSocket();
|
||||
void parseIPC(const std::string&);
|
||||
|
||||
@@ -45,6 +47,8 @@ class IPC {
|
||||
util::JsonParser parser_;
|
||||
std::mutex callbackMutex_;
|
||||
std::list<std::pair<std::string, EventHandler*>> callbacks_;
|
||||
|
||||
std::atomic<bool> running_{true};
|
||||
};
|
||||
|
||||
inline std::unique_ptr<IPC> gIPC;
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include <gtkmm/box.h>
|
||||
#include <gtkmm/button.h>
|
||||
#include <gtkmm/image.h>
|
||||
#include <gtkmm/label.h>
|
||||
#include <json/value.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace waybar::modules::niri {
|
||||
|
||||
class Workspaces;
|
||||
|
||||
class Workspace {
|
||||
public:
|
||||
Workspace(const Json::Value& workspace_data, Workspaces& manager);
|
||||
~Workspace() = default;
|
||||
|
||||
Workspace(const Workspace&) = delete;
|
||||
Workspace& operator=(const Workspace&) = delete;
|
||||
|
||||
Gtk::Button& button() { return button_; }
|
||||
uint64_t id() const { return id_; }
|
||||
|
||||
void update(const Json::Value& workspace_data, const std::vector<Json::Value>& all_windows,
|
||||
const std::string& windows_str, std::size_t total);
|
||||
|
||||
private:
|
||||
void rebuildTaskbar(const std::vector<Json::Value>& my_windows);
|
||||
|
||||
Glib::RefPtr<Gdk::Pixbuf> loadIcon(const std::string& app_id, int size);
|
||||
|
||||
Workspaces& manager_;
|
||||
uint64_t id_;
|
||||
|
||||
// Layout: button_
|
||||
// └─ box_ (horizontal)
|
||||
// ├─ label_ workspace label / icon
|
||||
// └─ taskbar_box_ app icon buttons (shown only when taskbar enabled)
|
||||
Gtk::Button button_;
|
||||
Gtk::Box box_;
|
||||
Gtk::Label label_;
|
||||
Gtk::Box taskbar_box_;
|
||||
};
|
||||
|
||||
} // namespace waybar::modules::niri
|
||||
@@ -1,30 +1,62 @@
|
||||
#pragma once
|
||||
|
||||
#include <gtkmm/button.h>
|
||||
#include <gtkmm/box.h>
|
||||
#include <json/value.h>
|
||||
|
||||
#include <memory>
|
||||
#include <regex>
|
||||
#include <vector>
|
||||
|
||||
#include "AModule.hpp"
|
||||
#include "bar.hpp"
|
||||
#include "modules/niri/backend.hpp"
|
||||
#include "modules/niri/workspace.hpp"
|
||||
#include "util/regex_collection.hpp" // Added for rewrite rules
|
||||
|
||||
namespace waybar::modules::niri {
|
||||
|
||||
class Workspaces : public AModule, public EventHandler {
|
||||
public:
|
||||
Workspaces(const std::string&, const Bar&, const Json::Value&);
|
||||
Workspaces(const std::string& id, const Bar& bar, const Json::Value& config);
|
||||
~Workspaces() override;
|
||||
|
||||
void update() override;
|
||||
|
||||
const Json::Value& config() const { return config_; }
|
||||
const Bar& bar() const { return bar_; }
|
||||
|
||||
std::string getIcon(const std::string& value, const Json::Value& ws) const;
|
||||
|
||||
private:
|
||||
void onEvent(const Json::Value& ev) override;
|
||||
void doUpdate();
|
||||
Gtk::Button& addButton(const Json::Value& ws);
|
||||
std::string getIcon(const std::string& value, const Json::Value& ws);
|
||||
void createWorkspace(const Json::Value& workspace_data);
|
||||
void sortWorkspaces(std::vector<const Json::Value*>& workspaces) const;
|
||||
bool isWorkspaceIgnored(const std::string& name);
|
||||
bool handleScroll(GdkEventScroll* /*unused*/) override;
|
||||
// Added for window rewrite
|
||||
void populateWindowRewriteConfig();
|
||||
void populateFormatWindowSeparatorConfig();
|
||||
std::string getRewrite(const std::string& app_id, const std::string& title);
|
||||
std::string getWindowsRepresentation(const Json::Value& ws);
|
||||
|
||||
const Bar& bar_;
|
||||
Gtk::Box box_;
|
||||
// Map from niri workspace id to button.
|
||||
std::unordered_map<uint64_t, Gtk::Button> buttons_;
|
||||
|
||||
std::vector<std::unique_ptr<Workspace>> workspaces_;
|
||||
|
||||
// Vec of regex rules to ignore workspaces.
|
||||
std::vector<std::regex> ignoreWorkspaces_;
|
||||
|
||||
bool sort_by_id_ = false;
|
||||
bool sort_by_name_ = false;
|
||||
bool sort_by_coordinates_ = false;
|
||||
|
||||
// Added for window rewrite
|
||||
util::RegexCollection m_windowRewriteRules;
|
||||
std::string m_windowRewriteDefault;
|
||||
std::string m_formatWindowSeparator;
|
||||
|
||||
};
|
||||
|
||||
} // namespace waybar::modules::niri
|
||||
|
||||
@@ -9,9 +9,18 @@ namespace waybar::modules {
|
||||
|
||||
struct Profile {
|
||||
std::string name;
|
||||
// Legacy driver field, kept for backward compatibility with the
|
||||
// `{driver}` format placeholder and with older power-profiles-daemon
|
||||
// versions that only expose a single `Driver` DBus property.
|
||||
std::string driver;
|
||||
std::string cpuDriver;
|
||||
std::string platformDriver;
|
||||
|
||||
Profile(std::string n, std::string d) : name(std::move(n)), driver(std::move(d)) {}
|
||||
Profile(std::string n, std::string d, std::string cd, std::string pd)
|
||||
: name(std::move(n)),
|
||||
driver(std::move(d)),
|
||||
cpuDriver(std::move(cd)),
|
||||
platformDriver(std::move(pd)) {}
|
||||
};
|
||||
|
||||
class PowerProfilesDaemon : public ALabel {
|
||||
|
||||
@@ -1,40 +1,50 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <string>
|
||||
|
||||
#include "gtkmm/box.h"
|
||||
#include "modules/privacy/privacy_item.hpp"
|
||||
#include "util/geoclue_backend.hpp"
|
||||
#include "util/pipewire/pipewire_backend.hpp"
|
||||
#include "util/pipewire/privacy_node_info.hpp"
|
||||
|
||||
using waybar::util::PipewireBackend::PrivacyNodeInfo;
|
||||
using waybar::util::PipewireBackend::PrivacyNodeType;
|
||||
using waybar::util::PipewireBackend::PWPrivacyNodeInfo;
|
||||
|
||||
namespace waybar::modules::privacy {
|
||||
|
||||
class Privacy : public AModule {
|
||||
public:
|
||||
Privacy(const std::string&, const Json::Value&, Gtk::Orientation, const std::string& pos);
|
||||
~Privacy() override;
|
||||
auto update() -> void override;
|
||||
|
||||
void onPrivacyNodesChanged();
|
||||
|
||||
private:
|
||||
std::list<PrivacyNodeInfo*> nodes_screenshare; // Screen is being shared
|
||||
std::list<PrivacyNodeInfo*> nodes_audio_in; // Application is using the microphone
|
||||
std::list<PrivacyNodeInfo*> nodes_audio_out; // Application is outputting audio
|
||||
std::list<PWPrivacyNodeInfo*> nodes_screenshare; // Screen is being shared
|
||||
std::list<PWPrivacyNodeInfo*> nodes_audio_in; // Application is using the microphone
|
||||
std::list<PWPrivacyNodeInfo*> nodes_audio_out; // Application is outputting audio
|
||||
std::atomic<bool> location_in_use; // GeoClue is being used
|
||||
|
||||
std::mutex mutex_;
|
||||
sigc::connection visibility_conn;
|
||||
sigc::connection geoclue_timeout_conn;
|
||||
|
||||
// Config
|
||||
Gtk::Box box_;
|
||||
std::vector<PrivacyItem*> modules_;
|
||||
uint iconSpacing = 4;
|
||||
uint iconSize = 20;
|
||||
uint transition_duration = 250;
|
||||
std::set<std::pair<PrivacyNodeType, std::string>> ignore;
|
||||
bool ignore_monitor = true;
|
||||
|
||||
std::shared_ptr<util::PipewireBackend::PipewireBackend> backend = nullptr;
|
||||
std::shared_ptr<util::PipewireBackend::PipewireBackend> pw_backend = nullptr;
|
||||
std::shared_ptr<util::GeoClueBackend::GeoClueBackend> geoclue_backend = nullptr;
|
||||
|
||||
void onPWPrivacyNodesChanged();
|
||||
bool locationTimeout(bool in_use);
|
||||
void onGeoCluePrivacyNodesChanged();
|
||||
};
|
||||
|
||||
} // namespace waybar::modules::privacy
|
||||
|
||||
@@ -9,35 +9,36 @@
|
||||
#include "gtkmm/revealer.h"
|
||||
#include "util/pipewire/privacy_node_info.hpp"
|
||||
|
||||
using waybar::util::PipewireBackend::PrivacyNodeInfo;
|
||||
using waybar::util::PipewireBackend::PrivacyNodeType;
|
||||
using waybar::util::PipewireBackend::PWPrivacyNodeInfo;
|
||||
|
||||
namespace waybar::modules::privacy {
|
||||
|
||||
class PrivacyItem : public Gtk::Revealer {
|
||||
public:
|
||||
protected:
|
||||
PrivacyItem(const Json::Value& config_, enum PrivacyNodeType privacy_type_,
|
||||
std::list<PrivacyNodeInfo*>* nodes, Gtk::Orientation orientation,
|
||||
const std::string& pos, const uint icon_size, const uint transition_duration);
|
||||
Gtk::Orientation orientation, const std::string& pos, const uint icon_size,
|
||||
const uint transition_duration);
|
||||
|
||||
public:
|
||||
virtual void set_tooltip() = 0;
|
||||
|
||||
enum PrivacyNodeType privacy_type;
|
||||
|
||||
void set_in_use(bool in_use);
|
||||
|
||||
private:
|
||||
std::list<PrivacyNodeInfo*>* nodes;
|
||||
|
||||
sigc::connection signal_conn;
|
||||
|
||||
uint tooltipIconSize = 24;
|
||||
Gtk::Box tooltip_window;
|
||||
|
||||
private:
|
||||
sigc::connection signal_conn;
|
||||
|
||||
bool init = false;
|
||||
bool in_use = false;
|
||||
|
||||
// Config
|
||||
std::string iconName = "image-missing-symbolic";
|
||||
bool tooltip = true;
|
||||
uint tooltipIconSize = 24;
|
||||
|
||||
Gtk::Box box_;
|
||||
Gtk::Image icon_;
|
||||
@@ -45,4 +46,28 @@ class PrivacyItem : public Gtk::Revealer {
|
||||
void update_tooltip();
|
||||
};
|
||||
|
||||
class GeoCluePrivacyItem : public PrivacyItem {
|
||||
public:
|
||||
GeoCluePrivacyItem(const Json::Value& config_, Gtk::Orientation orientation,
|
||||
const std::string& pos, const uint icon_size, const uint transition_duration)
|
||||
: PrivacyItem(config_, util::PipewireBackend::PRIVACY_NODE_TYPE_LOCATION, orientation, pos,
|
||||
icon_size, transition_duration) {}
|
||||
|
||||
void set_tooltip() override;
|
||||
};
|
||||
|
||||
class PWPrivacyItem : public PrivacyItem {
|
||||
public:
|
||||
PWPrivacyItem(const Json::Value& config_, enum PrivacyNodeType privacy_type_,
|
||||
std::list<PWPrivacyNodeInfo*>* nodes_, Gtk::Orientation orientation,
|
||||
const std::string& pos, const uint icon_size, const uint transition_duration)
|
||||
: PrivacyItem(config_, privacy_type_, orientation, pos, icon_size, transition_duration),
|
||||
nodes(nodes_) {}
|
||||
|
||||
void set_tooltip() override;
|
||||
|
||||
private:
|
||||
std::list<PWPrivacyNodeInfo*>* nodes;
|
||||
};
|
||||
|
||||
} // namespace waybar::modules::privacy
|
||||
|
||||
@@ -22,6 +22,7 @@ class Pulseaudio : public ALabel {
|
||||
const std::vector<std::string> getPulseIcon() const;
|
||||
|
||||
std::shared_ptr<util::AudioBackend> backend = nullptr;
|
||||
util::PulseaudioTarget target = util::PulseaudioTarget::Sink;
|
||||
};
|
||||
|
||||
} // namespace waybar::modules
|
||||
|
||||
@@ -6,11 +6,6 @@
|
||||
#include "util/audio_backend.hpp"
|
||||
namespace waybar::modules {
|
||||
|
||||
enum class PulseaudioSliderTarget {
|
||||
Sink,
|
||||
Source,
|
||||
};
|
||||
|
||||
class PulseaudioSlider : public ASlider {
|
||||
public:
|
||||
PulseaudioSlider(const std::string&, const Json::Value&);
|
||||
@@ -21,7 +16,15 @@ class PulseaudioSlider : public ASlider {
|
||||
|
||||
private:
|
||||
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
|
||||
|
||||
@@ -21,6 +21,8 @@ class Tags : public waybar::AModule {
|
||||
void handle_view_tags(struct wl_array* tags);
|
||||
void handle_urgent_tags(uint32_t tags);
|
||||
void handle_focused_view(const char *title, uint32_t tags);
|
||||
void handle_focused_output(struct wl_output* output);
|
||||
void handle_unfocused_output(struct wl_output* output);
|
||||
|
||||
void handle_show();
|
||||
void handle_primary_clicked(uint32_t tag);
|
||||
@@ -28,17 +30,17 @@ class Tags : public waybar::AModule {
|
||||
|
||||
struct zriver_status_manager_v1* status_manager_;
|
||||
struct zriver_control_v1* control_;
|
||||
struct zriver_seat_status_v1 *seat_status_;
|
||||
struct wl_seat* seat_;
|
||||
// used to make sure the focused view tags are on the correct output
|
||||
const wl_output* output_;
|
||||
const wl_output* focused_output_;
|
||||
|
||||
private:
|
||||
const waybar::Bar& bar_;
|
||||
struct wl_output* focused_output_; // stores the focused output
|
||||
struct wl_output* output_; // stores the output this module belongs to
|
||||
Gtk::Box box_;
|
||||
std::vector<Gtk::Button> buttons_;
|
||||
struct zriver_output_status_v1* output_status_;
|
||||
struct zriver_seat_status_v1* seat_status_;
|
||||
bool hide_vacant_ = false; // parsed once; asBool() in a wl callback would throw (#4078)
|
||||
};
|
||||
|
||||
} /* namespace waybar::modules::river */
|
||||
|
||||
@@ -14,16 +14,22 @@ namespace waybar::modules::SNI {
|
||||
|
||||
class Host {
|
||||
public:
|
||||
Host(const std::size_t id, const Json::Value&, const Bar&,
|
||||
Host(std::size_t id, const Json::Value&, const Bar&, const std::vector<std::string>&,
|
||||
const std::function<void(std::unique_ptr<Item>&)>&,
|
||||
const std::function<void(std::unique_ptr<Item>&)>&, const std::function<void()>&);
|
||||
const std::function<void(std::unique_ptr<Item>&)>&, const std::function<void()>&,
|
||||
const std::function<void()>&);
|
||||
~Host();
|
||||
|
||||
void checkIgnoreList(const std::vector<std::string>& ignore_list,
|
||||
const std::function<void(std::unique_ptr<Item>&)>& on_remove);
|
||||
|
||||
void reorderItems();
|
||||
|
||||
private:
|
||||
void busAcquired(const Glib::RefPtr<Gio::DBus::Connection>&, Glib::ustring);
|
||||
void nameAppeared(const Glib::RefPtr<Gio::DBus::Connection>&, Glib::ustring,
|
||||
void busAcquired(const Glib::RefPtr<Gio::DBus::Connection>&, const Glib::ustring&);
|
||||
void nameAppeared(const Glib::RefPtr<Gio::DBus::Connection>&, const Glib::ustring&,
|
||||
const Glib::ustring&);
|
||||
void nameVanished(const Glib::RefPtr<Gio::DBus::Connection>&, Glib::ustring);
|
||||
void nameVanished(const Glib::RefPtr<Gio::DBus::Connection>&, const Glib::ustring&);
|
||||
static void proxyReady(GObject*, GAsyncResult*, gpointer);
|
||||
static void registerHost(GObject*, GAsyncResult*, gpointer);
|
||||
static void itemRegistered(SnWatcher*, const gchar*, gpointer);
|
||||
@@ -33,7 +39,7 @@ class Host {
|
||||
void removeItem(std::vector<std::unique_ptr<Item>>::iterator);
|
||||
void clearItems();
|
||||
|
||||
std::tuple<std::string, std::string> getBusNameAndObjectPath(const std::string);
|
||||
static std::tuple<std::string, std::string> getBusNameAndObjectPath(const std::string&);
|
||||
void addRegisteredItem(const std::string& service);
|
||||
|
||||
std::vector<std::unique_ptr<Item>> items_;
|
||||
@@ -43,10 +49,19 @@ class Host {
|
||||
std::size_t watcher_id_;
|
||||
GCancellable* cancellable_ = nullptr;
|
||||
SnWatcher* watcher_ = nullptr;
|
||||
sigc::connection retry_connection_;
|
||||
unsigned retry_count_ = 0;
|
||||
const Json::Value& config_;
|
||||
const Bar& bar_;
|
||||
const std::vector<std::string> ignore_list_;
|
||||
const std::function<void(std::unique_ptr<Item>&)> on_add_;
|
||||
const std::function<void(std::unique_ptr<Item>&)> on_remove_;
|
||||
// Re-applies the configured ordering to the already-added tray widgets. This
|
||||
// must NOT re-run the add path (which would re-parent widgets and reconnect
|
||||
// signals); it only reorders existing children.
|
||||
const std::function<void()> on_reorder_;
|
||||
|
||||
ItemOrderMap orders_;
|
||||
const std::function<void()> on_update_;
|
||||
};
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
class IconManager {
|
||||
public:
|
||||
@@ -19,7 +20,10 @@ class IconManager {
|
||||
std::string app_name = key;
|
||||
const Json::Value& icon_value = icons_config[key];
|
||||
|
||||
if (icon_value.isString()) {
|
||||
if (icon_value.isBool() && !icon_value.asBool()) {
|
||||
// false value means hide this app
|
||||
hidden_apps_.insert(app_name);
|
||||
} else if (icon_value.isString()) {
|
||||
std::string icon_path = icon_value.asString();
|
||||
icons_map_[app_name] = icon_path;
|
||||
}
|
||||
@@ -37,7 +41,12 @@ class IconManager {
|
||||
return "";
|
||||
}
|
||||
|
||||
bool isHidden(const std::string& app_name) const {
|
||||
return hidden_apps_.find(app_name) != hidden_apps_.end();
|
||||
}
|
||||
|
||||
private:
|
||||
IconManager() = default;
|
||||
std::unordered_map<std::string, std::string> icons_map_;
|
||||
std::unordered_set<std::string> hidden_apps_;
|
||||
};
|
||||
|
||||
@@ -24,11 +24,15 @@ struct ToolTip {
|
||||
Glib::ustring text;
|
||||
};
|
||||
|
||||
class Host;
|
||||
|
||||
using ItemOrderMap = std::unordered_map<std::string, int>;
|
||||
|
||||
class Item : public sigc::trackable {
|
||||
public:
|
||||
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()>&);
|
||||
const std::function<void()>&, Host&, const ItemOrderMap&);
|
||||
~Item();
|
||||
|
||||
bool isReady() const;
|
||||
@@ -46,6 +50,7 @@ class Item : public sigc::trackable {
|
||||
std::string title;
|
||||
std::string icon_name;
|
||||
Glib::RefPtr<Gdk::Pixbuf> icon_pixmap;
|
||||
bool has_custom_icon_ = false;
|
||||
Glib::RefPtr<Gtk::IconTheme> icon_theme;
|
||||
std::string overlay_icon_name;
|
||||
Glib::RefPtr<Gdk::Pixbuf> overlay_icon_pixmap;
|
||||
@@ -63,6 +68,7 @@ class Item : public sigc::trackable {
|
||||
* while compliant SNI implementation would always reset the flag to desired value.
|
||||
*/
|
||||
bool item_is_menu = true;
|
||||
int order_ = -1; // -1 means not set
|
||||
|
||||
private:
|
||||
void onConfigure(GdkEventConfigure* ev);
|
||||
@@ -100,6 +106,8 @@ class Item : public sigc::trackable {
|
||||
gdouble distance_scrolled_y_ = 0;
|
||||
// visibility of items with Status == Passive
|
||||
bool show_passive_ = false;
|
||||
// hidden via config
|
||||
bool is_hidden_ = false;
|
||||
bool ready_ = false;
|
||||
Glib::ustring status_ = "active";
|
||||
|
||||
@@ -111,6 +119,9 @@ class Item : public sigc::trackable {
|
||||
Glib::RefPtr<Gio::DBus::Proxy> proxy_;
|
||||
Glib::RefPtr<Gio::Cancellable> cancellable_;
|
||||
std::set<std::string_view> update_pending_;
|
||||
|
||||
Host& host_;
|
||||
const ItemOrderMap& orders_;
|
||||
};
|
||||
|
||||
} // namespace waybar::modules::SNI
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <sigc++/connection.h>
|
||||
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
#include "AModule.hpp"
|
||||
#include "bar.hpp"
|
||||
@@ -13,19 +17,28 @@ namespace waybar::modules::SNI {
|
||||
class Tray : public AModule {
|
||||
public:
|
||||
Tray(const std::string&, const Bar&, const Json::Value&);
|
||||
virtual ~Tray() = default;
|
||||
~Tray() override = default;
|
||||
auto update() -> void override;
|
||||
|
||||
private:
|
||||
void onAdd(std::unique_ptr<Item>& item);
|
||||
void onRemove(std::unique_ptr<Item>& item);
|
||||
// Reorders the already-added tray widgets by their configured order. Does not
|
||||
// add or remove any widget.
|
||||
void reorderBox();
|
||||
void checkIgnoreList(std::unique_ptr<Item>* item);
|
||||
std::vector<std::string> parseIgnoreList(const Json::Value& config);
|
||||
void queueUpdate();
|
||||
|
||||
static inline std::size_t nb_hosts_ = 0;
|
||||
bool show_passive_ = false;
|
||||
Gtk::Box box_;
|
||||
SNI::Watcher::singleton watcher_;
|
||||
std::vector<std::string> ignore_list_;
|
||||
SNI::Host host_;
|
||||
std::vector<Item*> items_;
|
||||
// signal_show/signal_hide connections owned per added item, so they can be
|
||||
// disconnected on removal instead of leaking and accumulating.
|
||||
std::unordered_map<Item*, std::pair<sigc::connection, sigc::connection>> item_connections_;
|
||||
};
|
||||
|
||||
} // namespace waybar::modules::SNI
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <sigc++/sigc++.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "ipc.hpp"
|
||||
#include "util/SafeSignal.hpp"
|
||||
@@ -41,11 +39,20 @@ class Ipc {
|
||||
static inline const std::string ipc_magic_ = "i3-ipc";
|
||||
static inline const size_t ipc_header_size_ = ipc_magic_.size() + 8;
|
||||
|
||||
const std::string getSocketPath() const;
|
||||
int open(const std::string&) const;
|
||||
static std::string getSocketPath();
|
||||
static int open(const std::string&);
|
||||
|
||||
struct ipc_response send(int fd, uint32_t type, const std::string& payload = "");
|
||||
struct ipc_response recv(int fd);
|
||||
|
||||
// Re-establish the event socket and re-subscribe after sway drops us, backing
|
||||
// off between attempts so we don't busy-loop while sway is unavailable.
|
||||
void reconnectEvent();
|
||||
|
||||
std::string socketPath_;
|
||||
std::vector<std::string> subscribed_events_;
|
||||
std::atomic<bool> running_{true};
|
||||
|
||||
util::ScopedFd fd_;
|
||||
util::ScopedFd fd_event_;
|
||||
std::mutex mutex_;
|
||||
|
||||
@@ -54,6 +54,9 @@ class Language : public ALabel, public sigc::trackable {
|
||||
const static std::string XKB_ACTIVE_LAYOUT_NAME_KEY;
|
||||
|
||||
Layout layout_;
|
||||
// CSS class currently applied to label_. Tracked so update() (main thread) can swap classes
|
||||
// instead of set_current_layout() mutating the widget from the IPC worker thread (#3702).
|
||||
std::string applied_class_;
|
||||
std::string tooltip_format_ = "";
|
||||
std::map<std::string, Layout> layouts_map_;
|
||||
bool hide_single_;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <gtkmm/button.h>
|
||||
#include <gtkmm/label.h>
|
||||
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
|
||||
@@ -24,12 +25,15 @@ class Workspaces : public AModule, public sigc::trackable {
|
||||
|
||||
private:
|
||||
static constexpr std::string_view workspace_switch_cmd_ = "workspace {} \"{}\"";
|
||||
static constexpr std::string_view workspace_switch_number_cmd_ = "workspace {} number {}";
|
||||
static constexpr std::string_view persistent_workspace_switch_cmd_ =
|
||||
R"(workspace {} "{}"; move workspace to output "{}"; workspace {} "{}")";
|
||||
|
||||
static int convertWorkspaceNameToNum(const std::string& name);
|
||||
static int windowRewritePriorityFunction(std::string const& window_rule);
|
||||
|
||||
auto populateIgnoreWorkspacesConfig(const Json::Value& config) -> void;
|
||||
bool isWorkspaceIgnored(std::string const& name);
|
||||
void onCmd(const struct Ipc::ipc_response&);
|
||||
void onEvent(const struct Ipc::ipc_response&);
|
||||
bool filterButtons();
|
||||
@@ -41,6 +45,7 @@ class Workspaces : public AModule, public sigc::trackable {
|
||||
std::string getCycleWorkspace(std::vector<Json::Value>::iterator, bool prev) const;
|
||||
uint16_t getWorkspaceIndex(const std::string& name) const;
|
||||
static std::string trimWorkspaceName(const std::string&);
|
||||
std::optional<uint16_t> getCustomSortIndex(const std::string& name) const;
|
||||
bool handleScroll(GdkEventScroll* /*unused*/) override;
|
||||
|
||||
const Bar& bar_;
|
||||
@@ -49,9 +54,11 @@ class Workspaces : public AModule, public sigc::trackable {
|
||||
std::vector<std::string> workspaces_order_;
|
||||
Gtk::Box box_;
|
||||
std::string m_formatWindowSeparator;
|
||||
std::vector<std::regex> m_ignoreWorkspaces;
|
||||
util::RegexCollection m_windowRewriteRules;
|
||||
util::JsonParser parser_;
|
||||
std::unordered_map<std::string, Gtk::Button> buttons_;
|
||||
std::unordered_map<std::string, uint16_t> custom_sort_priorities_;
|
||||
std::mutex mutex_;
|
||||
Ipc ipc_;
|
||||
};
|
||||
|
||||
@@ -14,6 +14,8 @@ class Temperature : public ALabel {
|
||||
Temperature(const std::string&, const Json::Value&);
|
||||
virtual ~Temperature() = default;
|
||||
auto update() -> void override;
|
||||
void suspend() override;
|
||||
void resume() override;
|
||||
|
||||
private:
|
||||
float getTemperature();
|
||||
|
||||
@@ -17,6 +17,11 @@ class Wireplumber : public ALabel {
|
||||
auto update() -> void override;
|
||||
|
||||
private:
|
||||
bool setupConnection();
|
||||
void teardownConnection();
|
||||
void scheduleReconnect();
|
||||
bool onReconnectTimeout();
|
||||
static void onCoreDisconnected(waybar::modules::Wireplumber* self);
|
||||
void asyncLoadRequiredApiModules();
|
||||
void prepare(waybar::modules::Wireplumber* self);
|
||||
void activatePlugins();
|
||||
@@ -24,17 +29,25 @@ class Wireplumber : public ALabel {
|
||||
static void updateNodeName(waybar::modules::Wireplumber* self, uint32_t id);
|
||||
static void updateSourceVolume(waybar::modules::Wireplumber* self, uint32_t id);
|
||||
static void updateSourceName(waybar::modules::Wireplumber* self, uint32_t id); // NEW
|
||||
static void onPluginActivated(WpObject* p, GAsyncResult* res, waybar::modules::Wireplumber* self);
|
||||
static void onDefaultNodesApiLoaded(WpObject* p, GAsyncResult* res,
|
||||
waybar::modules::Wireplumber* self);
|
||||
static void onMixerApiLoaded(WpObject* p, GAsyncResult* res, waybar::modules::Wireplumber* self);
|
||||
static void onPluginActivated(WpObject* p, GAsyncResult* res, gpointer data);
|
||||
static void onDefaultNodesApiLoaded(WpObject* p, GAsyncResult* res, gpointer data);
|
||||
static void onMixerApiLoaded(WpObject* p, GAsyncResult* res, gpointer data);
|
||||
static void onObjectManagerInstalled(waybar::modules::Wireplumber* self);
|
||||
static void onMixerChanged(waybar::modules::Wireplumber* self, uint32_t id);
|
||||
static void onDefaultNodesApiChanged(waybar::modules::Wireplumber* self);
|
||||
|
||||
bool handleScroll(GdkEventScroll* e) override;
|
||||
std::vector<std::string> getWPIcon();
|
||||
|
||||
static std::list<waybar::modules::Wireplumber*> modules;
|
||||
// Returns true while `self` is still a live module. Async load/activation callbacks use this to
|
||||
// avoid dereferencing a `self` that was destroyed before the callback fired (see #3974).
|
||||
static bool isModuleAlive(waybar::modules::Wireplumber* self);
|
||||
|
||||
uint32_t resolvePhysicalSink(uint32_t start_id);
|
||||
uint32_t findPlaybackNodeId(const gchar* description);
|
||||
uint32_t get_linked_sink_id(WpObjectManager* om, uint32_t from_node_id);
|
||||
uint32_t get_linked_node_from_output_ports(WpObjectManager* om, uint32_t from_node_id);
|
||||
|
||||
WpCore* wp_core_;
|
||||
GPtrArray* apis_;
|
||||
@@ -43,6 +56,10 @@ class Wireplumber : public ALabel {
|
||||
WpPlugin* def_nodes_api_;
|
||||
gchar* default_node_name_;
|
||||
uint32_t pending_plugins_;
|
||||
// Bumped on every (re)connection. The async load/activate callbacks capture the generation they
|
||||
// were scheduled under (via their user_data) and no-op if it no longer matches, so a completion
|
||||
// from a connection that was already torn down cannot corrupt the new generation's state (#2882).
|
||||
uint32_t connection_generation_{0};
|
||||
bool muted_;
|
||||
double volume_;
|
||||
double min_step_;
|
||||
@@ -54,6 +71,12 @@ class Wireplumber : public ALabel {
|
||||
bool source_muted_;
|
||||
double source_volume_;
|
||||
gchar* default_source_name_;
|
||||
bool only_physical_;
|
||||
bool resolved_physical_;
|
||||
std::string form_factor_;
|
||||
// Timer used to retry connecting to PipeWire after it goes away; disconnected in the destructor
|
||||
// so a pending attempt can't outlive the module. See #2882.
|
||||
sigc::connection reconnect_timer_;
|
||||
};
|
||||
|
||||
} // namespace waybar::modules
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <ranges>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
@@ -18,6 +19,7 @@
|
||||
#include "AModule.hpp"
|
||||
#include "bar.hpp"
|
||||
#include "client.hpp"
|
||||
#include "ext-workspace-v1-client-protocol.h"
|
||||
#include "giomm/desktopappinfo.h"
|
||||
#include "util/icon_loader.hpp"
|
||||
#include "util/json.hpp"
|
||||
@@ -68,6 +70,11 @@ class Task {
|
||||
Glib::RefPtr<Gio::DesktopAppInfo> app_info_;
|
||||
bool button_visible_ = false;
|
||||
bool ignored_ = false;
|
||||
bool squashed_ = false;
|
||||
/* Whether the toplevel is on this bar's output, per the protocol's
|
||||
* output_enter/output_leave events */
|
||||
bool on_bar_output_ = false;
|
||||
bool size_allocate_connected_ = false;
|
||||
|
||||
bool with_icon_ = false;
|
||||
bool with_name_ = false;
|
||||
@@ -80,6 +87,7 @@ class Task {
|
||||
std::string title_;
|
||||
std::string app_id_;
|
||||
uint32_t state_ = 0;
|
||||
struct ext_workspace_handle_v1* workspace_ = nullptr;
|
||||
|
||||
int32_t drag_start_x;
|
||||
int32_t drag_start_y;
|
||||
@@ -91,6 +99,9 @@ class Task {
|
||||
void set_minimize_hint();
|
||||
void on_button_size_allocated(Gtk::Allocation& alloc);
|
||||
void hide_if_ignored();
|
||||
void hide_if_duplicate();
|
||||
void show_button();
|
||||
void hide_button();
|
||||
|
||||
public:
|
||||
/* Getter functions */
|
||||
@@ -102,6 +113,9 @@ class Task {
|
||||
bool minimized() const { return state_ & MINIMIZED; }
|
||||
bool active() const { return state_ & ACTIVE; }
|
||||
bool fullscreen() const { return state_ & FULLSCREEN; }
|
||||
bool visible() const { return button_visible_; }
|
||||
struct ext_workspace_handle_v1* workspace() const { return workspace_; }
|
||||
void set_workspace(struct ext_workspace_handle_v1* workspace) { workspace_ = workspace; }
|
||||
|
||||
public:
|
||||
/* Callbacks for the wlr protocol */
|
||||
@@ -142,6 +156,12 @@ using TaskPtr = std::unique_ptr<Task>;
|
||||
|
||||
class Taskbar : public waybar::AModule {
|
||||
public:
|
||||
struct WorkspaceState {
|
||||
Taskbar* taskbar;
|
||||
struct ext_workspace_handle_v1* handle;
|
||||
uint32_t state = 0;
|
||||
};
|
||||
|
||||
Taskbar(const std::string&, const waybar::Bar&, const Json::Value&);
|
||||
~Taskbar();
|
||||
void update();
|
||||
@@ -153,32 +173,56 @@ class Taskbar : public waybar::AModule {
|
||||
|
||||
IconLoader icon_loader_;
|
||||
std::unordered_set<std::string> ignore_list_;
|
||||
std::unordered_set<std::string> squash_list_;
|
||||
std::map<std::string, std::string> app_ids_replace_map_;
|
||||
|
||||
struct zwlr_foreign_toplevel_manager_v1* manager_;
|
||||
struct ext_workspace_manager_v1* workspace_manager_;
|
||||
struct wl_seat* seat_;
|
||||
std::vector<struct ext_workspace_group_handle_v1*> workspace_groups_;
|
||||
std::vector<std::unique_ptr<WorkspaceState>> workspaces_;
|
||||
struct ext_workspace_handle_v1* current_workspace_ = nullptr;
|
||||
|
||||
public:
|
||||
/* Callbacks for global registration */
|
||||
void register_manager(struct wl_registry*, uint32_t name, uint32_t version);
|
||||
void register_workspace_manager(struct wl_registry*, uint32_t name, uint32_t version);
|
||||
void register_seat(struct wl_registry*, uint32_t name, uint32_t version);
|
||||
|
||||
/* Callbacks for the wlr protocol */
|
||||
void handle_toplevel_create(struct zwlr_foreign_toplevel_handle_v1*);
|
||||
void handle_finished();
|
||||
void handle_workspace_group_create(struct ext_workspace_group_handle_v1*);
|
||||
void handle_workspace_group_removed(struct ext_workspace_group_handle_v1*);
|
||||
void handle_workspace_create(struct ext_workspace_handle_v1*);
|
||||
void handle_workspace_done();
|
||||
void handle_workspace_finished();
|
||||
void handle_workspace_removed(struct ext_workspace_handle_v1*);
|
||||
|
||||
public:
|
||||
void add_button(Gtk::Button&);
|
||||
void move_button(Gtk::Button&, int);
|
||||
void remove_button(Gtk::Button&);
|
||||
void remove_task(uint32_t);
|
||||
void assign_current_workspace(Task&);
|
||||
void update_bar_css_classes();
|
||||
|
||||
bool show_output(struct wl_output*) const;
|
||||
bool all_outputs() const;
|
||||
|
||||
const IconLoader& icon_loader() const;
|
||||
const std::unordered_set<std::string>& ignore_list() const;
|
||||
const std::unordered_set<std::string>& squash_list() const;
|
||||
const std::map<std::string, std::string>& app_ids_replace_map() const;
|
||||
std::size_t task_id_count(std::string_view id) const;
|
||||
std::size_t task_title_count(std::string_view title) const;
|
||||
|
||||
auto tasks() {
|
||||
return tasks_ | std::views::transform([](auto& task) -> Task& { return *task; });
|
||||
}
|
||||
|
||||
private:
|
||||
void set_bar_css_class(const std::string&, bool);
|
||||
};
|
||||
|
||||
} /* namespace waybar::modules::wlr */
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <libmm-glib/libmm-glib.h>
|
||||
#include <sys/statvfs.h>
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#include "ALabel.hpp"
|
||||
#include "util/format.hpp"
|
||||
#include "util/sleeper_thread.hpp"
|
||||
|
||||
namespace waybar::modules {
|
||||
|
||||
class Wwan : public ALabel {
|
||||
public:
|
||||
Wwan(const std::string&, const Json::Value&);
|
||||
virtual ~Wwan();
|
||||
auto update() -> void override;
|
||||
|
||||
private:
|
||||
void updateCurrentModem();
|
||||
|
||||
util::SleeperThread thread_;
|
||||
std::string state_;
|
||||
GDBusConnection* connection = nullptr;
|
||||
MMManager* manager = nullptr;
|
||||
MMModem* current_modem = nullptr;
|
||||
|
||||
bool hideDisconnected = true;
|
||||
|
||||
const std::string dbus_name = "org.freedesktop.ModemManager1";
|
||||
const std::string dbus_obj_path = "/org/freedesktop/ModemManager1/";
|
||||
const std::string dbus_modems_path = "/org/freedesktop/ModemManager1/Modems/";
|
||||
};
|
||||
|
||||
} // namespace waybar::modules
|
||||
@@ -12,11 +12,6 @@
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#ifdef __OpenBSD__
|
||||
#define SIGRTMIN SIGUSR1 - 1
|
||||
#define SIGRTMAX SIGUSR1 + 1
|
||||
#endif
|
||||
|
||||
namespace waybar {
|
||||
|
||||
/**
|
||||
|
||||
@@ -14,6 +14,11 @@
|
||||
|
||||
namespace waybar::util {
|
||||
|
||||
enum class PulseaudioTarget {
|
||||
Sink,
|
||||
Source,
|
||||
};
|
||||
|
||||
class AudioBackend {
|
||||
private:
|
||||
static void subscribeCb(pa_context*, pa_subscription_event_type_t, uint32_t, void*);
|
||||
@@ -22,12 +27,21 @@ class AudioBackend {
|
||||
static void sourceInfoCb(pa_context*, const pa_source_info* i, int, void* data);
|
||||
static void serverInfoCb(pa_context*, const pa_server_info*, void*);
|
||||
static void volumeModifyCb(pa_context*, int, void*);
|
||||
static void sourceVolumeModifyCb(pa_context*, int, void*);
|
||||
void connectContext();
|
||||
// Non-throwing reconnect used from the PulseAudio callback thread. Throwing
|
||||
// across the libpulse C callback boundary calls std::terminate, so this
|
||||
// swallows any failure and reports it via the return value instead.
|
||||
bool reconnectContext() noexcept;
|
||||
|
||||
pa_threaded_mainloop* mainloop_;
|
||||
pa_mainloop_api* mainloop_api_;
|
||||
pa_context* context_;
|
||||
// Guards against the FAILED -> connect -> FAILED recursion / busy loop when a
|
||||
// reconnect attempt fails synchronously inside pa_context_connect().
|
||||
bool reconnecting_{false};
|
||||
pa_cvolume pa_volume_;
|
||||
pa_cvolume pa_source_volume_;
|
||||
|
||||
// SINK
|
||||
uint32_t sink_idx_{0};
|
||||
@@ -50,6 +64,7 @@ class AudioBackend {
|
||||
std::string default_source_name_;
|
||||
|
||||
std::vector<std::string> ignored_sinks_;
|
||||
std::map<std::string, std::string> sink_mapping_;
|
||||
|
||||
std::function<void()> on_updated_cb_ = NOOP;
|
||||
|
||||
@@ -67,10 +82,13 @@ class AudioBackend {
|
||||
AudioBackend(std::function<void()> on_updated_cb, private_constructor_tag tag);
|
||||
~AudioBackend();
|
||||
|
||||
void changeVolume(uint16_t volume, uint16_t min_volume = 0, uint16_t max_volume = 100);
|
||||
void changeVolume(ChangeType change_type, double step = 1, uint16_t max_volume = 100);
|
||||
void changeVolume(uint16_t volume, uint16_t min_volume = 0, uint16_t max_volume = 100,
|
||||
PulseaudioTarget target = PulseaudioTarget::Sink);
|
||||
void changeVolume(ChangeType change_type, double step = 1, uint16_t max_volume = 100,
|
||||
PulseaudioTarget target = PulseaudioTarget::Sink);
|
||||
|
||||
void setIgnoredSinks(const Json::Value& config);
|
||||
void setSinkMapping(const Json::Value& config);
|
||||
|
||||
std::string getSinkPortName() const { return port_name_; }
|
||||
std::string getFormFactor() const { return form_factor_; }
|
||||
@@ -92,7 +110,11 @@ class AudioBackend {
|
||||
void toggleSourceMute();
|
||||
void toggleSourceMute(bool);
|
||||
|
||||
uint16_t getVolume(PulseaudioTarget) const;
|
||||
bool getMuted(PulseaudioTarget) const;
|
||||
void unmute(PulseaudioTarget);
|
||||
|
||||
bool isBluetooth();
|
||||
};
|
||||
|
||||
} // namespace waybar::util
|
||||
} // namespace waybar::util
|
||||
|
||||
@@ -27,9 +27,11 @@ namespace waybar::util {
|
||||
class BacklightDevice {
|
||||
public:
|
||||
BacklightDevice() = default;
|
||||
BacklightDevice(std::string name, int actual, int max, bool powered);
|
||||
BacklightDevice(std::string name, int actual, int max, bool powered,
|
||||
std::string subsystem = "backlight");
|
||||
|
||||
std::string name() const;
|
||||
std::string subsystem() const;
|
||||
int get_actual() const;
|
||||
void set_actual(int actual);
|
||||
int get_max() const;
|
||||
@@ -45,6 +47,7 @@ class BacklightDevice {
|
||||
int actual_ = 1;
|
||||
int max_ = 1;
|
||||
bool powered_ = true;
|
||||
std::string subsystem_ = "backlight";
|
||||
};
|
||||
|
||||
class BacklightBackend {
|
||||
@@ -70,7 +73,8 @@ class BacklightBackend {
|
||||
std::mutex udev_thread_mutex_;
|
||||
|
||||
private:
|
||||
void set_brightness_internal(const std::string& device_name, int brightness, int max_brightness);
|
||||
void set_brightness_internal(const std::string& device_name, int brightness, int max_brightness,
|
||||
const std::string& subsystem = "backlight");
|
||||
|
||||
std::function<void()> on_updated_cb_;
|
||||
std::chrono::milliseconds polling_interval_;
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <glibmm/main.h>
|
||||
#include <glibmm/spawn.h>
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
namespace waybar::util::command {
|
||||
|
||||
class LineStream {
|
||||
public:
|
||||
using OutputCallback = std::function<void(const std::string&)>;
|
||||
using ExitCallback = std::function<void(int)>;
|
||||
|
||||
LineStream(std::string output_name, OutputCallback on_output, ExitCallback on_exit);
|
||||
~LineStream();
|
||||
|
||||
void start(const std::string& cmd);
|
||||
void stop();
|
||||
bool running() const;
|
||||
|
||||
private:
|
||||
bool handleStdout(Glib::IOCondition condition);
|
||||
void handleExit(Glib::Pid pid, int status);
|
||||
void closeStdout();
|
||||
void drainStdout(bool flush_trailing_line);
|
||||
static int statusToExitCode(int status);
|
||||
|
||||
std::string output_name_;
|
||||
OutputCallback on_output_;
|
||||
ExitCallback on_exit_;
|
||||
std::string buffer_;
|
||||
Glib::Pid pid_;
|
||||
int stdout_fd_;
|
||||
sigc::connection stdout_connection_;
|
||||
sigc::connection child_connection_;
|
||||
};
|
||||
|
||||
} // namespace waybar::util::command
|
||||
@@ -14,12 +14,14 @@ struct pollfd;
|
||||
namespace waybar {
|
||||
class CssReloadHelper {
|
||||
public:
|
||||
CssReloadHelper(std::string cssFile, std::function<void()> callback);
|
||||
CssReloadHelper(std::string cssFile, std::function<void(const std::string&)> callback);
|
||||
|
||||
virtual ~CssReloadHelper() = default;
|
||||
|
||||
virtual void monitorChanges();
|
||||
|
||||
virtual void changeCssFile(const std::string& newCssFile);
|
||||
|
||||
protected:
|
||||
std::vector<std::string> parseImports(const std::string& cssFile);
|
||||
|
||||
@@ -42,7 +44,7 @@ class CssReloadHelper {
|
||||
private:
|
||||
std::string m_cssFile;
|
||||
|
||||
std::function<void()> m_callback;
|
||||
std::function<void(const std::string&)> m_callback;
|
||||
|
||||
std::vector<std::tuple<Glib::RefPtr<Gio::FileMonitor>>> m_fileMonitors;
|
||||
};
|
||||
|
||||
+21
-7
@@ -1,19 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <map>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#include "util/string.hpp"
|
||||
|
||||
namespace waybar::util {
|
||||
|
||||
template <typename EnumType>
|
||||
struct EnumParser {
|
||||
public:
|
||||
EnumParser();
|
||||
~EnumParser();
|
||||
|
||||
//struct EnumParser {
|
||||
EnumType parseStringToEnum(const std::string& str,
|
||||
const std::map<std::string, EnumType>& enumMap);
|
||||
};
|
||||
const std::map<std::string, EnumType>& enumMap) {
|
||||
std::string uppercaseStr = capitalize(str);
|
||||
std::map<std::string, EnumType> capitalizedEnumMap;
|
||||
std::transform(
|
||||
enumMap.begin(), enumMap.end(),
|
||||
std::inserter(capitalizedEnumMap, capitalizedEnumMap.end()),
|
||||
[](const auto& pair) {
|
||||
return std::make_pair(capitalize(pair.first), pair.second);
|
||||
});
|
||||
|
||||
auto it = capitalizedEnumMap.find(uppercaseStr);
|
||||
if (it != capitalizedEnumMap.end()) return it->second;
|
||||
|
||||
throw std::invalid_argument("Invalid string representation for enum");
|
||||
// }
|
||||
}
|
||||
|
||||
} // namespace waybar::util
|
||||
|
||||
+126
-42
@@ -5,41 +5,69 @@
|
||||
|
||||
class pow_format {
|
||||
public:
|
||||
pow_format(long long val, std::string&& unit, bool binary = false)
|
||||
: val_(val), unit_(unit), binary_(binary) {};
|
||||
pow_format(long long val, std::string&& unit, bool binary = false, bool skip_decimal = false,
|
||||
int min_pow_for_decimal = 0)
|
||||
: val_(val),
|
||||
unit_(unit),
|
||||
binary_(binary),
|
||||
skip_decimal_(skip_decimal),
|
||||
min_pow_for_decimal_(min_pow_for_decimal) {};
|
||||
|
||||
long long val_;
|
||||
std::string unit_;
|
||||
bool binary_;
|
||||
bool skip_decimal_;
|
||||
int min_pow_for_decimal_;
|
||||
};
|
||||
|
||||
namespace fmt {
|
||||
template <>
|
||||
struct formatter<pow_format> {
|
||||
char spec = 0;
|
||||
int width = 0;
|
||||
char spec = 0; // alignment: '>', '<', '=' (0 = none)
|
||||
int width = 0; // width digits; enforced only when scale_spec != 0
|
||||
char scale_spec = 0; // forced scale: 0 = auto, else one of '#','k','M','G','T','P'
|
||||
char unit_pref = 0; // unit tri-state: 0 = default, 'u' = hide, 'U' = show
|
||||
char base_pref = 0; // base tri-state: 0 = call-site, 'b' = decimal, 'B' = binary
|
||||
bool force_int = false; // 'i': force integer display
|
||||
|
||||
template <typename ParseContext>
|
||||
constexpr auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
auto it = ctx.begin(), end = ctx.end();
|
||||
if (it != end && *it == ':') ++it;
|
||||
if (it && (*it == '>' || *it == '<' || *it == '=')) {
|
||||
if (it != end && (*it == '>' || *it == '<' || *it == '=')) {
|
||||
spec = *it;
|
||||
++it;
|
||||
}
|
||||
if (it == end || *it == '}') return it;
|
||||
if ('0' <= *it && *it <= '9') {
|
||||
// We ignore it for now, but keep it for compatibility with
|
||||
// existing configs where the format for pow_format'ed numbers was
|
||||
// 'string' and specifications such as {:>9} were valid.
|
||||
// The rationale for ignoring it is that the only reason to specify
|
||||
// an alignment and a with is to get a fixed width bar, and ">" is
|
||||
// sufficient in this implementation.
|
||||
// Consume scale/flag modifiers and the width in any order, until '}' or end.
|
||||
// The width digits (parsed but only enforced when a scale is forced — see
|
||||
// format()) may appear anywhere among the modifiers, so both {:=#3} and the
|
||||
// more natural {:=3#} are accepted. On an unrecognised char we stop and let
|
||||
// fmt raise its usual error.
|
||||
while (it != end && *it != '}') {
|
||||
char c = *it;
|
||||
if (c == '#' || c == 'k' || c == 'M' || c == 'G' || c == 'T' || c == 'P') {
|
||||
scale_spec = c;
|
||||
++it;
|
||||
} else if (c == 'u' || c == 'U') {
|
||||
unit_pref = c;
|
||||
++it;
|
||||
} else if (c == 'b' || c == 'B') {
|
||||
base_pref = c;
|
||||
++it;
|
||||
} else if (c == 'i') {
|
||||
force_int = true;
|
||||
++it;
|
||||
} else if ('0' <= c && c <= '9') {
|
||||
// Width kept for compatibility with existing configs such as {:>9}; only
|
||||
// enforced (fixed field + '#' overflow) when a scale is forced.
|
||||
#if FMT_VERSION < 80000
|
||||
width = parse_nonnegative_int(it, end, ctx);
|
||||
width = parse_nonnegative_int(it, end, ctx);
|
||||
#else
|
||||
width = detail::parse_nonnegative_int(it, end, -1);
|
||||
width = detail::parse_nonnegative_int(it, end, -1);
|
||||
#endif
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return it;
|
||||
}
|
||||
@@ -47,44 +75,100 @@ struct formatter<pow_format> {
|
||||
template <class FormatContext>
|
||||
auto format(const pow_format& s, FormatContext& ctx) const -> decltype(ctx.out()) {
|
||||
const char* units[] = {"", "k", "M", "G", "T", "P", nullptr};
|
||||
const int max_pow = 5; // last valid index in units[]
|
||||
|
||||
auto base = s.binary_ ? 1024ull : 1000ll;
|
||||
// Effective base: 'b'/'B' override the call-site binary_.
|
||||
bool binary = base_pref == 'B' ? true : base_pref == 'b' ? false : s.binary_;
|
||||
auto base = binary ? 1024ull : 1000ll;
|
||||
auto div = 1ll;
|
||||
auto fraction = (double)s.val_;
|
||||
|
||||
int pow;
|
||||
for (pow = 0; units[pow + 1] != nullptr && fraction / base >= 1; ++pow) {
|
||||
fraction /= base;
|
||||
if (scale_spec != 0) {
|
||||
// Forced scale: map the char to a fixed index into units[].
|
||||
switch (scale_spec) {
|
||||
case 'k':
|
||||
pow = 1;
|
||||
break;
|
||||
case 'M':
|
||||
pow = 2;
|
||||
break;
|
||||
case 'G':
|
||||
pow = 3;
|
||||
break;
|
||||
case 'T':
|
||||
pow = 4;
|
||||
break;
|
||||
case 'P':
|
||||
pow = 5;
|
||||
break;
|
||||
default:
|
||||
pow = 0;
|
||||
break; // '#' -> base scale
|
||||
}
|
||||
if (pow > max_pow) pow = max_pow;
|
||||
for (int i = 0; i < pow; ++i) div *= base;
|
||||
fraction /= div;
|
||||
} else {
|
||||
for (pow = 0; units[pow + 1] != nullptr && fraction / base >= 1; ++pow) {
|
||||
fraction /= base;
|
||||
div *= base;
|
||||
}
|
||||
}
|
||||
|
||||
auto number_width = 5 // coeff in {:.1f} format
|
||||
+ s.binary_; // potential 4th digit before the decimal point
|
||||
auto max_width = number_width + 1 // prefix from units array
|
||||
+ s.binary_ // for the 'i' in GiB.
|
||||
+ s.unit_.length();
|
||||
// Precision: 'i' forces 0; otherwise 1 (or 0 when skip_decimal_ divides
|
||||
// evenly). min_pow_for_decimal_ keeps its default-branch-only effect.
|
||||
int precision = force_int ? 0 : (s.skip_decimal_ && ((s.val_ % div) == 0)) ? 0 : 1;
|
||||
if (!force_int && scale_spec == 0 && pow < s.min_pow_for_decimal_) precision = 0;
|
||||
|
||||
// Unit visibility: default on for auto scale, off for a forced scale; 'u'/'U'
|
||||
// override. The binary 'i' is part of the scale prefix, so the unit is just
|
||||
// unit_.
|
||||
bool hide_unit = unit_pref == 'u' || (unit_pref == 0 && scale_spec != 0);
|
||||
|
||||
// Scale prefix (letter + binary 'i'), suppressed entirely when a scale is
|
||||
// forced.
|
||||
std::string prefix =
|
||||
scale_spec != 0 ? "" : std::string(units[pow]) + ((binary && pow) ? "i" : "");
|
||||
std::string unit = hide_unit ? "" : s.unit_;
|
||||
|
||||
auto number_width = 3 + precision // coeff in {:.{precision}f} format
|
||||
+ (precision != 0) // float dot
|
||||
+ binary; // potential digit before the decimal point
|
||||
// In auto mode the prefix column is always reserved (letter + optional 'i'),
|
||||
// matching the historical fixed max_width even at base scale (the '=' padding
|
||||
// fills the gap). A forced scale drops the prefix column entirely.
|
||||
auto prefix_col = scale_spec != 0 ? 0 : 1 + binary;
|
||||
auto max_width = number_width + prefix_col + unit.length();
|
||||
|
||||
// The numeric coefficient string. When a scale is forced with a width and the
|
||||
// number does not fit, it overflows to '#' (spreadsheet-style).
|
||||
bool fixed_num = scale_spec != 0 && width > 0;
|
||||
std::string number = fmt::format("{:.{}f}", fraction, precision);
|
||||
if (fixed_num && (int)number.length() > width) number = std::string(width, '#');
|
||||
|
||||
// Base-scale compensation for the '=' column-align: only in auto mode, where
|
||||
// the absent prefix (and binary 'i') would otherwise shift the unit column.
|
||||
const char* padding = (scale_spec == 0 && pow == 0) ? (binary ? " " : " ") : "";
|
||||
|
||||
const char* format;
|
||||
std::string string;
|
||||
switch (spec) {
|
||||
case '>':
|
||||
return fmt::format_to(ctx.out(), "{:>{}}", fmt::format("{}", s), max_width);
|
||||
case '<':
|
||||
return fmt::format_to(ctx.out(), "{:<{}}", fmt::format("{}", s), max_width);
|
||||
case '=':
|
||||
format = "{coefficient:<{number_width}.1f}{padding}{prefix}{unit}";
|
||||
break;
|
||||
// Column-align: left-justify the coefficient within its column, then pad
|
||||
// so the prefix/unit line up across values of different magnitude.
|
||||
return fmt::format_to(ctx.out(), "{:<{}}{}{}{}", number, fixed_num ? width : number_width,
|
||||
padding, prefix, unit);
|
||||
case '>':
|
||||
case '<':
|
||||
case 0:
|
||||
default:
|
||||
format = "{coefficient:.1f}{prefix}{unit}";
|
||||
break;
|
||||
default: {
|
||||
// Right-justify the numeric field to the fixed width when forced.
|
||||
std::string body =
|
||||
(fixed_num ? fmt::format("{:>{}}", number, width) : number) + prefix + unit;
|
||||
if (spec == '>') return fmt::format_to(ctx.out(), "{:>{}}", body, max_width);
|
||||
if (spec == '<') return fmt::format_to(ctx.out(), "{:<{}}", body, max_width);
|
||||
return fmt::format_to(ctx.out(), "{}", body);
|
||||
}
|
||||
}
|
||||
return fmt::format_to(
|
||||
ctx.out(), fmt::runtime(format), fmt::arg("coefficient", fraction),
|
||||
fmt::arg("number_width", number_width),
|
||||
fmt::arg("prefix", std::string() + units[pow] + ((s.binary_ && pow) ? "i" : "")),
|
||||
fmt::arg("unit", s.unit_),
|
||||
fmt::arg("padding", pow ? ""
|
||||
: s.binary_ ? " "
|
||||
: " "));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include <giomm/dbusconnection.h>
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include "giomm/dbusproxy.h"
|
||||
|
||||
namespace waybar::util::GeoClueBackend {
|
||||
|
||||
class GeoClueBackend {
|
||||
private:
|
||||
guint watcherID_;
|
||||
sigc::connection signal_conn;
|
||||
Glib::RefPtr<Gio::DBus::Proxy> proxy;
|
||||
bool connected;
|
||||
|
||||
/* Hack to keep constructor inaccessible but still public.
|
||||
* This is required to be able to use std::make_shared.
|
||||
* It is important to keep this class only accessible via a reference-counted
|
||||
* pointer because the destructor will manually free memory, and this could be
|
||||
* a problem with C++20's copy and move semantics.
|
||||
*/
|
||||
struct PrivateConstructorTag {};
|
||||
|
||||
public:
|
||||
sigc::signal<void> in_use_changed_signal_event;
|
||||
|
||||
std::atomic<bool> location_in_use; // GeoClue is being used
|
||||
|
||||
static std::shared_ptr<GeoClueBackend> getInstance();
|
||||
|
||||
// DBus callbacks
|
||||
void onAppear(const Glib::RefPtr<Gio::DBus::Connection>&, const Glib::ustring&,
|
||||
const Glib::ustring&);
|
||||
void onVanished(const Glib::RefPtr<Gio::DBus::Connection>&, const Glib::ustring&);
|
||||
void propertyChanged(const Gio::DBus::Proxy::MapChangedProperties& changedProperties,
|
||||
const std::vector<Glib::ustring>& invalidatedProperties);
|
||||
|
||||
GeoClueBackend(PrivateConstructorTag tag);
|
||||
~GeoClueBackend();
|
||||
};
|
||||
} // namespace waybar::util::GeoClueBackend
|
||||
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <json/value.h>
|
||||
|
||||
namespace waybar::util {
|
||||
bool valid_host(const Json::Value& config);
|
||||
} // namespace waybar::util
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <codecvt>
|
||||
#include <iostream>
|
||||
#include <locale>
|
||||
#include <memory>
|
||||
#include <regex>
|
||||
|
||||
#if (FMT_VERSION >= 90000)
|
||||
@@ -26,14 +27,19 @@ class JsonParser {
|
||||
Json::Value root;
|
||||
|
||||
// replace all occurrences of "\x" with "\u00", because JSON doesn't allow "\x" escape sequences
|
||||
std::string modifiedJsonStr = replaceHexadecimalEscape(jsonStr);
|
||||
std::string modifiedJsonStr;
|
||||
const std::string* json = &jsonStr;
|
||||
if (jsonStr.find("\\x") != std::string::npos) {
|
||||
modifiedJsonStr = replaceHexadecimalEscape(jsonStr);
|
||||
json = &modifiedJsonStr;
|
||||
}
|
||||
|
||||
std::istringstream jsonStream(modifiedJsonStr);
|
||||
std::string errs;
|
||||
// Use local CharReaderBuilder for thread safety - the IPC singleton's
|
||||
// parser can be called concurrently from multiple module threads
|
||||
Json::CharReaderBuilder readerBuilder;
|
||||
if (!Json::parseFromStream(readerBuilder, jsonStream, &root, &errs)) {
|
||||
auto reader = std::unique_ptr<Json::CharReader>(readerBuilder.newCharReader());
|
||||
if (!reader->parse(json->data(), json->data() + json->size(), &root, &errs)) {
|
||||
throw std::runtime_error("Error parsing JSON: " + errs);
|
||||
}
|
||||
return root;
|
||||
|
||||
@@ -13,14 +13,14 @@ enum class KillSignalAction : std::uint8_t {
|
||||
HIDE,
|
||||
NOOP,
|
||||
};
|
||||
const std::map<std::string, KillSignalAction> userKillSignalActions = {
|
||||
inline const std::map<std::string, KillSignalAction> userKillSignalActions = {
|
||||
{"TOGGLE", KillSignalAction::TOGGLE},
|
||||
{"RELOAD", KillSignalAction::RELOAD},
|
||||
{"SHOW", KillSignalAction::SHOW},
|
||||
{"HIDE", KillSignalAction::HIDE},
|
||||
{"NOOP", KillSignalAction::NOOP}};
|
||||
|
||||
const KillSignalAction SIGNALACTION_DEFAULT_SIGUSR1 = KillSignalAction::TOGGLE;
|
||||
const KillSignalAction SIGNALACTION_DEFAULT_SIGUSR2 = KillSignalAction::RELOAD;
|
||||
inline const KillSignalAction SIGNALACTION_DEFAULT_SIGUSR1 = KillSignalAction::TOGGLE;
|
||||
inline const KillSignalAction SIGNALACTION_DEFAULT_SIGUSR2 = KillSignalAction::RELOAD;
|
||||
|
||||
}; // namespace waybar::util
|
||||
|
||||
@@ -29,7 +29,7 @@ class PipewireBackend {
|
||||
public:
|
||||
sigc::signal<void> privacy_nodes_changed_signal_event;
|
||||
|
||||
std::unordered_map<uint32_t, PrivacyNodeInfo*> privacy_nodes;
|
||||
std::unordered_map<uint32_t, PWPrivacyNodeInfo*> privacy_nodes;
|
||||
std::mutex mutex_;
|
||||
|
||||
static std::shared_ptr<PipewireBackend> getInstance();
|
||||
|
||||
@@ -12,10 +12,11 @@ enum PrivacyNodeType {
|
||||
PRIVACY_NODE_TYPE_NONE,
|
||||
PRIVACY_NODE_TYPE_VIDEO_INPUT,
|
||||
PRIVACY_NODE_TYPE_AUDIO_INPUT,
|
||||
PRIVACY_NODE_TYPE_AUDIO_OUTPUT
|
||||
PRIVACY_NODE_TYPE_AUDIO_OUTPUT,
|
||||
PRIVACY_NODE_TYPE_LOCATION
|
||||
};
|
||||
|
||||
class PrivacyNodeInfo {
|
||||
class PWPrivacyNodeInfo {
|
||||
public:
|
||||
PrivacyNodeType type = PRIVACY_NODE_TYPE_NONE;
|
||||
uint32_t id;
|
||||
|
||||
@@ -79,6 +79,12 @@ class SleeperThread {
|
||||
auto sleep_for(std::chrono::system_clock::duration dur) {
|
||||
std::unique_lock lk(mutex_);
|
||||
CancellationGuard cancel_lock;
|
||||
|
||||
condvar_.wait(lk, [this] {
|
||||
return !is_paused_ || signal_.load(std::memory_order_relaxed) ||
|
||||
!do_run_.load(std::memory_order_relaxed);
|
||||
});
|
||||
|
||||
constexpr auto max_time_point = std::chrono::steady_clock::time_point::max();
|
||||
auto wait_end = max_time_point;
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
@@ -95,6 +101,12 @@ class SleeperThread {
|
||||
time_point) {
|
||||
std::unique_lock lk(mutex_);
|
||||
CancellationGuard cancel_lock;
|
||||
|
||||
condvar_.wait(lk, [this] {
|
||||
return !is_paused_ || signal_.load(std::memory_order_relaxed) ||
|
||||
!do_run_.load(std::memory_order_relaxed);
|
||||
});
|
||||
|
||||
return condvar_.wait_until(lk, time_point, [this] {
|
||||
return signal_.load(std::memory_order_relaxed) || !do_run_.load(std::memory_order_relaxed);
|
||||
});
|
||||
@@ -122,6 +134,17 @@ class SleeperThread {
|
||||
}
|
||||
}
|
||||
|
||||
void pause() {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
is_paused_ = true;
|
||||
}
|
||||
|
||||
void resume() {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
is_paused_ = false;
|
||||
condvar_.notify_all();
|
||||
}
|
||||
|
||||
~SleeperThread() {
|
||||
connection_.disconnect();
|
||||
stop();
|
||||
@@ -137,6 +160,7 @@ class SleeperThread {
|
||||
std::atomic<bool> do_run_ = true;
|
||||
std::atomic<bool> signal_ = false;
|
||||
sigc::connection connection_;
|
||||
bool is_paused_{false};
|
||||
};
|
||||
|
||||
} // 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
|
||||
Reference in New Issue
Block a user