Merge branch 'master' of https://github.com/Alexays/Waybar into workspace-manager-implementation

This commit is contained in:
dmitry
2021-11-16 21:44:50 +03:00
132 changed files with 5444 additions and 1481 deletions

View File

@ -10,16 +10,15 @@ namespace waybar {
class ALabel : public AModule {
public:
ALabel(const Json::Value &, const std::string &, const std::string &, const std::string &format,
uint16_t interval = 0, bool ellipsize = false);
uint16_t interval = 0, bool ellipsize = false, bool enable_click = false, bool enable_scroll = false);
virtual ~ALabel() = default;
virtual auto update() -> void;
virtual std::string getIcon(uint16_t, const std::string &alt = "", uint16_t max = 0);
virtual std::string getIcon(uint16_t, std::vector<std::string> &alts, uint16_t max = 0);
virtual std::string getIcon(uint16_t, const std::vector<std::string> &alts, uint16_t max = 0);
protected:
Gtk::Label label_;
std::string format_;
std::string click_param;
const std::chrono::seconds interval_;
bool alt_ = false;
std::string default_format_;

View File

@ -7,9 +7,8 @@
#include <gtkmm/main.h>
#include <gtkmm/window.h>
#include <json/json.h>
#include "AModule.hpp"
#include "idle-inhibit-unstable-v1-client-protocol.h"
#include "wlr-layer-shell-unstable-v1-client-protocol.h"
#include "xdg-output-unstable-v1-client-protocol.h"
namespace waybar {
@ -18,65 +17,68 @@ class Factory;
struct waybar_output {
Glib::RefPtr<Gdk::Monitor> monitor;
std::string name;
std::string identifier;
std::unique_ptr<struct zxdg_output_v1, decltype(&zxdg_output_v1_destroy)> xdg_output = {
nullptr, &zxdg_output_v1_destroy};
};
enum class bar_layer : uint8_t {
BOTTOM,
TOP,
OVERLAY,
};
struct bar_margins {
int top = 0;
int right = 0;
int bottom = 0;
int left = 0;
};
class BarSurface {
protected:
BarSurface() = default;
public:
virtual void setExclusiveZone(bool enable) = 0;
virtual void setLayer(bar_layer layer) = 0;
virtual void setMargins(const struct bar_margins &margins) = 0;
virtual void setPassThrough(bool enable) = 0;
virtual void setPosition(const std::string_view &position) = 0;
virtual void setSize(uint32_t width, uint32_t height) = 0;
virtual void commit(){};
virtual ~BarSurface() = default;
};
class Bar {
public:
Bar(struct waybar_output *w_output, const Json::Value &);
Bar(const Bar &) = delete;
~Bar() = default;
auto toggle() -> void;
void setVisible(bool visible);
void toggle();
void handleSignal(int);
struct waybar_output *output;
Json::Value config;
struct wl_surface * surface;
bool exclusive = true;
bool visible = true;
bool vertical = false;
Gtk::Window window;
private:
static constexpr const char *MIN_HEIGHT_MSG =
"Requested height: {} exceeds the minimum height: {} required by the modules";
static constexpr const char *MIN_WIDTH_MSG =
"Requested width: {} exceeds the minimum width: {} required by the modules";
static constexpr const char *BAR_SIZE_MSG =
"Bar configured (width: {}, height: {}) for output: {}";
static constexpr const char *SIZE_DEFINED =
"{} size is defined in the config file so it will stay like that";
static void layerSurfaceHandleConfigure(void *, struct zwlr_layer_surface_v1 *, uint32_t,
uint32_t, uint32_t);
static void layerSurfaceHandleClosed(void *, struct zwlr_layer_surface_v1 *);
#ifdef HAVE_GTK_LAYER_SHELL
void initGtkLayerShell();
#endif
void onConfigure(GdkEventConfigure *ev);
void onRealize();
void onMap(GdkEventAny *ev);
void setExclusiveZone(uint32_t width, uint32_t height);
void setSurfaceSize(uint32_t width, uint32_t height);
void onMap(GdkEventAny *);
auto setupWidgets() -> void;
void getModules(const Factory &, const std::string &);
void setupAltFormatKeyForModule(const std::string &module_name);
void setupAltFormatKeyForModuleList(const char *module_list_name);
struct margins {
int top = 0;
int right = 0;
int bottom = 0;
int left = 0;
} margins_;
struct zwlr_layer_surface_v1 *layer_surface_;
// use gtk-layer-shell instead of handling layer surfaces directly
bool use_gls_ = false;
uint32_t width_ = 0;
uint32_t height_ = 1;
uint8_t anchor_;
std::unique_ptr<BarSurface> surface_impl_;
bar_layer layer_;
Gtk::Box left_;
Gtk::Box center_;
Gtk::Box right_;

View File

@ -3,10 +3,14 @@
#include <fmt/format.h>
#include <gdk/gdk.h>
#include <gdk/gdkwayland.h>
#include <unistd.h>
#include <wayland-client.h>
#include <wordexp.h>
#include "bar.hpp"
#include "config.hpp"
struct zwlr_layer_shell_v1;
struct zwp_idle_inhibitor_v1;
struct zwp_idle_inhibit_manager_v1;
namespace waybar {
@ -14,6 +18,7 @@ class Client {
public:
static Client *inst();
int main(int argc, char *argv[]);
void reset();
Glib::RefPtr<Gtk::Application> gtk_app;
Glib::RefPtr<Gdk::Display> gdk_display;
@ -23,28 +28,27 @@ class Client {
struct zxdg_output_manager_v1 * xdg_output_manager = nullptr;
struct zwp_idle_inhibit_manager_v1 *idle_inhibit_manager = nullptr;
std::vector<std::unique_ptr<Bar>> bars;
Config config;
private:
Client() = default;
std::tuple<const std::string, const std::string> getConfigs(const std::string &config,
const std::string &style) const;
void bindInterfaces();
const std::string getValidPath(const std::vector<std::string> &paths) const;
void handleOutput(struct waybar_output &output);
bool isValidOutput(const Json::Value &config, struct waybar_output &output);
auto setupConfig(const std::string &config_file) -> void;
auto setupCss(const std::string &css_file) -> void;
struct waybar_output &getOutput(void *);
const std::string getStyle(const std::string &style);
void bindInterfaces();
void handleOutput(struct waybar_output &output);
auto setupCss(const std::string &css_file) -> void;
struct waybar_output & getOutput(void *);
std::vector<Json::Value> getOutputConfigs(struct waybar_output &output);
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 handleOutputDone(void *, struct zxdg_output_v1 *);
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);
void handleMonitorRemoved(Glib::RefPtr<Gdk::Monitor> monitor);
void handleDeferredMonitorRemoval(Glib::RefPtr<Gdk::Monitor> monitor);
Json::Value config_;
Glib::RefPtr<Gtk::StyleContext> style_context_;
Glib::RefPtr<Gtk::CssProvider> css_provider_;
std::list<struct waybar_output> outputs_;

39
include/config.hpp Normal file
View File

@ -0,0 +1,39 @@
#pragma once
#include <json/json.h>
#include <optional>
#include <string>
#ifndef SYSCONFDIR
#define SYSCONFDIR "/etc"
#endif
namespace waybar {
class Config {
public:
static const std::vector<std::string> CONFIG_DIRS;
/* Try to find any of provided names in the supported set of config directories */
static std::optional<std::string> findConfigPath(
const std::vector<std::string> &names, const std::vector<std::string> &dirs = CONFIG_DIRS);
Config() = default;
void load(const std::string &config);
Json::Value &getConfig() { return config_; }
std::vector<Json::Value> getOutputConfigs(const std::string &name, const std::string &identifier);
private:
void setupConfig(Json::Value &dst, const std::string &config_file, int depth);
void resolveConfigIncludes(Json::Value &config, int depth);
void mergeConfig(Json::Value &a_config_, Json::Value &b_config_);
std::string config_file_;
Json::Value config_;
};
} // namespace waybar

View File

@ -1,11 +1,16 @@
#pragma once
#include <json/json.h>
#ifdef HAVE_LIBDATE
#include "modules/clock.hpp"
#else
#include "modules/simpleclock.hpp"
#endif
#ifdef HAVE_SWAY
#include "modules/sway/mode.hpp"
#include "modules/sway/window.hpp"
#include "modules/sway/workspaces.hpp"
#include "modules/sway/language.hpp"
#endif
#ifdef HAVE_WLR
#include "modules/wlr/taskbar.hpp"
@ -34,17 +39,25 @@
#ifdef HAVE_LIBUDEV
#include "modules/backlight.hpp"
#endif
#ifdef HAVE_LIBEVDEV
#include "modules/keyboard_state.hpp"
#endif
#ifdef HAVE_LIBPULSE
#include "modules/pulseaudio.hpp"
#endif
#ifdef HAVE_LIBMPDCLIENT
#include "modules/mpd.hpp"
#include "modules/mpd/mpd.hpp"
#endif
#ifdef HAVE_LIBSNDIO
#include "modules/sndio.hpp"
#endif
#include "bar.hpp"
#include "modules/custom.hpp"
#include "modules/temperature.hpp"
#if defined(__linux__)
#include "modules/bluetooth.hpp"
# ifdef WANT_RFKILL
# include "modules/bluetooth.hpp"
# endif
#endif
namespace waybar {

View File

@ -31,19 +31,22 @@ class Battery : public ALabel {
private:
static inline const fs::path data_dir_ = "/sys/class/power_supply/";
void getBatteries();
void worker();
const std::string getAdapterStatus(uint8_t capacity) const;
const std::tuple<uint8_t, float, std::string> getInfos() const;
const std::string formatTimeRemaining(float hoursRemaining);
void refreshBatteries();
void worker();
const std::string getAdapterStatus(uint8_t capacity) const;
const std::tuple<uint8_t, float, std::string, float> getInfos();
const std::string formatTimeRemaining(float hoursRemaining);
std::vector<fs::path> batteries_;
int global_watch;
std::map<fs::path,int> batteries_;
fs::path adapter_;
int fd_;
std::vector<int> wds_;
int battery_watch_fd_;
int global_watch_fd_;
std::mutex battery_list_mutex_;
std::string old_status_;
util::SleeperThread thread_;
util::SleeperThread thread_battery_update_;
util::SleeperThread thread_timer_;
};

View File

@ -1,10 +1,6 @@
#pragma once
#include <fmt/format.h>
#include "ALabel.hpp"
#include <fmt/chrono.h>
#include "util/sleeper_thread.hpp"
#include "util/rfkill.hpp"
namespace waybar::modules {
@ -16,10 +12,6 @@ class Bluetooth : public ALabel {
auto update() -> void;
private:
std::string status_;
util::SleeperThread thread_;
util::SleeperThread intervall_thread_;
util::Rfkill rfkill_;
};

View File

@ -28,12 +28,16 @@ class Clock : public ALabel {
std::locale locale_;
const date::time_zone* time_zone_;
bool fixed_time_zone_;
date::year_month_day cached_calendar_ymd_;
int time_zone_idx_;
date::year_month_day cached_calendar_ymd_ = date::January/1/0;
std::string cached_calendar_text_;
bool handleScroll(GdkEventScroll* e);
auto calendar_text(const waybar_time& wtime) -> std::string;
auto weekdays_header(const date::weekday& first_dow, std::ostream& os) -> void;
auto first_day_of_week() -> date::weekday;
bool setTimeZone(Json::Value zone_name);
};
} // namespace waybar::modules

View File

@ -1,7 +1,6 @@
#pragma once
#include <fmt/format.h>
#include <unistd.h>
#include <cstdint>
#include <fstream>
#include <numeric>
@ -20,9 +19,11 @@ class Cpu : public ALabel {
auto update() -> void;
private:
uint16_t getCpuLoad();
std::tuple<uint16_t, std::string> getCpuUsage();
std::vector<std::tuple<size_t, size_t>> parseCpuinfo();
double getCpuLoad();
std::tuple<std::vector<uint16_t>, std::string> getCpuUsage();
std::tuple<float, float, float> getCpuFrequency();
std::vector<std::tuple<size_t, size_t>> parseCpuinfo();
std::vector<float> parseCpuFrequencies();
std::vector<std::tuple<size_t, size_t>> prev_times_;

View File

@ -22,6 +22,7 @@ class Custom : public ALabel {
void continuousWorker();
void parseOutputRaw();
void parseOutputJson();
void handleEvent();
bool handleScroll(GdkEventScroll* e);
bool handleToggle(GdkEventButton* const& e);

View File

@ -12,12 +12,13 @@ class IdleInhibitor : public ALabel {
IdleInhibitor(const std::string&, const waybar::Bar&, const Json::Value&);
~IdleInhibitor();
auto update() -> void;
static std::list<waybar::AModule*> modules;
static bool status;
private:
bool handleToggle(GdkEventButton* const& e);
const Bar& bar_;
std::string status_;
struct zwp_idle_inhibitor_v1* idle_inhibitor_;
int pid_;
};

View File

@ -0,0 +1,47 @@
#pragma once
#include <fmt/format.h>
#if FMT_VERSION < 60000
#include <fmt/time.h>
#else
#include <fmt/chrono.h>
#endif
#include "AModule.hpp"
#include "bar.hpp"
#include "util/sleeper_thread.hpp"
#include <gtkmm/label.h>
extern "C" {
#include <libevdev/libevdev.h>
}
namespace waybar::modules {
class KeyboardState : public AModule {
public:
KeyboardState(const std::string&, const waybar::Bar&, const Json::Value&);
~KeyboardState();
auto update() -> void;
private:
static auto openDevice(const std::string&) -> std::pair<int, libevdev*>;
Gtk::Box box_;
Gtk::Label numlock_label_;
Gtk::Label capslock_label_;
Gtk::Label scrolllock_label_;
std::string numlock_format_;
std::string capslock_format_;
std::string scrolllock_format_;
const std::chrono::seconds interval_;
std::string icon_locked_;
std::string icon_unlocked_;
int fd_;
libevdev* dev_;
util::SleeperThread thread_;
};
} // namespace waybar::modules

View File

@ -1,74 +0,0 @@
#pragma once
#include <fmt/format.h>
#include <mpd/client.h>
#include <condition_variable>
#include <thread>
#include "ALabel.hpp"
namespace waybar::modules {
class MPD : public ALabel {
public:
MPD(const std::string&, const Json::Value&);
auto update() -> void;
private:
std::thread periodic_updater();
std::string getTag(mpd_tag_type type, unsigned idx = 0);
void setLabel();
std::string getStateIcon();
std::string getOptionIcon(std::string optionName, bool activated);
std::thread event_listener();
// Assumes `connection_lock_` is locked
void tryConnect();
// If checking errors on the main connection, make sure to lock it using
// `connection_lock_` before calling checkErrors
void checkErrors(mpd_connection* conn);
// Assumes `connection_lock_` is locked
void fetchState();
void waitForEvent();
bool handlePlayPause(GdkEventButton* const&);
bool stopped();
bool playing();
bool paused();
const std::string module_name_;
using unique_connection = std::unique_ptr<mpd_connection, decltype(&mpd_connection_free)>;
using unique_status = std::unique_ptr<mpd_status, decltype(&mpd_status_free)>;
using unique_song = std::unique_ptr<mpd_song, decltype(&mpd_song_free)>;
// Not using unique_ptr since we don't manage the pointer
// (It's either nullptr, or from the config)
const char* server_;
const unsigned port_;
unsigned timeout_;
// We need a mutex here because we can trigger updates from multiple thread:
// the event based updates, the periodic updates needed for the elapsed time,
// and the click play/pause feature
std::mutex connection_lock_;
unique_connection connection_;
// The alternate connection will be used to wait for events: since it will
// be blocking (idle) we can't send commands via this connection
//
// No lock since only used in the event listener thread
unique_connection alternate_connection_;
// Protect them using the `connection_lock_`
unique_status status_;
mpd_state state_;
unique_song song_;
// To make sure the previous periodic_updater stops before creating a new one
std::mutex periodic_lock_;
};
} // namespace waybar::modules

View File

@ -0,0 +1,67 @@
#pragma once
#include <fmt/format.h>
#include <mpd/client.h>
#include <spdlog/spdlog.h>
#include <condition_variable>
#include <thread>
#include "ALabel.hpp"
#include "modules/mpd/state.hpp"
namespace waybar::modules {
class MPD : public ALabel {
friend class detail::Context;
// State machine
detail::Context context_{this};
const std::string module_name_;
// Not using unique_ptr since we don't manage the pointer
// (It's either nullptr, or from the config)
const char* server_;
const unsigned port_;
const std::string password_;
unsigned timeout_;
detail::unique_connection connection_;
detail::unique_status status_;
mpd_state state_;
detail::unique_song song_;
public:
MPD(const std::string&, const Json::Value&);
virtual ~MPD() noexcept = default;
auto update() -> void;
private:
std::string getTag(mpd_tag_type type, unsigned idx = 0) const;
void setLabel();
std::string getStateIcon() const;
std::string getOptionIcon(std::string optionName, bool activated) const;
// GUI-side methods
bool handlePlayPause(GdkEventButton* const&);
void emit() { dp.emit(); }
// MPD-side, Non-GUI methods.
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; }
};
#if !defined(MPD_NOINLINE)
#include "modules/mpd/state.inl.hpp"
#endif
} // namespace waybar::modules

View File

@ -0,0 +1,217 @@
#pragma once
#include <mpd/client.h>
#include <fmt/format.h>
#include <spdlog/spdlog.h>
#include <condition_variable>
#include <thread>
#include "ALabel.hpp"
namespace waybar::modules {
class MPD;
} // namespace waybar::modules
namespace waybar::modules::detail {
using unique_connection = std::unique_ptr<mpd_connection, decltype(&mpd_connection_free)>;
using unique_status = std::unique_ptr<mpd_status, decltype(&mpd_status_free)>;
using unique_song = std::unique_ptr<mpd_song, decltype(&mpd_song_free)>;
class Context;
/// This state machine loosely follows a non-hierarchical, statechart
/// pattern, and includes ENTRY and EXIT actions.
///
/// The State class is the base class for all other states. The
/// entry and exit methods are automatically called when entering
/// into a new state and exiting from the current state. This
/// includes initially entering (Disconnected class) and exiting
/// Waybar.
///
/// The following nested "top-level" states are represented:
/// 1. Idle - await notification of MPD activity.
/// 2. All Non-Idle states:
/// 1. Playing - An active song is producing audio output.
/// 2. Paused - The current song is paused.
/// 3. Stopped - No song is actively playing.
/// 3. Disconnected - periodically attempt MPD (re-)connection.
///
/// NOTE: Since this statechart is non-hierarchical, the above
/// states are flattened into a set.
class State {
public:
virtual ~State() noexcept = default;
virtual void entry() noexcept { spdlog::debug("mpd: ignore entry action"); }
virtual void exit() noexcept { spdlog::debug("mpd: ignore exit action"); }
virtual void play() { spdlog::debug("mpd: ignore play state transition"); }
virtual void stop() { spdlog::debug("mpd: ignore stop state transition"); }
virtual void pause() { spdlog::debug("mpd: ignore pause state transition"); }
/// Request state update the GUI.
virtual void update() noexcept { spdlog::debug("mpd: ignoring update method request"); }
};
class Idle : public State {
Context* const ctx_;
sigc::connection idle_connection_;
public:
Idle(Context* const ctx) : ctx_{ctx} {}
virtual ~Idle() noexcept { this->exit(); };
void entry() noexcept override;
void exit() noexcept override;
void play() override;
void stop() override;
void pause() override;
void update() noexcept override;
private:
Idle(const Idle&) = delete;
Idle& operator=(const Idle&) = delete;
bool on_io(Glib::IOCondition const&);
};
class Playing : public State {
Context* const ctx_;
sigc::connection timer_connection_;
public:
Playing(Context* const ctx) : ctx_{ctx} {}
virtual ~Playing() noexcept { this->exit(); }
void entry() noexcept override;
void exit() noexcept override;
void pause() override;
void stop() override;
void update() noexcept override;
private:
Playing(Playing const&) = delete;
Playing& operator=(Playing const&) = delete;
bool on_timer();
};
class Paused : public State {
Context* const ctx_;
sigc::connection timer_connection_;
public:
Paused(Context* const ctx) : ctx_{ctx} {}
virtual ~Paused() noexcept { this->exit(); }
void entry() noexcept override;
void exit() noexcept override;
void play() override;
void stop() override;
void update() noexcept override;
private:
Paused(Paused const&) = delete;
Paused& operator=(Paused const&) = delete;
bool on_timer();
};
class Stopped : public State {
Context* const ctx_;
sigc::connection timer_connection_;
public:
Stopped(Context* const ctx) : ctx_{ctx} {}
virtual ~Stopped() noexcept { this->exit(); }
void entry() noexcept override;
void exit() noexcept override;
void play() override;
void pause() override;
void update() noexcept override;
private:
Stopped(Stopped const&) = delete;
Stopped& operator=(Stopped const&) = delete;
bool on_timer();
};
class Disconnected : public State {
Context* const ctx_;
sigc::connection timer_connection_;
public:
Disconnected(Context* const ctx) : ctx_{ctx} {}
virtual ~Disconnected() noexcept { this->exit(); }
void entry() noexcept override;
void exit() noexcept override;
void update() noexcept override;
private:
Disconnected(Disconnected const&) = delete;
Disconnected& operator=(Disconnected const&) = delete;
void arm_timer(int interval) noexcept;
void disarm_timer() noexcept;
bool on_timer();
};
class Context {
std::unique_ptr<State> state_;
waybar::modules::MPD* mpd_module_;
friend class State;
friend class Playing;
friend class Paused;
friend class Stopped;
friend class Disconnected;
friend class Idle;
protected:
void setState(std::unique_ptr<State>&& new_state) noexcept {
if (state_.get() != nullptr) {
state_->exit();
}
state_ = std::move(new_state);
state_->entry();
}
bool is_connected() const;
bool is_playing() const;
bool is_paused() const;
bool is_stopped() const;
constexpr std::size_t 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;
[[nodiscard]] unique_connection& connection();
public:
explicit Context(waybar::modules::MPD* const mpd_module)
: state_{std::make_unique<Disconnected>(this)}, mpd_module_{mpd_module} {
state_->entry();
}
void play() { state_->play(); }
void stop() { state_->stop(); }
void pause() { state_->pause(); }
void update() noexcept { state_->update(); }
};
} // namespace waybar::modules::detail

View File

@ -0,0 +1,24 @@
#pragma once
namespace detail {
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(); }
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_; }
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(); }
} // namespace detail

View File

@ -2,16 +2,16 @@
#include <arpa/inet.h>
#include <fmt/format.h>
#include <ifaddrs.h>
#include <linux/nl80211.h>
#include <net/if.h>
#include <netlink/genl/ctrl.h>
#include <netlink/genl/genl.h>
#include <netlink/netlink.h>
#include <sys/epoll.h>
#include "ALabel.hpp"
#include "util/sleeper_thread.hpp"
#ifdef WANT_RFKILL
#include "util/rfkill.hpp"
#endif
namespace waybar::modules {
@ -26,23 +26,20 @@ class Network : public ALabel {
static const uint8_t EPOLL_MAX = 200;
static int handleEvents(struct nl_msg*, void*);
static int handleEventsDone(struct nl_msg*, void*);
static int handleScan(struct nl_msg*, void*);
void askForStateDump(void);
void worker();
void createInfoSocket();
void createEventSocket();
int getExternalInterface(int skip_idx = -1) const;
void getInterfaceAddress();
int netlinkRequest(void*, uint32_t, uint32_t groups = 0) const;
int netlinkResponse(void*, uint32_t, uint32_t groups = 0) const;
void parseEssid(struct nlattr**);
void parseSignal(struct nlattr**);
void parseFreq(struct nlattr**);
bool associatedOrJoined(struct nlattr**);
bool checkInterface(struct ifinfomsg* rtif, std::string name);
int getPreferredIface(int skip_idx = -1, bool wait = true) const;
bool checkInterface(std::string name);
auto getInfo() -> void;
void checkNewInterface(struct ifinfomsg* rtif);
const std::string getNetworkState() const;
void clearIface();
bool wildcardMatch(const std::string& pattern, const std::string& text) const;
@ -52,27 +49,37 @@ class Network : public ALabel {
struct sockaddr_nl nladdr_ = {0};
struct nl_sock* sock_ = nullptr;
struct nl_sock* ev_sock_ = nullptr;
int efd_;
int ev_fd_;
int nl80211_id_;
std::mutex mutex_;
bool want_route_dump_;
bool want_link_dump_;
bool want_addr_dump_;
bool dump_in_progress_;
unsigned long long bandwidth_down_total_;
unsigned long long bandwidth_up_total_;
std::string state_;
std::string essid_;
bool carrier_;
std::string ifname_;
std::string ipaddr_;
std::string gwaddr_;
std::string netmask_;
int cidr_;
int32_t signal_strength_dbm_;
uint8_t signal_strength_;
uint32_t frequency_;
uint32_t route_priority;
util::SleeperThread thread_;
util::SleeperThread thread_timer_;
util::SleeperThread thread_rfkill_;
#ifdef WANT_RFKILL
util::Rfkill rfkill_;
#endif
};
} // namespace waybar::modules

View File

@ -24,7 +24,7 @@ class Pulseaudio : public ALabel {
static void volumeModifyCb(pa_context*, int, void*);
bool handleScroll(GdkEventScroll* e);
const std::string getPortIcon() const;
const std::vector<std::string> getPulseIcon() const;
pa_threaded_mainloop* mainloop_;
pa_mainloop_api* mainloop_api_;
@ -38,7 +38,8 @@ class Pulseaudio : public ALabel {
std::string form_factor_;
std::string desc_;
std::string monitor_;
std::string default_sink_name_;
std::string current_sink_name_;
bool current_sink_running_;
// SOURCE
uint32_t source_idx_{0};
uint16_t source_volume_;

View File

@ -18,6 +18,7 @@ class Tags : public waybar::AModule {
// Handlers for wayland events
void handle_focused_tags(uint32_t tags);
void handle_view_tags(struct wl_array *tags);
void handle_urgent_tags(uint32_t tags);
struct zriver_status_manager_v1 *status_manager_;

View File

@ -0,0 +1,24 @@
#pragma once
#include <fmt/format.h>
#if FMT_VERSION < 60000
#include <fmt/time.h>
#else
#include <fmt/chrono.h>
#endif
#include "ALabel.hpp"
#include "util/sleeper_thread.hpp"
namespace waybar::modules {
class Clock : public ALabel {
public:
Clock(const std::string&, const Json::Value&);
~Clock() = default;
auto update() -> void;
private:
util::SleeperThread thread_;
};
} // namespace waybar::modules

30
include/modules/sndio.hpp Normal file
View File

@ -0,0 +1,30 @@
#pragma once
#include <sndio.h>
#include <vector>
#include "ALabel.hpp"
#include "util/sleeper_thread.hpp"
namespace waybar::modules {
class Sndio : public ALabel {
public:
Sndio(const std::string&, const Json::Value&);
~Sndio();
auto update() -> void;
auto set_desc(struct sioctl_desc *, unsigned int) -> void;
auto put_val(unsigned int, unsigned int) -> void;
bool handleScroll(GdkEventScroll *);
bool handleToggle(GdkEventButton* const&);
private:
auto connect_to_sndio() -> void;
util::SleeperThread thread_;
struct sioctl_hdl *hdl_;
std::vector<struct pollfd> pfds_;
unsigned int addr_;
unsigned int volume_, old_volume_, maxval_;
bool muted_;
};
} // namespace waybar::modules

View File

@ -5,13 +5,15 @@
#include <glibmm/refptr.h>
#include <json/json.h>
#include <tuple>
#include "bar.hpp"
#include "modules/sni/item.hpp"
namespace waybar::modules::SNI {
class Host {
public:
Host(const std::size_t id, const Json::Value&, const std::function<void(std::unique_ptr<Item>&)>&,
Host(const std::size_t id, const Json::Value&, const Bar&,
const std::function<void(std::unique_ptr<Item>&)>&,
const std::function<void(std::unique_ptr<Item>&)>&);
~Host();
@ -36,6 +38,7 @@ class Host {
GCancellable* cancellable_ = nullptr;
SnWatcher* watcher_ = nullptr;
const Json::Value& config_;
const Bar& bar_;
const std::function<void(std::unique_ptr<Item>&)> on_add_;
const std::function<void(std::unique_ptr<Item>&)> on_remove_;
};

View File

@ -11,11 +11,21 @@
#include <libdbusmenu-gtk/dbusmenu-gtk.h>
#include <sigc++/trackable.h>
#include <set>
#include <string_view>
#include "bar.hpp"
namespace waybar::modules::SNI {
struct ToolTip {
Glib::ustring icon_name;
Glib::ustring text;
};
class Item : public sigc::trackable {
public:
Item(const std::string&, const std::string&, const Json::Value&);
Item(const std::string&, const std::string&, const Json::Value&, const Bar&);
~Item() = default;
std::string bus_name;
@ -27,10 +37,8 @@ class Item : public sigc::trackable {
Gtk::EventBox event_box;
std::string category;
std::string id;
std::string status;
std::string title;
int32_t window_id;
std::string icon_name;
Glib::RefPtr<Gdk::Pixbuf> icon_pixmap;
Glib::RefPtr<Gtk::IconTheme> icon_theme;
@ -39,6 +47,7 @@ class Item : public sigc::trackable {
std::string attention_movie_name;
std::string icon_theme_path;
std::string menu;
ToolTip tooltip;
DbusmenuGtkMenu* dbus_menu = nullptr;
Gtk::Menu* gtk_menu = nullptr;
/**
@ -49,8 +58,10 @@ class Item : public sigc::trackable {
bool item_is_menu = true;
private:
void onConfigure(GdkEventConfigure* ev);
void proxyReady(Glib::RefPtr<Gio::AsyncResult>& result);
void setProperty(const Glib::ustring& name, Glib::VariantBase& value);
void setStatus(const Glib::ustring& value);
void getUpdatedProperties();
void processUpdatedProperties(Glib::RefPtr<Gio::AsyncResult>& result);
void onSignal(const Glib::ustring& sender_name, const Glib::ustring& signal_name,
@ -58,14 +69,24 @@ class Item : public sigc::trackable {
void updateImage();
Glib::RefPtr<Gdk::Pixbuf> extractPixBuf(GVariant* variant);
Glib::RefPtr<Gdk::Pixbuf> getIconPixbuf();
Glib::RefPtr<Gdk::Pixbuf> getIconByName(const std::string& name, int size);
double getScaledIconSize();
static void onMenuDestroyed(Item* self, GObject* old_menu_pointer);
void makeMenu();
bool handleClick(GdkEventButton* const& /*ev*/);
bool handleScroll(GdkEventScroll* const&);
// smooth scrolling threshold
gdouble scroll_threshold_ = 0;
gdouble distance_scrolled_x_ = 0;
gdouble distance_scrolled_y_ = 0;
// visibility of items with Status == Passive
bool show_passive_ = false;
Glib::RefPtr<Gio::DBus::Proxy> proxy_;
Glib::RefPtr<Gio::Cancellable> cancellable_;
bool update_pending_;
std::set<std::string_view> update_pending_;
};
} // namespace waybar::modules::SNI

View File

@ -29,4 +29,8 @@ enum ipc_command_type {
IPC_EVENT_BINDING = ((1 << 31) | 5),
IPC_EVENT_SHUTDOWN = ((1 << 31) | 6),
IPC_EVENT_TICK = ((1 << 31) | 7),
// sway-specific event types
IPC_EVENT_BAR_STATE_UPDATE = ((1<<31) | 20),
IPC_EVENT_INPUT = ((1<<31) | 21),
};

View File

@ -0,0 +1,69 @@
#pragma once
#include <fmt/format.h>
#include <xkbcommon/xkbregistry.h>
#include <map>
#include <string>
#include "ALabel.hpp"
#include "bar.hpp"
#include "client.hpp"
#include "modules/sway/ipc/client.hpp"
#include "util/json.hpp"
namespace waybar::modules::sway {
class Language : public ALabel, public sigc::trackable {
public:
Language(const std::string& id, const Json::Value& config);
~Language() = default;
auto update() -> void;
private:
enum class DispayedShortFlag {
None = 0,
ShortName = 1,
ShortDescription = 1 << 1
};
struct Layout {
std::string full_name;
std::string short_name;
std::string variant;
std::string short_description;
};
class XKBContext {
public:
XKBContext();
~XKBContext();
auto next_layout() -> Layout*;
private:
rxkb_context* context_ = nullptr;
rxkb_layout* xkb_layout_ = nullptr;
Layout* layout_ = nullptr;
std::map<std::string, rxkb_layout*> base_layouts_by_name_;
};
void onEvent(const struct Ipc::ipc_response&);
void onCmd(const struct Ipc::ipc_response&);
auto set_current_layout(std::string current_layout) -> void;
auto init_layouts_map(const std::vector<std::string>& used_layouts) -> void;
const static std::string XKB_LAYOUT_NAMES_KEY;
const static std::string XKB_ACTIVE_LAYOUT_NAME_KEY;
Layout layout_;
std::string tooltip_format_ = "";
std::map<std::string, Layout> layouts_map_;
bool is_variant_displayed;
std::byte displayed_short_flag = static_cast<std::byte>(DispayedShortFlag::None);
util::JsonParser parser_;
std::mutex mutex_;
Ipc ipc_;
};
} // namespace waybar::modules::sway

View File

@ -22,6 +22,7 @@ class Window : public ALabel, public sigc::trackable {
std::tuple<std::size_t, int, std::string, std::string> getFocusedNode(const Json::Value& nodes,
std::string& output);
void getTree();
std::string rewriteTitle(const std::string& title);
const Bar& bar_;
std::string window_;

View File

@ -20,7 +20,7 @@ class Workspaces : public AModule, public sigc::trackable {
auto update() -> void;
private:
static inline const std::string workspace_switch_cmd_ = "workspace --no-auto-back-and-forth \"{}\"";
static inline const std::string workspace_switch_cmd_ = "workspace {} \"{}\"";
static int convertWorkspaceNameToNum(std::string name);

View File

@ -8,6 +8,7 @@
#include <memory>
#include <string>
#include <vector>
#include <unordered_set>
#include <gdk/gdk.h>
@ -61,6 +62,7 @@ class Task
Gtk::Label text_before_;
Gtk::Label text_after_;
bool button_visible_;
bool ignored_;
bool with_icon_;
std::string format_before_;
@ -70,7 +72,7 @@ class Task
std::string title_;
std::string app_id_;
uint32_t state_;
uint32_t state_ = 0;
private:
std::string repr() const;
@ -132,6 +134,7 @@ class Taskbar : public waybar::AModule
std::vector<TaskPtr> tasks_;
std::vector<Glib::RefPtr<Gtk::IconTheme>> icon_themes_;
std::unordered_set<std::string> ignore_list_;
struct zwlr_foreign_toplevel_manager_v1 *manager_;
struct wl_seat *seat_;
@ -155,6 +158,7 @@ class Taskbar : public waybar::AModule
bool all_outputs() const;
std::vector<Glib::RefPtr<Gtk::IconTheme>> icon_themes() const;
const std::unordered_set<std::string>& ignore_list() const;
};
} /* namespace waybar::modules::wlr */

View File

@ -5,8 +5,18 @@
#include <sys/wait.h>
#include <unistd.h>
#ifdef __linux__
#include <sys/prctl.h>
#endif
#ifdef __FreeBSD__
#include <sys/procctl.h>
#endif
#include <array>
extern std::mutex reap_mtx;
extern std::list<pid_t> reap;
namespace waybar::util::command {
struct res {
@ -32,10 +42,11 @@ inline std::string read(FILE* fp) {
inline int close(FILE* fp, pid_t pid) {
int stat = -1;
pid_t ret;
fclose(fp);
do {
waitpid(pid, &stat, WCONTINUED | WUNTRACED);
ret = waitpid(pid, &stat, WCONTINUED | WUNTRACED);
if (WIFEXITED(stat)) {
spdlog::debug("Cmd exited with code {}", WEXITSTATUS(stat));
@ -45,6 +56,8 @@ inline int close(FILE* fp, pid_t pid) {
spdlog::debug("Cmd stopped by {}", WSTOPSIG(stat));
} else if (WIFCONTINUED(stat)) {
spdlog::debug("Cmd continued");
} else if (ret == -1) {
spdlog::debug("waitpid failed: {}", strerror(errno));
} else {
break;
}
@ -65,6 +78,24 @@ inline FILE* open(const std::string& cmd, int& pid) {
}
if (!child_pid) {
int err;
sigset_t mask;
sigfillset(&mask);
// Reset sigmask
err = pthread_sigmask(SIG_UNBLOCK, &mask, nullptr);
if (err != 0) spdlog::error("pthread_sigmask in open failed: {}", strerror(err));
// Kill child if Waybar exits
int deathsig = SIGTERM;
#ifdef __linux__
if (prctl(PR_SET_PDEATHSIG, deathsig) != 0) {
spdlog::error("prctl(PR_SET_PDEATHSIG) in open failed: {}", strerror(errno));
}
#endif
#ifdef __FreeBSD__
if (procctl(P_PID, 0, PROC_PDEATHSIG_CTL, reinterpret_cast<void*>(&deathsig)) == -1) {
spdlog::error("procctl(PROC_PDEATHSIG_CTL) in open failed: {}", strerror(errno));
}
#endif
::close(fd[0]);
dup2(fd[1], 1);
setpgid(child_pid, child_pid);
@ -97,7 +128,7 @@ inline struct res execNoRead(const std::string& cmd) {
inline int32_t forkExec(const std::string& cmd) {
if (cmd == "") return -1;
int32_t pid = fork();
pid_t pid = fork();
if (pid < 0) {
spdlog::error("Unable to exec cmd {}, error {}", cmd.c_str(), strerror(errno));
@ -106,12 +137,20 @@ inline int32_t forkExec(const std::string& cmd) {
// Child executes the command
if (!pid) {
int err;
sigset_t mask;
sigfillset(&mask);
// Reset sigmask
err = pthread_sigmask(SIG_UNBLOCK, &mask, nullptr);
if (err != 0) spdlog::error("pthread_sigmask in forkExec failed: {}", strerror(err));
setpgid(pid, pid);
signal(SIGCHLD, SIG_DFL);
execl("/bin/sh", "sh", "-c", cmd.c_str(), (char*)0);
exit(0);
} else {
signal(SIGCHLD, SIG_IGN);
reap_mtx.lock();
reap.push_back(pid);
reap_mtx.unlock();
spdlog::debug("Added child to reap list: {}", pid);
}
return pid;

View File

@ -23,7 +23,7 @@ namespace fmt {
constexpr auto parse(ParseContext& ctx) -> decltype (ctx.begin()) {
auto it = ctx.begin(), end = ctx.end();
if (it != end && *it == ':') ++it;
if (*it == '>' || *it == '<' || *it == '=') {
if (it && (*it == '>' || *it == '<' || *it == '=')) {
spec = *it;
++it;
}
@ -35,7 +35,11 @@ namespace fmt {
// 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.
#if FMT_VERSION < 80000
width = parse_nonnegative_int(it, end, ctx);
#else
width = detail::parse_nonnegative_int(it, end, -1);
#endif
}
return it;
}

View File

@ -1,19 +1,26 @@
#pragma once
#include <glibmm/iochannel.h>
#include <linux/rfkill.h>
#include <sigc++/signal.h>
#include <sigc++/trackable.h>
namespace waybar::util {
class Rfkill {
class Rfkill : public sigc::trackable {
public:
Rfkill(enum rfkill_type rfkill_type);
~Rfkill() = default;
void waitForEvent();
~Rfkill();
bool getState() const;
sigc::signal<void(struct rfkill_event&)> on_update;
private:
enum rfkill_type rfkill_type_;
int state_ = 0;
bool state_ = false;
int fd_ = -1;
bool on_event(Glib::IOCondition cond);
};
} // namespace waybar::util

View File

@ -8,6 +8,20 @@
namespace waybar::util {
/**
* Defer pthread_cancel until the end of a current scope.
*
* Required to protect a scope where it's unsafe to raise `__forced_unwind` exception.
* An example of these is a call of a method marked as `noexcept`; an attempt to cancel within such
* a method may result in a `std::terminate` call.
*/
class CancellationGuard {
int oldstate;
public:
CancellationGuard() { pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate); }
~CancellationGuard() { pthread_setcancelstate(oldstate, &oldstate); }
};
class SleeperThread {
public:
SleeperThread() = default;
@ -33,14 +47,16 @@ class SleeperThread {
bool isRunning() const { return do_run_; }
auto sleep_for(std::chrono::system_clock::duration dur) {
std::unique_lock lk(mutex_);
std::unique_lock lk(mutex_);
CancellationGuard cancel_lock;
return condvar_.wait_for(lk, dur, [this] { return signal_ || !do_run_; });
}
auto sleep_until(
std::chrono::time_point<std::chrono::system_clock, std::chrono::system_clock::duration>
time_point) {
std::unique_lock lk(mutex_);
std::unique_lock lk(mutex_);
CancellationGuard cancel_lock;
return condvar_.wait_until(lk, time_point, [this] { return signal_ || !do_run_; });
}

15
include/util/string.hpp Normal file
View File

@ -0,0 +1,15 @@
#include <string>
const std::string WHITESPACE = " \n\r\t\f\v";
std::string ltrim(const std::string s) {
size_t begin = s.find_first_not_of(WHITESPACE);
return (begin == std::string::npos) ? "" : s.substr(begin);
}
std::string rtrim(const std::string s) {
size_t end = s.find_last_not_of(WHITESPACE);
return (end == std::string::npos) ? "" : s.substr(0, end + 1);
}
std::string trim(const std::string& s) { return rtrim(ltrim(s)); }

View File

@ -0,0 +1,5 @@
#pragma once
#include <glibmm/ustring.h>
// calculate column width of ustring
int ustring_clen(const Glib::ustring &str);