Compare commits

..

44 Commits
0.0.1 ... 0.0.4

Author SHA1 Message Date
93f87f322f chore: v0.0.4 2018-08-16 17:19:02 +02:00
8768183f3d fea(workspaces): add disable-scroll config 2018-08-16 17:12:45 +02:00
e4f35d7ca0 fea(custom): add max-length config 2018-08-16 17:09:51 +02:00
57f3a01a5b refactor: remove assert 2018-08-16 15:41:09 +02:00
6635548d3e Style code (#25) 2018-08-16 14:29:41 +02:00
3fdc50163d feat(window): update when window title change 2018-08-16 00:02:57 +02:00
a9246a09eb feat(workspaces): add a option to show all workspaces from all outputs 2018-08-15 22:19:17 +02:00
3ed3416d75 fix(config): update sway workspaces key 2018-08-15 21:03:49 +02:00
008856cbb8 feat(clock): allow choose interval 2018-08-15 21:00:04 +02:00
608b791ac1 refactor(clock): use fmt::localtime 2018-08-15 20:53:27 +02:00
d427512d7d chore: update README 2018-08-15 20:18:00 +02:00
f94598c138 feat(sway): add focused window name 2018-08-15 20:17:17 +02:00
9b75302d22 refactor(client): cleanup 2018-08-15 17:31:45 +02:00
be66cc2dd1 feat(workspaces): add urgent, visible class 2018-08-15 15:03:51 +02:00
52e7b6148b feat(workspaces): add class to button when label is a icon 2018-08-15 14:58:55 +02:00
c3bd6da1d0 chore: v0.0.3 2018-08-15 14:50:19 +02:00
e3e099f836 feat(workspaces): icons 2018-08-15 14:48:08 +02:00
767d9dd5b4 fix(workspaces): buttons iterator 2018-08-15 14:30:01 +02:00
d1d51b76aa fix(client): try to fix #20 2018-08-15 01:53:43 +02:00
cee031d2fa Workspaces scroll event (#19) 2018-08-14 11:26:06 +02:00
18c7ad0026 fix(workspaces): lock mutex inside click callback 2018-08-13 23:43:35 +02:00
1555cb71e1 feat(pulseaudio): volume icons 2018-08-13 22:33:07 +02:00
ea9a08d473 refactor(workspaces): listen ipc event 2018-08-13 21:23:43 +02:00
68f9ea3065 fix(battery): add check for sys files 2018-08-13 17:11:47 +02:00
a423f7032d Battery event (#18) 2018-08-13 14:05:13 +02:00
01894f18cd chore: clean headers 2018-08-12 20:25:19 +02:00
3690e7ac55 chore: v0.0.2 2018-08-11 13:19:26 +02:00
b381e2a596 feat(battery): capacity icons 2018-08-11 13:15:31 +02:00
67fa8bd4c3 style(battery): animates background when battery is in a critical state 2018-08-11 13:03:35 +02:00
6ff296a4b0 refactor(bar): prefer standard unique_ptr 2018-08-11 12:49:28 +02:00
8d5b61a9fd refactor(bar): default width is 0 2018-08-11 10:25:21 +02:00
3c66f4baa7 feat(config): can force width 2018-08-11 09:59:35 +02:00
04183dc696 refactor(modules): prefer sigc::mem_fun 2018-08-11 02:40:13 +02:00
14053d61fc feat(workspace): catch ipc errors 2018-08-11 02:09:39 +02:00
424ebb3c9b feat(workspace): only show workspaces which are on same output as bar 2018-08-11 00:32:59 +02:00
2512d51564 fix(gtk): use idle to avoid mutex 2018-08-10 23:21:21 +02:00
95f1ab471a feat(config): add some comments 2018-08-10 18:57:46 +02:00
c792871f6e feat(bar): choose height from config file 2018-08-10 18:46:31 +02:00
8b0cdc5f4b feat(bar): choose between layers used 2018-08-10 18:30:13 +02:00
7d05f6c612 fix(bar): now use bottom layer 2018-08-10 18:25:07 +02:00
679aafa9e8 refactor(pulseaudio): remove signal init 2018-08-10 18:16:11 +02:00
9b2c551f2e fix(battery): typo 2018-08-10 18:04:48 +02:00
d728de2dd7 fix(battery): check for battries :( 2018-08-10 18:02:12 +02:00
4d3879f26f refactor: avoid useless has_class check 2018-08-10 17:05:12 +02:00
42 changed files with 1191 additions and 2255 deletions

View File

@ -6,6 +6,7 @@
**Current features**
- Sway Workspaces
- Sway focused window name
- Local time
- Battery
- Network
@ -13,6 +14,7 @@
- Memory
- Cpu load average
- Custom scripts
- And much more customizations
**Configuration and Customization**
@ -27,7 +29,8 @@ $ ninja -C build
$ ./build/waybar
```
Contributions welcome! - have fun :)
Contributions welcome! - have fun :)<br>
The style guidelines is [Google's](https://google.github.io/styleguide/cppguide.html)
## License

View File

@ -1,10 +1,14 @@
#pragma once
#include <gtkmm.h>
namespace waybar {
class IModule {
public:
virtual ~IModule() {}
virtual auto update() -> void = 0;
virtual operator Gtk::Widget &() = 0;
};
class IModule {
public:
virtual ~IModule() {}
virtual auto update() -> void = 0;
virtual operator Gtk::Widget &() = 0;
};
}

View File

@ -3,43 +3,51 @@
#include <json/json.h>
#include <gtkmm.h>
#include "wlr-layer-shell-unstable-v1-client-protocol.h"
#include "xdg-output-unstable-v1-client-protocol.h"
namespace waybar {
struct Client;
class Client;
struct Bar {
Bar(Client& client, std::unique_ptr<struct wl_output *>&& output);
class Bar {
public:
Bar(Client&, std::unique_ptr<struct wl_output *>&&);
Bar(const Bar&) = delete;
auto setWidth(uint32_t) -> void;
auto toggle() -> void;
Client& client;
Gtk::Window window;
struct wl_surface *surface;
struct zwlr_layer_surface_v1 *layerSurface;
struct zwlr_layer_surface_v1 *layer_surface;
std::unique_ptr<struct wl_output *> output;
std::string outputName;
bool visible = true;
auto setWidth(int) -> void;
auto toggle() -> void;
private:
static void _handleGeometry(void *data, struct wl_output *wl_output,
int32_t x, int32_t y, int32_t physical_width, int32_t physical_height,
int32_t subpixel, const char *make, const char *model, int32_t transform);
static void _handleMode(void *data, struct wl_output *wl_output,
uint32_t f, int32_t w, int32_t h, int32_t refresh);
static void _handleDone(void *data, struct wl_output *);
static void _handleScale(void *data, struct wl_output *wl_output,
int32_t factor);
static void _layerSurfaceHandleConfigure(void *data,
struct zwlr_layer_surface_v1 *surface, uint32_t serial, uint32_t width,
uint32_t height);
static void _layerSurfaceHandleClosed(void *data,
struct zwlr_layer_surface_v1 *surface);
auto _setupConfig() -> void;
auto _setupWidgets() -> void;
auto _setupCss() -> void;
int _width = 10;
Json::Value _config;
Glib::RefPtr<Gtk::StyleContext> _styleContext;
Glib::RefPtr<Gtk::CssProvider> _cssProvider;
};
static void handleLogicalPosition(void *, struct zxdg_output_v1 *, int32_t,
int32_t);
static void handleLogicalSize(void *, struct zxdg_output_v1 *, int32_t,
int32_t);
static void handleDone(void *, struct zxdg_output_v1 *);
static void handleName(void *, struct zxdg_output_v1 *, const char *);
static void handleDescription(void *, struct zxdg_output_v1 *,
const char *);
static void layerSurfaceHandleConfigure(void *,
struct zwlr_layer_surface_v1 *, uint32_t, uint32_t, uint32_t);
static void layerSurfaceHandleClosed(void *,
struct zwlr_layer_surface_v1 *);
auto setupConfig() -> void;
auto setupWidgets() -> void;
auto setupCss() -> void;
uint32_t width_ = 0;
uint32_t height_ = 30;
Json::Value config_;
Glib::RefPtr<Gtk::StyleContext> style_context_;
Glib::RefPtr<Gtk::CssProvider> css_provider_;
struct zxdg_output_v1 *xdg_output_;
};
}

View File

@ -3,48 +3,41 @@
#include <unistd.h>
#include <wordexp.h>
#include <iostream>
#include <fmt/format.h>
#include <gdk/gdk.h>
#include <gtkmm.h>
#include <wayland-client.h>
#include "wlr-layer-shell-unstable-v1-client-protocol.h"
#include "idle-client-protocol.h"
#include "util/ptr_vec.hpp"
#include <gdk/gdkwayland.h>
#include "bar.hpp"
namespace waybar {
struct Client {
uint32_t height = 30;
std::string cssFile;
std::string configFile;
class Client {
public:
Client(int argc, char *argv[]);
int main(int argc, char *argv[]);
Gtk::Main gtk_main;
std::string css_file;
std::string config_file;
Glib::RefPtr<Gdk::Display> gdk_display;
struct wl_display *wlDisplay;
struct wl_registry *registry;
struct zwlr_layer_shell_v1 *layer_shell;
struct org_kde_kwin_idle *idle_manager;
struct wl_seat *seat;
util::ptr_vec<Bar> bars;
struct wl_display *wl_display = nullptr;
struct wl_registry *registry = nullptr;
struct zwlr_layer_shell_v1 *layer_shell = nullptr;
struct zxdg_output_manager_v1 *xdg_output_manager = nullptr;
struct wl_seat *seat = nullptr;
std::vector<std::unique_ptr<Bar>> bars;
private:
void bindInterfaces();
auto setupCss();
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);
};
Client(int argc, char* argv[]);
void bind_interfaces();
auto setup_css();
int main(int argc, char* argv[]);
private:
static void _handle_global(void *data, struct wl_registry *registry,
uint32_t name, const char *interface, uint32_t version);
static void _handle_global_remove(void *data,
struct wl_registry *registry, uint32_t name);
};
}

View File

@ -2,7 +2,8 @@
#include <json/json.h>
#include "modules/clock.hpp"
#include "modules/workspaces.hpp"
#include "modules/sway/workspaces.hpp"
#include "modules/sway/window.hpp"
#include "modules/battery.hpp"
#include "modules/memory.hpp"
#include "modules/cpu.hpp"
@ -12,13 +13,13 @@
namespace waybar {
class Factory {
public:
Factory(Bar &bar, Json::Value config);
IModule &makeModule(std::string name);
private:
Bar &_bar;
Json::Value _config;
};
class Factory {
public:
Factory(Bar &bar, Json::Value config);
IModule *makeModule(const std::string &name);
private:
Bar &_bar;
Json::Value _config;
};
}

View File

@ -3,27 +3,31 @@
#include <json/json.h>
#include <filesystem>
#include <fstream>
#include <gtkmm.h>
#include <iostream>
#include <fmt/format.h>
#include <sys/inotify.h>
#include <algorithm>
#include "util/chrono.hpp"
#include "IModule.hpp"
namespace waybar::modules {
namespace fs = std::filesystem;
namespace fs = std::filesystem;
class Battery : public IModule {
public:
Battery(Json::Value config);
auto update() -> void;
operator Gtk::Widget&();
private:
static inline const fs::path _data_dir = "/sys/class/power_supply/";
std::vector<fs::path> _batteries;
util::SleeperThread _thread;
Gtk::Label _label;
Json::Value _config;
};
class Battery : public IModule {
public:
Battery(Json::Value);
auto update() -> void;
operator Gtk::Widget&();
private:
std::string getIcon(uint16_t percentage);
static inline const fs::path data_dir_ = "/sys/class/power_supply/";
Gtk::Label label_;
Json::Value config_;
util::SleeperThread thread_;
std::vector<fs::path> batteries_;
};
}

View File

@ -1,23 +1,22 @@
#pragma once
#include <json/json.h>
#include <gtkmm.h>
#include <fmt/format.h>
#include <thread>
#include "fmt/time.h"
#include "util/chrono.hpp"
#include "IModule.hpp"
namespace waybar::modules {
class Clock : public IModule {
public:
Clock(Json::Value config);
auto update() -> void;
operator Gtk::Widget &();
private:
Gtk::Label _label;
waybar::util::SleeperThread _thread;
Json::Value _config;
};
class Clock : public IModule {
public:
Clock(Json::Value);
auto update() -> void;
operator Gtk::Widget &();
private:
Gtk::Label label_;
waybar::util::SleeperThread thread_;
Json::Value config_;
};
}

View File

@ -1,24 +1,22 @@
#pragma once
#include <json/json.h>
#include <gtkmm.h>
#include <fmt/format.h>
#include <sys/sysinfo.h>
#include <thread>
#include "util/chrono.hpp"
#include "IModule.hpp"
namespace waybar::modules {
class Cpu : public IModule {
public:
Cpu(Json::Value config);
auto update() -> void;
operator Gtk::Widget &();
private:
Gtk::Label _label;
waybar::util::SleeperThread _thread;
Json::Value _config;
};
class Cpu : public IModule {
public:
Cpu(Json::Value);
auto update() -> void;
operator Gtk::Widget &();
private:
Gtk::Label label_;
waybar::util::SleeperThread thread_;
Json::Value config_;
};
}

View File

@ -1,24 +1,22 @@
#pragma once
#include <json/json.h>
#include <gtkmm.h>
#include <fmt/format.h>
#include <thread>
#include "util/chrono.hpp"
#include "IModule.hpp"
namespace waybar::modules {
class Custom : public IModule {
public:
Custom(std::string name, Json::Value config);
auto update() -> void;
operator Gtk::Widget &();
private:
Gtk::Label _label;
waybar::util::SleeperThread _thread;
const std::string _name;
Json::Value _config;
};
class Custom : public IModule {
public:
Custom(const std::string&, Json::Value);
auto update() -> void;
operator Gtk::Widget &();
private:
const std::string name_;
Gtk::Label label_;
waybar::util::SleeperThread thread_;
Json::Value config_;
};
}

View File

@ -1,24 +1,22 @@
#pragma once
#include <json/json.h>
#include <gtkmm.h>
#include <fmt/format.h>
#include <sys/sysinfo.h>
#include <thread>
#include "util/chrono.hpp"
#include "IModule.hpp"
namespace waybar::modules {
class Memory : public IModule {
public:
Memory(Json::Value config);
auto update() -> void;
operator Gtk::Widget &();
private:
Gtk::Label _label;
waybar::util::SleeperThread _thread;
Json::Value _config;
};
class Memory : public IModule {
public:
Memory(Json::Value);
auto update() -> void;
operator Gtk::Widget &();
private:
Gtk::Label label_;
waybar::util::SleeperThread thread_;
Json::Value config_;
};
}

View File

@ -5,34 +5,33 @@
#include <netlink/genl/genl.h>
#include <netlink/genl/ctrl.h>
#include <linux/nl80211.h>
#include <iwlib.h> // TODO
#include <json/json.h>
#include <gtkmm.h>
#include <fmt/format.h>
#include <thread>
#include "util/chrono.hpp"
#include "IModule.hpp"
namespace waybar::modules {
class Network : public IModule {
public:
Network(Json::Value config);
auto update() -> void;
operator Gtk::Widget &();
private:
void _parseEssid(struct nlattr **bss);
void _parseSignal(struct nlattr **bss);
bool _associatedOrJoined(struct nlattr **bss);
static int _scanCb(struct nl_msg *msg, void *data);
auto _getInfo() -> void;
Gtk::Label _label;
waybar::util::SleeperThread _thread;
Json::Value _config;
std::size_t _ifid;
std::string _essid;
int _signalStrengthdBm;
int _signalStrength;
};
class Network : public IModule {
public:
Network(Json::Value);
auto update() -> void;
operator Gtk::Widget &();
private:
static int scanCb(struct nl_msg*, void*);
void parseEssid(struct nlattr**);
void parseSignal(struct nlattr**);
bool associatedOrJoined(struct nlattr**);
auto getInfo() -> void;
Gtk::Label label_;
waybar::util::SleeperThread thread_;
Json::Value config_;
std::size_t ifid_;
std::string essid_;
int signal_strength_dbm_;
uint16_t signal_strength_;
};
}

View File

@ -2,34 +2,35 @@
#include <pulse/pulseaudio.h>
#include <json/json.h>
#include <gtkmm.h>
#include <fmt/format.h>
#include <algorithm>
#include "IModule.hpp"
namespace waybar::modules {
class Pulseaudio : public IModule {
public:
Pulseaudio(Json::Value config);
auto update() -> void;
operator Gtk::Widget &();
private:
static void _subscribeCb(pa_context *context,
pa_subscription_event_type_t type, uint32_t idx, void *data);
static void _contextStateCb(pa_context *c, void *data);
static void _sinkInfoCb(pa_context *context, const pa_sink_info *i,
int eol, void *data);
static void _serverInfoCb(pa_context *context, const pa_server_info *i,
void *data);
Gtk::Label _label;
Json::Value _config;
pa_threaded_mainloop *_mainloop;
pa_mainloop_api *_mainloop_api;
pa_context *_context;
uint32_t _sinkIdx{0};
int _volume;
bool _muted;
std::string _desc;
};
class Pulseaudio : public IModule {
public:
Pulseaudio(Json::Value);
auto update() -> void;
operator Gtk::Widget &();
private:
static void subscribeCb(pa_context*, pa_subscription_event_type_t,
uint32_t, void*);
static void contextStateCb(pa_context*, void*);
static void sinkInfoCb(pa_context*, const pa_sink_info*, int, void*);
static void serverInfoCb(pa_context*, const pa_server_info*, void*);
std::string getIcon(uint16_t);
Gtk::Label label_;
Json::Value config_;
pa_threaded_mainloop* mainloop_;
pa_mainloop_api* mainloop_api_;
pa_context* context_;
uint32_t sink_idx_{0};
uint16_t volume_;
bool muted_;
std::string desc_;
};
}

View File

@ -16,17 +16,17 @@ struct ipc_response {
/**
* Gets the path to the IPC socket from sway.
*/
std::string get_socketpath(void);
std::string getSocketPath(void);
/**
* Opens the sway socket.
*/
int ipc_open_socket(std::string socket_path);
int ipcOpenSocket(const std::string &socketPath);
/**
* Issues a single IPC command and returns the buffer. len will be updated with
* the length of the buffer returned from sway.
*/
std::string ipc_single_command(int socketfd, uint32_t type, const char *payload, uint32_t *len);
std::string ipcSingleCommand(int socketfd, uint32_t type, const char *payload, uint32_t *len);
/**
* Receives a single IPC response and returns an ipc_response.
*/
struct ipc_response ipc_recv_response(int socketfd);
struct ipc_response ipcRecvResponse(int socketfd);

View File

@ -0,0 +1,31 @@
#pragma once
#include <fmt/format.h>
#include "bar.hpp"
#include "client.hpp"
#include "util/chrono.hpp"
#include "util/json.hpp"
#include "IModule.hpp"
namespace waybar::modules::sway {
class Window : public IModule {
public:
Window(waybar::Bar&, Json::Value);
auto update() -> void;
operator Gtk::Widget &();
private:
std::string getFocusedNode(Json::Value nodes);
void getFocusedWindow();
Bar& bar_;
Json::Value config_;
waybar::util::SleeperThread thread_;
Gtk::Label label_;
util::JsonParser parser_;
int ipcfd_;
int ipc_eventfd_;
std::string window_;
};
}

View File

@ -0,0 +1,37 @@
#pragma once
#include <fmt/format.h>
#include "bar.hpp"
#include "client.hpp"
#include "util/chrono.hpp"
#include "util/json.hpp"
#include "IModule.hpp"
namespace waybar::modules::sway {
class Workspaces : public IModule {
public:
Workspaces(waybar::Bar&, Json::Value);
auto update() -> void;
operator Gtk::Widget &();
private:
void addWorkspace(Json::Value);
std::string getIcon(std::string);
bool handleScroll(GdkEventScroll*);
int getPrevWorkspace();
int getNextWorkspace();
Bar& bar_;
Json::Value config_;
waybar::util::SleeperThread thread_;
Gtk::Box box_;
util::JsonParser parser_;
std::mutex mutex_;
bool scrolling_;
std::unordered_map<int, Gtk::Button> buttons_;
Json::Value workspaces_;
int ipcfd_;
int ipc_eventfd_;
};
}

View File

@ -1,33 +0,0 @@
#pragma once
#include <fmt/format.h>
#include "bar.hpp"
#include "client.hpp"
#include "util/chrono.hpp"
#include "IModule.hpp"
namespace waybar::modules {
class Workspaces : public IModule {
public:
Workspaces(waybar::Bar &bar);
auto update() -> void;
operator Gtk::Widget &();
private:
void _updateThread();
static void _handle_idle(void *data,
struct org_kde_kwin_idle_timeout *timer);
static void _handle_resume(void *data,
struct org_kde_kwin_idle_timeout *timer);
void _addWorkspace(Json::Value node);
Json::Value _getWorkspaces();
Bar &_bar;
util::SleeperThread *_thread;
Gtk::Box *_box;
std::unordered_map<int, Gtk::Button> _buttons;
int _ipcSocketfd;
int _ipcEventSocketfd;
struct org_kde_kwin_idle_timeout *_idle_timer;
};
}

View File

@ -1,823 +0,0 @@
#pragma once
#include <algorithm>
#include <functional>
#include <initializer_list>
#include <numeric>
#include <string>
#include <string_view>
namespace waybar::util {
/// Joins a sequence of strings, separating them using `js`
template<class StrIterator> // Models InputIterator<std::string>
std::string join_strings(StrIterator b, StrIterator e, std::string_view js = ", ")
{
std::string result;
std::for_each(b, e, [&](auto&& s) {
if (!result.empty()) {
result.append(js);
}
result.append(s);
});
return result;
}
inline const char* nonull(const char* str) {
if (str == nullptr) return "";
return str;
};
inline bool iequals(std::string_view a, std::string_view b)
{
return std::equal(a.begin(), a.end(), b.begin(), b.end(),
[](char a, char b) { return tolower(a) == tolower(b); });
}
inline bool starts_with(std::string_view prefix, std::string_view a)
{
return a.compare(0, prefix.size(), prefix) == 0;
}
inline bool ends_with(std::string_view prefix, std::string_view a)
{
return a.compare(a.size() - prefix.size(), prefix.size(), prefix) == 0;
}
/// Return a closure which compares the adress any reference to T to the address of t
template<typename T>
constexpr auto addr_eq(T&& t) {
return [&t] (auto&& t2) {
return &t == &t2;
};
}
template<typename T>
bool erase_this(std::vector<T>& cont, T* el)
{
if (el < cont.data() && el >= cont.data() + cont.size()) return false;
cont.erase(cont.begin() + (el - cont.data()));
return true;
}
template<typename T>
bool erase_this(std::vector<T>& cont, T& el)
{
return erase_this(cont, &el);
}
namespace detail {
template<class Func, int... ns>
constexpr auto generate_array_impl(std::integer_sequence<int, ns...>&&, Func&& gen)
{
return std::array<std::decay_t<decltype(std::invoke(gen, std::declval<int>()))>,
sizeof...(ns)>{{std::invoke(gen, ns)...}};
}
} // namespace detail
template<int n, class Func>
constexpr auto generate_array(Func&& gen)
{
auto intseq = std::make_integer_sequence<int, n>();
return detail::generate_array_impl(std::move(intseq), std::forward<Func>(gen));
}
namespace view {
namespace detail {
template<typename T>
using store_or_ref_t = std::conditional_t<std::is_rvalue_reference_v<T>, std::decay_t<T>, T&>;
}
template<typename Cont>
struct reverse {
reverse(Cont&& cont) noexcept : _container(std::forward<Cont>(cont)) {}
auto begin()
{
return std::rbegin(_container);
}
auto end()
{
return std::rend(_container);
}
auto begin() const
{
return std::rbegin(_container);
}
auto end() const
{
return std::rend(_container);
}
auto cbegin() const
{
return std::crbegin(_container);
}
auto cend() const
{
return std::crend(_container);
}
detail::store_or_ref_t<Cont&&> _container;
};
template<typename ContRef>
reverse(ContRef&& cont) -> reverse<ContRef&&>;
template<typename Cont>
struct constant {
constant(Cont&& cont) noexcept : _container(std::forward<Cont>(cont)){};
auto begin() const
{
return std::cbegin(_container);
}
auto end() const
{
return std::cend(_container);
}
auto cbegin() const
{
return std::cbegin(_container);
}
auto cend() const
{
return std::cend(_container);
}
detail::store_or_ref_t<Cont&&> _container;
};
template<typename ContRef>
constant(ContRef&& cont) -> constant<ContRef&&>;
} // namespace view
/*
* Range algorithms
*/
template<typename InputIt, typename Size, typename F>
constexpr InputIt for_each_n(InputIt&& first, Size n, F&& f)
{
for (Size i = 0; i < n; ++first, ++i) {
std::invoke(f, *first);
}
return first;
}
/// `for_each` with access to an index value. Function called as `f(*it, i)`
///
/// For each item in range `[first, last)`, invoke `f` with args
/// `*iter, i` where `iter` is the current iterator, and `i` is
/// an incrementing value, starting at zero. Use this instead of
/// raw indexed loops wherever possible.
///
/// \param first Input iterator to the begining of the range
/// \param last Input iterator to the end of the range
/// \param f Must be invocable with arguments `value_type`, `std::size_t`
/// \returns The number of iterations performed
template<typename InputIt, typename F>
constexpr std::size_t indexed_for(InputIt&& first, InputIt&& last, F&& f)
{
std::size_t i = 0;
std::for_each(std::forward<InputIt>(first), std::forward<InputIt>(last), [&](auto&& a) {
std::invoke(f, a, i);
i++;
});
return i;
}
template<typename Rng, typename F>
constexpr std::size_t indexed_for(Rng&& rng, F&& f)
{
return indexed_for(std::begin(rng), std::end(rng), std::forward<F>(f));
}
/// `for_each_n` with access to an index value. Function called as `f(*it, i)`
///
/// for `n` iterations, invoke `f` with args `*iter, i`
/// where `iter` is the current iterator starting with `first`,
/// and `i` is an incrementing value, starting at zero.
/// Use this instead of raw indexed loops wherever possible.
///
/// \param first Input iterator to the begining of the range
/// \param n Number of iterations to go through
/// \param f Must be invocable with arguments `value_type`, `std::size_t`
/// \returns An iterator one past the last one visited
template<class InputIt, class Size, class F>
constexpr InputIt indexed_for_n(InputIt first, Size n, F&& f)
{
for (Size i = 0; i < n; ++first, ++i) {
std::invoke(f, *first, i);
}
return first;
}
template<class Rng, class Size, class F>
constexpr std::size_t indexed_for_n(Rng&& rng, Size n, F&& f)
{
return indexed_for_n(std::begin(rng), std::end(rng), n, std::forward<F>(f));
}
template<typename Iter1, typename Iter2, typename F>
constexpr void for_both(Iter1&& f1, Iter1&& l1, Iter2&& f2, Iter2&& l2, F&& f)
{
Iter1 i1 = std::forward<Iter1>(f1);
Iter2 i2 = std::forward<Iter2>(f2);
for (; i1 != l1 && i2 != l2; i1++, i2++) {
std::invoke(f, *i1, *i2);
}
}
template<typename Rng1, typename Rng2, typename F>
constexpr void for_both(Rng1&& r1, Rng2&& r2, F&& f)
{
for_both(std::begin(r1), std::end(r1), std::begin(r2), std::end(r2), std::forward<F>(f));
}
/*
* Range based standard algorithms
*
* Thanks, chris from SO!
*/
template<typename Cont, typename T>
constexpr auto accumulate(Cont&& cont, T&& init)
{
// TODO C++20: std::accumulate is constexpr
using std::begin, std::end;
auto first = begin(cont);
auto last = end(cont);
for (; first != last; ++first) init = init + *first;
return init;
}
template<typename Cont, typename T, typename BinaryOperation>
constexpr auto accumulate(Cont&& cont, T&& init, BinaryOperation&& op)
{
// TODO C++20: std::accumulate is constexpr
using std::begin, std::end;
auto first = begin(cont);
auto last = end(cont);
for (; first != last; ++first) init = op(init, *first);
return init;
}
template<typename Cont, typename OutputIterator>
decltype(auto) adjacent_difference(Cont&& cont, OutputIterator&& first)
{
using std::begin;
using std::end;
return std::adjacent_difference(begin(cont), end(cont), std::forward<OutputIterator>(first));
}
template<typename Cont>
decltype(auto) prev_permutation(Cont&& cont)
{
using std::begin;
using std::end;
return std::prev_permutation(begin(cont), end(cont));
}
template<typename Cont, typename Compare>
decltype(auto) prev_permutation(Cont&& cont, Compare&& comp)
{
using std::begin;
using std::end;
return std::prev_permutation(begin(cont), end(cont), std::forward<Compare>(comp));
}
template<typename Cont>
decltype(auto) push_heap(Cont&& cont)
{
using std::begin;
using std::end;
return std::push_heap(begin(cont), end(cont));
}
template<typename Cont, typename Compare>
decltype(auto) push_heap(Cont&& cont, Compare&& comp)
{
using std::begin;
using std::end;
return std::push_heap(begin(cont), end(cont), std::forward<Compare>(comp));
}
template<typename Cont, typename T>
decltype(auto) remove(Cont&& cont, T&& value)
{
using std::begin;
using std::end;
return std::remove(begin(cont), end(cont), std::forward<T>(value));
}
template<typename Cont, typename OutputIterator, typename T>
decltype(auto) remove_copy(Cont&& cont, OutputIterator&& first, T&& value)
{
using std::begin;
using std::end;
return std::remove_copy(begin(cont), end(cont), std::forward<OutputIterator>(first),
std::forward<T>(value));
}
template<typename Cont, typename OutputIterator, typename UnaryPredicate>
decltype(auto) remove_copy_if(Cont&& cont, OutputIterator&& first, UnaryPredicate&& p)
{
using std::begin;
using std::end;
return std::remove_copy_if(begin(cont), end(cont), std::forward<OutputIterator>(first),
std::forward<UnaryPredicate>(p));
}
template<typename Cont, typename UnaryPredicate>
decltype(auto) remove_if(Cont&& cont, UnaryPredicate&& p)
{
using std::begin;
using std::end;
return std::remove_if(begin(cont), end(cont), std::forward<UnaryPredicate>(p));
}
template<typename Cont, typename T, typename T2>
decltype(auto) replace(Cont&& cont, T&& old_value, T2&& new_value)
{
using std::begin;
using std::end;
return std::replace(begin(cont), end(cont), std::forward<T>(old_value),
std::forward<T2>(new_value));
}
template<typename Cont, typename OutputIterator, typename T, typename T2>
decltype(auto) replace_copy(Cont&& cont, OutputIterator&& first, T&& old_value, T2&& new_value)
{
using std::begin;
using std::end;
return std::replace_copy(begin(cont), end(cont), std::forward<OutputIterator>(first),
std::forward<T>(old_value), std::forward<T2>(old_value));
}
template<typename Cont, typename OutputIterator, typename UnaryPredicate, typename T>
decltype(auto) replace_copy_if(Cont&& cont,
OutputIterator&& first,
UnaryPredicate&& p,
T&& new_value)
{
using std::begin;
using std::end;
return std::replace_copy(begin(cont), end(cont), std::forward<OutputIterator>(first),
std::forward<UnaryPredicate>(p), std::forward<T>(new_value));
}
template<typename Cont, typename UnaryPredicate, typename T>
decltype(auto) replace_if(Cont&& cont, UnaryPredicate&& p, T&& new_value)
{
using std::begin;
using std::end;
return std::replace_if(begin(cont), end(cont), std::forward<UnaryPredicate>(p),
std::forward<T>(new_value));
}
template<typename Cont>
decltype(auto) reverse(Cont&& cont)
{
using std::begin;
using std::end;
return std::reverse(begin(cont), end(cont));
}
template<typename Cont, typename OutputIterator>
decltype(auto) reverse_copy(Cont&& cont, OutputIterator&& first)
{
using std::begin;
using std::end;
return std::reverse_copy(begin(cont), end(cont), std::forward<OutputIterator>(first));
}
template<typename Cont, typename ForwardIterator>
decltype(auto) rotate(Cont&& cont, ForwardIterator&& new_first)
{
using std::begin;
using std::end;
return std::rotate(begin(cont), std::forward<ForwardIterator>(new_first), end(cont));
}
template<typename Cont, typename ForwardIterator, typename OutputIterator>
decltype(auto) rotate_copy(Cont&& cont, ForwardIterator&& new_first, OutputIterator&& first)
{
using std::begin;
using std::end;
return std::rotate_copy(begin(cont), std::forward<ForwardIterator>(new_first), end(cont),
std::forward<OutputIterator>(first));
}
template<typename Cont, typename Cont2>
decltype(auto) search(Cont&& cont, Cont2&& cont2)
{
using std::begin;
using std::end;
return std::search(begin(cont), end(cont), begin(cont2), end(cont2));
}
template<typename Cont, typename Cont2, typename BinaryPredicate>
decltype(auto) search(Cont&& cont, Cont2&& cont2, BinaryPredicate&& p)
{
using std::begin;
using std::end;
return std::search(begin(cont), end(cont), begin(cont2), end(cont2),
std::forward<BinaryPredicate>(p));
}
template<typename Cont, typename Size, typename T>
decltype(auto) search_n(Cont&& cont, Size count, T&& value)
{
using std::begin;
using std::end;
return std::search_n(begin(cont), end(cont), count, std::forward<T>(value));
}
template<typename Cont, typename Size, typename T, typename BinaryPredicate>
decltype(auto) search_n(Cont&& cont, Size count, T&& value, BinaryPredicate&& p)
{
using std::begin;
using std::end;
return std::search_n(begin(cont), end(cont), count, std::forward<T>(value),
std::forward<BinaryPredicate>(p));
}
template<typename Cont, typename Cont2, typename OutputIterator>
decltype(auto) set_difference(Cont&& cont, Cont2&& cont2, OutputIterator&& first)
{
using std::begin;
using std::end;
return std::set_difference(begin(cont), end(cont), begin(cont2), end(cont2),
std::forward<OutputIterator>(first));
}
template<typename Cont, typename Cont2, typename OutputIterator, typename Compare>
decltype(auto) set_difference(Cont&& cont, Cont2&& cont2, OutputIterator&& first, Compare&& comp)
{
using std::begin;
using std::end;
return std::set_difference(begin(cont), end(cont), begin(cont2), end(cont2),
std::forward<OutputIterator>(first), std::forward<Compare>(comp));
}
template<typename Cont, typename Cont2, typename OutputIterator>
decltype(auto) set_intersection(Cont&& cont, Cont2&& cont2, OutputIterator&& first)
{
using std::begin;
using std::end;
return std::set_intersection(begin(cont), end(cont), begin(cont2), end(cont2),
std::forward<OutputIterator>(first));
}
template<typename Cont, typename Cont2, typename OutputIterator, typename Compare>
decltype(auto) set_intersection(Cont&& cont,
Cont2&& cont2,
OutputIterator&& first,
Compare&& comp)
{
using std::begin;
using std::end;
return std::set_intersection(begin(cont), end(cont), begin(cont2), end(cont2),
std::forward<OutputIterator>(first), std::forward<Compare>(comp));
}
template<typename Cont, typename Cont2, typename OutputIterator>
decltype(auto) set_symmetric_difference(Cont&& cont, Cont2&& cont2, OutputIterator&& first)
{
using std::begin;
using std::end;
return std::set_symmetric_difference(begin(cont), end(cont), begin(cont2), end(cont2),
std::forward<OutputIterator>(first));
}
template<typename Cont, typename Cont2, typename OutputIterator, typename Compare>
decltype(auto) set_symmetric_difference(Cont&& cont,
Cont2&& cont2,
OutputIterator&& first,
Compare&& comp)
{
using std::begin;
using std::end;
return std::set_symmetric_difference(begin(cont), end(cont), begin(cont2), end(cont2),
std::forward<OutputIterator>(first),
std::forward<Compare>(comp));
}
template<typename Cont, typename Cont2, typename OutputIterator>
decltype(auto) set_union(Cont&& cont, Cont2&& cont2, OutputIterator&& first)
{
using std::begin;
using std::end;
return std::set_union(begin(cont), end(cont), begin(cont2), end(cont2),
std::forward<OutputIterator>(first));
}
template<typename Cont, typename Cont2, typename OutputIterator, typename Compare>
decltype(auto) set_union(Cont&& cont, Cont2&& cont2, OutputIterator&& first, Compare&& comp)
{
using std::begin;
using std::end;
return std::set_union(begin(cont), end(cont), begin(cont2), end(cont2),
std::forward<OutputIterator>(first), std::forward<Compare>(comp));
}
template<typename Cont, typename UniformRandomNumberGenerator>
decltype(auto) shuffle(Cont&& cont, UniformRandomNumberGenerator&& g)
{
using std::begin;
using std::end;
return std::shuffle(begin(cont), end(cont), std::forward<UniformRandomNumberGenerator>(g));
}
template<typename Cont>
decltype(auto) sort(Cont&& cont)
{
using std::begin;
using std::end;
return std::sort(begin(cont), end(cont));
}
template<typename Cont, typename Compare>
decltype(auto) sort(Cont&& cont, Compare&& comp)
{
using std::begin;
using std::end;
return std::sort(begin(cont), end(cont), std::forward<Compare>(comp));
}
template<typename Cont>
decltype(auto) sort_heap(Cont&& cont)
{
using std::begin;
using std::end;
return std::sort_heap(begin(cont), end(cont));
}
template<typename Cont, typename Compare>
decltype(auto) sort_heap(Cont&& cont, Compare&& comp)
{
using std::begin;
using std::end;
return std::sort_heap(begin(cont), end(cont), std::forward<Compare>(comp));
}
template<typename Cont, typename UnaryPredicate>
decltype(auto) stable_partition(Cont&& cont, UnaryPredicate&& p)
{
using std::begin;
using std::end;
return std::stable_partition(begin(cont), end(cont), std::forward<UnaryPredicate>(p));
}
template<typename Cont>
decltype(auto) stable_sort(Cont&& cont)
{
using std::begin;
using std::end;
return std::stable_sort(begin(cont), end(cont));
}
template<typename Cont, typename Compare>
decltype(auto) stable_sort(Cont&& cont, Compare&& comp)
{
using std::begin;
using std::end;
return std::stable_sort(begin(cont), end(cont), std::forward<Compare>(comp));
}
template<typename Cont, typename ForwardIterator>
decltype(auto) swap_ranges(Cont&& cont, ForwardIterator&& first)
{
using std::begin;
using std::end;
return std::swap_ranges(begin(cont), end(cont), std::forward<ForwardIterator>(first));
}
template<typename Cont, typename Cont2, typename F>
auto transform(Cont&& cont, Cont2&& cont2, F&& f) -> decltype(begin(cont2))
{
using std::begin;
using std::end;
return std::transform(begin(cont), end(cont), begin(cont2), std::forward<F>(f));
}
template<typename Cont, typename Iter, typename F>
decltype(auto) transform(Cont&& cont, Iter&& iter, F&& f)
{
using std::begin;
using std::end;
return std::transform(begin(cont), end(cont), std::forward<Iter>(iter), std::forward<F>(f));
}
template<typename Cont, typename Cont2, typename Cont3, typename BinaryPredicate>
auto transform(Cont&& cont, Cont2&& cont2, Cont3&& cont3, BinaryPredicate&& f)
-> decltype(begin(cont2), begin(cont3))
{
using std::begin;
using std::end;
return std::transform(begin(cont), end(cont), begin(cont2), begin(cont3),
std::forward<BinaryPredicate>(f));
}
template<typename Cont, typename InputIterator, typename Cont3, typename BinaryPredicate>
auto transform(Cont&& cont, InputIterator&& iter, Cont3&& cont3, BinaryPredicate&& f)
-> decltype(begin(cont), begin(cont3))
{
using std::begin;
using std::end;
return std::transform(begin(cont), end(cont), std::forward<InputIterator>(iter), begin(cont3),
std::forward<BinaryPredicate>(f));
}
template<typename Cont, typename Cont2, typename InputIterator, typename BinaryPredicate>
auto transform(Cont&& cont, Cont2&& cont2, InputIterator&& iter, BinaryPredicate&& f)
-> decltype(begin(cont), begin(cont2), iter)
{
using std::begin;
using std::end;
return std::transform(begin(cont), end(cont), begin(cont2), std::forward<InputIterator>(iter),
std::forward<BinaryPredicate>(f));
}
template<typename Cont, typename InputIterator, typename OutputIterator, typename BinaryOperation>
decltype(auto) transform(Cont&& cont,
InputIterator&& firstIn,
OutputIterator&& firstOut,
BinaryOperation&& op)
{
using std::begin;
using std::end;
return std::transform(begin(cont), end(cont), std::forward<InputIterator>(firstIn),
std::forward<OutputIterator>(firstOut),
std::forward<BinaryOperation>(op));
}
template<typename Cont>
decltype(auto) unique(Cont&& cont)
{
using std::begin;
using std::end;
return std::unique(begin(cont), end(cont));
}
template<typename Cont, typename BinaryPredicate>
decltype(auto) unique(Cont&& cont, BinaryPredicate&& p)
{
using std::begin;
using std::end;
return std::unique(begin(cont), end(cont), std::forward<BinaryPredicate>(p));
}
template<typename Cont, typename OutputIterator>
decltype(auto) unique_copy(Cont&& cont, OutputIterator&& first)
{
using std::begin;
using std::end;
return std::unique_copy(begin(cont), end(cont), std::forward<OutputIterator>(first));
}
template<typename Cont, typename OutputIterator, typename BinaryPredicate>
decltype(auto) unique_copy(Cont&& cont, OutputIterator&& first, BinaryPredicate&& p)
{
using std::begin;
using std::end;
return std::unique_copy(begin(cont), end(cont), std::forward<OutputIterator>(first),
std::forward<BinaryPredicate>(p));
}
template<typename Cont, typename T>
decltype(auto) upper_bound(Cont&& cont, T&& value)
{
using std::begin;
using std::end;
return std::upper_bound(begin(cont), end(cont), std::forward<T>(value));
}
template<typename Cont, typename T, typename Compare>
decltype(auto) upper_bound(Cont&& cont, T&& value, Compare&& comp)
{
using std::begin;
using std::end;
return std::upper_bound(begin(cont), end(cont), std::forward<T>(value),
std::forward<Compare>(comp));
}
template<typename Cont, typename OutputIterator>
decltype(auto) copy(Cont&& cont, OutputIterator&& first)
{
using std::begin;
using std::end;
return std::copy(begin(cont), end(cont), std::forward<OutputIterator>(first));
}
template<typename Cont, typename OutputIterator, typename UnaryPredicate>
decltype(auto) copy_if(Cont&& cont, OutputIterator&& first, UnaryPredicate&& p)
{
using std::begin;
using std::end;
return std::copy_if(begin(cont), end(cont), std::forward<OutputIterator>(first),
std::forward<UnaryPredicate>(p));
}
template<typename Cont, typename T>
decltype(auto) fill(Cont&& cont, T&& value)
{
using std::begin;
using std::end;
return std::fill(begin(cont), end(cont), std::forward<T>(value));
}
template<typename Cont, typename T>
decltype(auto) fill_n(Cont&& cont, std::size_t n, T&& value)
{
using std::begin;
using std::end;
return std::fill_n(begin(cont), n, std::forward<T>(value));
}
template<typename Cont, typename UnaryPredicate>
decltype(auto) any_of(Cont&& cont, UnaryPredicate&& p)
{
using std::begin;
using std::end;
return std::any_of(begin(cont), end(cont), std::forward<UnaryPredicate>(p));
}
template<typename Cont, typename UnaryPredicate>
decltype(auto) all_of(Cont&& cont, UnaryPredicate&& p)
{
using std::begin;
using std::end;
return std::all_of(begin(cont), end(cont), std::forward<UnaryPredicate>(p));
}
template<typename Cont, typename UnaryPredicate>
decltype(auto) none_of(Cont&& cont, UnaryPredicate&& p)
{
using std::begin;
using std::end;
return std::none_of(begin(cont), end(cont), std::forward<UnaryPredicate>(p));
}
template<typename Cont>
decltype(auto) max_element(Cont&& cont)
{
using std::begin;
using std::end;
return std::max_element(begin(cont), end(cont));
}
template<typename Cont>
decltype(auto) min_element(Cont&& cont)
{
using std::begin;
using std::end;
return std::min_element(begin(cont), end(cont));
}
template<typename Cont, typename Compare>
decltype(auto) min_element(Cont&& cont, Compare&& f)
{
using std::begin;
using std::end;
return std::min_element(begin(cont), end(cont), std::forward<Compare>(f));
}
template<typename Cont, typename Compare>
decltype(auto) max_element(Cont&& cont, Compare&& f)
{
using std::begin;
using std::end;
return std::max_element(begin(cont), end(cont), std::forward<Compare>(f));
}
template<typename Cont, typename T>
decltype(auto) find(Cont&& cont, T&& t)
{
using std::begin;
using std::end;
return std::find(begin(cont), end(cont), std::forward<T>(t));
}
template<typename Cont, typename UnaryPredicate>
decltype(auto) find_if(Cont&& cont, UnaryPredicate&& f)
{
using std::begin;
using std::end;
return std::find_if(begin(cont), end(cont), std::forward<UnaryPredicate>(f));
}
} // namespace waybar::util

View File

@ -39,7 +39,9 @@ namespace waybar::util {
func();
} while (do_run);
}}
{}
{
defined = true;
}
SleeperThread& operator=(std::function<void()> func)
{
@ -48,6 +50,7 @@ namespace waybar::util {
func();
} while (do_run);
});
defined = true;
return *this;
}
@ -72,14 +75,17 @@ namespace waybar::util {
~SleeperThread()
{
do_run = false;
condvar.notify_all();
thread.join();
if (defined) {
condvar.notify_all();
thread.join();
}
}
private:
std::thread thread;
std::condition_variable condvar;
std::mutex mutex;
bool defined = false;
bool do_run = true;
};

34
include/util/json.hpp Normal file
View File

@ -0,0 +1,34 @@
#pragma once
#include <json/json.h>
namespace waybar::util {
struct JsonParser {
JsonParser()
: _reader(_builder.newCharReader())
{}
Json::Value parse(const std::string data)
{
Json::Value root;
std::string err;
bool res =
_reader->parse(data.c_str(), data.c_str() + data.size(), &root, &err);
if (!res)
throw std::runtime_error(err);
return root;
}
~JsonParser()
{
delete _reader;
}
private:
Json::CharReaderBuilder _builder;
Json::CharReader *_reader;
};
}

View File

@ -1,581 +0,0 @@
#pragma once
#include <cassert>
#include <memory>
#include <type_traits>
#include <vector>
#include "algorithm.hpp"
namespace waybar::util {
/// An iterator wrapper that dereferences twice.
template<typename Iter>
struct double_iterator {
using wrapped = Iter;
using value_type = std::decay_t<decltype(*std::declval<typename wrapped::value_type>())>;
using difference_type = typename wrapped::difference_type;
using reference = value_type&;
using pointer = value_type*;
using iterator_category = std::random_access_iterator_tag;
using self_t = double_iterator<Iter>;
double_iterator(wrapped w) : _iter(std::move(w)) {}
double_iterator() : _iter() {}
reference operator*() const
{
return (**_iter);
}
pointer operator->() const
{
return &(**_iter);
}
self_t& operator++()
{
_iter.operator++();
return *this;
}
self_t operator++(int i)
{
return _iter.operator++(i);
}
self_t& operator--()
{
_iter.operator--();
return *this;
}
self_t operator--(int i)
{
return _iter.operator--(i);
}
auto operator==(const self_t& rhs) const noexcept
{
return _iter == rhs._iter;
}
auto operator!=(const self_t& rhs) const noexcept
{
return _iter != rhs._iter;
}
auto operator<(const self_t& rhs) const noexcept
{
return _iter < rhs._iter;
}
auto operator>(const self_t& rhs) const noexcept
{
return _iter > rhs._iter;
}
auto operator<=(const self_t& rhs) const noexcept
{
return _iter <= rhs._iter;
}
auto operator>=(const self_t& rhs) const noexcept
{
return _iter >= rhs._iter;
}
self_t operator+(difference_type d) const noexcept
{
return _iter + d;
}
self_t operator-(difference_type d) const noexcept
{
return _iter - d;
}
auto operator-(const self_t& rhs) const noexcept
{
return _iter - rhs._iter;
}
self_t& operator+=(difference_type d)
{
_iter += d;
return *this;
}
self_t& operator-=(difference_type d)
{
_iter -= d;
return *this;
}
operator wrapped&()
{
return _iter;
}
operator const wrapped&() const
{
return _iter;
}
wrapped& data()
{
return _iter;
}
const wrapped& data() const
{
return _iter;
}
private:
wrapped _iter;
};
template<typename Iter>
auto operator+(typename double_iterator<Iter>::difference_type diff, double_iterator<Iter> iter)
{
return iter + diff;
}
/// To avoid clients being moved, they are stored in unique_ptrs, which are
/// moved around in a vector. This class is purely for convenience, to still
/// have iterator semantics, and a few other utility functions
template<typename T>
struct ptr_vec {
using value_type = T;
std::vector<std::unique_ptr<value_type>> _order;
using iterator = double_iterator<typename decltype(_order)::iterator>;
using const_iterator = double_iterator<typename decltype(_order)::const_iterator>;
using reverse_iterator = double_iterator<typename decltype(_order)::reverse_iterator>;
using const_reverse_iterator =
double_iterator<typename decltype(_order)::const_reverse_iterator>;
value_type& push_back(const value_type& v)
{
auto ptr = std::make_unique<value_type>(v);
auto res = ptr.get();
_order.push_back(std::move(ptr));
return *res;
}
value_type& push_back(value_type&& v)
{
auto ptr = std::make_unique<value_type>(std::move(v));
auto res = ptr.get();
_order.push_back(std::move(ptr));
return *res;
}
value_type& push_back(std::unique_ptr<value_type> ptr)
{
auto res = ptr.get();
_order.push_back(std::move(ptr));
return *res;
}
template<typename... Args>
value_type& emplace_back(Args&&... args)
{
return push_back(std::make_unique<value_type>(std::forward<Args>(args)...));
}
std::unique_ptr<value_type> erase(const value_type& v)
{
auto iter =
std::find_if(_order.begin(), _order.end(), [&v](auto&& uptr) { return uptr.get() == &v; });
if (iter != _order.end()) {
auto uptr = std::move(*iter);
_order.erase(iter);
return uptr;
}
return nullptr;
}
iterator rotate_to_back(const value_type& v)
{
auto iter =
std::find_if(_order.begin(), _order.end(), [&v](auto&& uptr) { return uptr.get() == &v; });
return rotate_to_back(iter);
}
iterator rotate_to_back(iterator iter)
{
if (iter != _order.end()) {
{
return std::rotate(iter.data(), iter.data() + 1, _order.end());
}
}
return end();
}
iterator rotate_to_front(const value_type& v)
{
auto iter =
std::find_if(_order.begin(), _order.end(), [&v](auto&& uptr) { return uptr.get() == &v; });
return rotate_to_front(iter);
}
iterator rotate_to_front(iterator iter)
{
if (iter != _order.end()) {
{
return std::rotate(_order.begin(), iter.data(), iter.data() + 1);
}
}
return end();
}
std::size_t size() const noexcept
{
return _order.size();
}
bool empty() const noexcept
{
return _order.empty();
}
std::size_t capacity() const noexcept
{
return _order.capacity();
}
std::size_t max_size() const noexcept
{
return _order.max_size();
}
void reserve(std::size_t new_cap)
{
_order.reserve(new_cap);
}
void shrink_to_fit()
{
_order.shrink_to_fit();
}
value_type& operator[](std::size_t n)
{
return *_order[n];
}
const value_type& operator[](std::size_t n) const
{
return *_order[n];
}
value_type& at(std::size_t n)
{
return *_order.at(n);
}
const value_type& at(std::size_t n) const
{
return *_order.at(n);
}
iterator begin()
{
return _order.begin();
}
iterator end()
{
return _order.end();
}
const_iterator begin() const
{
return _order.begin();
}
const_iterator end() const
{
return _order.end();
}
reverse_iterator rbegin()
{
return _order.rbegin();
}
reverse_iterator rend()
{
return _order.rend();
}
const_reverse_iterator rbegin() const
{
return _order.rbegin();
}
const_reverse_iterator rend() const
{
return _order.rend();
}
value_type& front()
{
return *_order.front();
}
value_type& back()
{
return *_order.back();
}
const value_type& front() const
{
return *_order.front();
}
const value_type& back() const
{
return *_order.back();
}
std::vector<std::unique_ptr<value_type>>& underlying() {
return _order;
}
};
template<typename T, typename T2>
std::unique_ptr<T> erase_this(ptr_vec<T>& vec, T2* el)
{
return vec.erase(*el);
}
template<typename T, typename T2>
std::unique_ptr<T> erase_this(ptr_vec<T>& vec, T2& el)
{
return vec.erase(el);
}
template<typename T>
struct non_null_ptr {
non_null_ptr() = delete;
constexpr non_null_ptr(T* ptr) : _ptr(ptr)
{
assert(ptr != nullptr);
}
non_null_ptr(std::nullptr_t) = delete;
constexpr non_null_ptr(const non_null_ptr&) = default;
constexpr non_null_ptr(non_null_ptr&&) = default;
constexpr non_null_ptr& operator=(const non_null_ptr&) = default;
constexpr non_null_ptr& operator=(non_null_ptr&&) = default;
constexpr T& operator*() const noexcept
{
return *_ptr;
}
constexpr T* operator->() const noexcept
{
return _ptr;
}
constexpr operator T*() noexcept
{
return _ptr;
}
constexpr operator T* const() const noexcept
{
return _ptr;
}
private:
T* _ptr;
};
template<typename T>
struct ref_vec {
using value_type = T;
std::vector<value_type*> _order;
using iterator = double_iterator<typename decltype(_order)::iterator>;
using const_iterator = double_iterator<typename decltype(_order)::const_iterator>;
using reverse_iterator = double_iterator<typename decltype(_order)::reverse_iterator>;
using const_reverse_iterator =
double_iterator<typename decltype(_order)::const_reverse_iterator>;
ref_vec() = default;
ref_vec(std::initializer_list<value_type*> lst) : _order {lst} { };
template<typename InputIter, typename = std::enable_if_t<std::is_same_v<decltype(*std::declval<InputIter>()), value_type&>>>
ref_vec(InputIter iter1, InputIter iter2) {
_order.reserve(std::distance(iter1, iter2));
std::transform(iter1, iter2, std::back_inserter(_order), [] (auto& v) {return &v; });
}
template<typename Range, typename = std::enable_if_t<std::is_same_v<decltype(*std::declval<Range>().begin()), value_type&>>>
ref_vec(Range&& rng) : ref_vec (std::begin(rng), std::end(rng)) { }
value_type& push_back(value_type& v)
{
_order.push_back(&v);
return v;
}
value_type& push_back(non_null_ptr<value_type> ptr)
{
_order.push_back(ptr);
return *ptr;
}
value_type& emplace_back(value_type& v)
{
return push_back(v);
}
std::unique_ptr<value_type> erase(const value_type& v)
{
auto iter =
std::find_if(_order.begin(), _order.end(), [&v](auto&& ptr) { return ptr == &v; });
if (iter != _order.end()) {
auto uptr = std::move(*iter);
_order.erase(iter);
return uptr;
}
return nullptr;
}
iterator rotate_to_back(const value_type& v)
{
auto iter =
std::find_if(_order.begin(), _order.end(), [&v](auto&& ptr) { return ptr == &v; });
return rotate_to_back(iter);
}
iterator rotate_to_back(iterator iter)
{
if (iter != _order.end()) {
{
return std::rotate(iter.data(), iter.data() + 1, _order.end());
}
}
return end();
}
iterator rotate_to_front(const value_type& v)
{
auto iter =
std::find_if(_order.begin(), _order.end(), [&v](auto&& ptr) { return ptr == &v; });
return rotate_to_front(iter);
}
iterator rotate_to_front(iterator iter)
{
if (iter != _order.end()) {
{
return std::rotate(_order.begin(), iter.data(), iter.data() + 1);
}
}
return end();
}
std::size_t size() const noexcept
{
return _order.size();
}
bool empty() const noexcept
{
return _order.empty();
}
std::size_t capacity() const noexcept
{
return _order.capacity();
}
std::size_t max_size() const noexcept
{
return _order.max_size();
}
void reserve(std::size_t new_cap)
{
_order.reserve(new_cap);
}
void shrink_to_fit()
{
_order.shrink_to_fit();
}
value_type& operator[](std::size_t n)
{
return *_order[n];
}
const value_type& operator[](std::size_t n) const
{
return *_order[n];
}
value_type& at(std::size_t n)
{
return *_order.at(n);
}
const value_type& at(std::size_t n) const
{
return *_order.at(n);
}
iterator begin()
{
return _order.begin();
}
iterator end()
{
return _order.end();
}
const_iterator begin() const
{
return _order.begin();
}
const_iterator end() const
{
return _order.end();
}
reverse_iterator rbegin()
{
return _order.rbegin();
}
reverse_iterator rend()
{
return _order.rend();
}
const_reverse_iterator rbegin() const
{
return _order.rbegin();
}
const_reverse_iterator rend() const
{
return _order.rend();
}
value_type& front()
{
return *_order.front();
}
value_type& back()
{
return *_order.back();
}
const value_type& front() const
{
return *_order.front();
}
const value_type& back() const
{
return *_order.back();
}
std::vector<value_type*>& underlying() {
return _order;
}
};
} // namespace waybar::util

View File

@ -1,6 +1,6 @@
project(
'waybar', 'cpp', 'c',
version: '0.0.1',
version: '0.0.4',
license: 'MIT',
default_options : ['cpp_std=c++17'],
)
@ -37,9 +37,11 @@ libpulse = dependency('libpulse')
subdir('protocol')
src_files = run_command('find', './src', '-name', '*.cpp').stdout().strip().split('\n')
executable(
'waybar',
run_command('find', './src', '-name', '*.cpp').stdout().strip().split('\n'),
src_files,
dependencies: [
thread_dep,
wlroots,
@ -64,3 +66,15 @@ install_data(
'./resources/style.css',
install_dir: '/etc/xdg/waybar',
)
clangtidy = find_program('clang-tidy', required: false)
if clangtidy.found()
run_target(
'tidy',
command: [
clangtidy,
'-checks=*,-fuchsia-default-arguments',
'-p', meson.build_root()
] + src_files)
endif

View File

@ -1,49 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="idle">
<copyright><![CDATA[
Copyright (C) 2015 Martin Gräßlin
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
]]></copyright>
<interface name="org_kde_kwin_idle" version="1">
<description summary="User idle time manager">
This interface allows to monitor user idle time on a given seat. The interface
allows to register timers which trigger after no user activity was registered
on the seat for a given interval. It notifies when user activity resumes.
This is useful for applications wanting to perform actions when the user is not
interacting with the system, e.g. chat applications setting the user as away, power
management features to dim screen, etc..
</description>
<request name="get_idle_timeout">
<arg name="id" type="new_id" interface="org_kde_kwin_idle_timeout"/>
<arg name="seat" type="object" interface="wl_seat"/>
<arg name="timeout" type="uint" summary="The idle timeout in msec"/>
</request>
</interface>
<interface name="org_kde_kwin_idle_timeout" version="1">
<request name="release" type="destructor">
<description summary="release the timeout object"/>
</request>
<request name="simulate_user_activity">
<description summary="Simulates user activity for this timeout, behaves just like real user activity on the seat"/>
</request>
<event name="idle">
<description summary="Triggered when there has not been any user activity in the requested idle time interval"/>
</event>
<event name="resumed">
<description summary="Triggered on the first user activity after an idle event"/>
</event>
</interface>
</protocol>

View File

@ -23,8 +23,8 @@ wayland_scanner_client = generator(
client_protocols = [
[wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'],
[wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'],
['wlr-layer-shell-unstable-v1.xml'],
['idle.xml'],
]
client_protos_src = []

View File

@ -1,6 +1,24 @@
{
"modules-left": ["workspaces", "custom/spotify"],
// "layer": "top", // Waybar at top layer
// "position": "bottom", // Waybar at the bottom of your screen
// "height": 30, // Waybar height
// "width": 1280, // Waybar width
// Choose the order of the modules
"modules-left": ["sway/workspaces", "custom/spotify"],
"modules-center": ["sway/window"],
"modules-right": ["pulseaudio", "network", "cpu", "memory", "battery", "clock"],
// Modules configuration
"sway/workspaces": {
// "disable-scroll": true,
// "all-outputs": true,
// "format-icons": {
// "1": "",
// "2": "",
// "3": "",
// "4": "",
// "5": ""
// }
},
"cpu": {
"format": "{}% "
},
@ -8,18 +26,21 @@
"format": "{}% "
},
"battery": {
"format": "{}% "
"format": "{capacity}% {icon}",
"format-icons": ["", "", "", "", ""]
},
"network": {
"interface": "wlp2s0",
"format": "{essid} ({signalStrength}%) "
},
"pulseaudio": {
"format": "{}% ",
"format-muted": ""
"format": "{volume}% {icon}",
"format-muted": "",
"format-icons": ["", ""]
},
"custom/spotify": {
"format": " {}",
"max-length": 40,
"exec": "$HOME/.bin/mediaplayer.sh"
}
}

View File

@ -11,61 +11,82 @@ window {
color: white;
}
.workspaces button {
#workspaces button {
padding: 0 5px;
background: transparent;
color: white;
border-bottom: 3px solid transparent;
}
.workspaces button.current {
#workspaces button.icon label {
font-size: 10px;
}
#workspaces button.focused {
background: #64727D;
border-bottom: 3px solid white;
}
.clock, .battery, .cpu, .memory, .network, .pulseaudio, .custom-spotify {
#clock, #battery, #cpu, #memory, #network, #pulseaudio, #custom-spotify {
padding: 0 10px;
margin: 0 5px;
}
.clock {
#clock {
background-color: #64727D;
}
.battery {
#battery {
background-color: #ffffff;
color: black;
}
.battery.charging {
#battery.charging {
color: white;
background-color: #26A65B;
}
.cpu {
@keyframes blink {
to {
background-color: #ffffff;
color: black;
}
}
#battery.warning {
background: #f53c3c;
color: white;
animation-name: blink;
animation-duration: 0.5s;
animation-timing-function: linear;
animation-iteration-count: infinite;
animation-direction: alternate;
}
#cpu {
background: #2ecc71;
color: #000000;
}
.memory {
#memory {
background: #9b59b6;
}
.network {
#network {
background: #2980b9;
}
.pulseaudio {
#pulseaudio {
background: #f1c40f;
color: black;
}
.pulseaudio.muted {
#pulseaudio.muted {
background: #90b1b1;
color: #2a5c45;
}
.custom-spotify {
#custom-spotify {
background: #66cc99;
color: #2a5c45;
}

View File

@ -1,154 +1,160 @@
#include <condition_variable>
#include <gdk/gdkwayland.h>
#include <thread>
#include <fstream>
#include "bar.hpp"
#include "client.hpp"
#include "factory.hpp"
#include "util/chrono.hpp"
#include "util/json.hpp"
waybar::Bar::Bar(Client &client, std::unique_ptr<struct wl_output *> &&p_output)
: client(client), window{Gtk::WindowType::WINDOW_TOPLEVEL},
output(std::move(p_output))
{
static const struct wl_output_listener outputListener = {
.geometry = _handleGeometry,
.mode = _handleMode,
.done = _handleDone,
.scale = _handleScale,
static const struct zxdg_output_v1_listener xdgOutputListener = {
.logical_position = handleLogicalPosition,
.logical_size = handleLogicalSize,
.done = handleDone,
.name = handleName,
.description = handleDescription,
};
wl_output_add_listener(*output, &outputListener, this);
xdg_output_ =
zxdg_output_manager_v1_get_xdg_output(client.xdg_output_manager, *output);
zxdg_output_v1_add_listener(xdg_output_, &xdgOutputListener, this);
window.set_title("waybar");
window.set_decorated(false);
_setupConfig();
_setupCss();
_setupWidgets();
bool positionBottom = (_config["position"] == "bottom");
gtk_widget_realize(GTK_WIDGET(window.gobj()));
GdkWindow *gdkWindow = gtk_widget_get_window(GTK_WIDGET(window.gobj()));
gdk_wayland_window_set_use_custom_surface(gdkWindow);
surface = gdk_wayland_window_get_wl_surface(gdkWindow);
layerSurface = zwlr_layer_shell_v1_get_layer_surface(
client.layer_shell, surface, *output,
(positionBottom ? ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM : ZWLR_LAYER_SHELL_V1_LAYER_TOP),
"waybar");
zwlr_layer_surface_v1_set_anchor(layerSurface,
ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
(positionBottom ? ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM : ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP));
zwlr_layer_surface_v1_set_size(layerSurface, _width, client.height);
static const struct zwlr_layer_surface_v1_listener layerSurfaceListener = {
.configure = _layerSurfaceHandleConfigure,
.closed = _layerSurfaceHandleClosed,
setupConfig();
setupCss();
setupWidgets();
if (config_["height"]) {
height_ = config_["height"].asUInt();
}
Gtk::Widget& wrap(window);
gtk_widget_realize(wrap.gobj());
GdkWindow *gdk_window = gtk_widget_get_window(wrap.gobj());
gdk_wayland_window_set_use_custom_surface(gdk_window);
surface = gdk_wayland_window_get_wl_surface(gdk_window);
std::size_t layer_top = config_["layer"] == "top"
? ZWLR_LAYER_SHELL_V1_LAYER_TOP : ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM;
layer_surface = zwlr_layer_shell_v1_get_layer_surface(
client.layer_shell, surface, *output, layer_top, "waybar");
std::size_t position_bottom = config_["position"] == "bottom"
? ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM : ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP;
zwlr_layer_surface_v1_set_anchor(layer_surface, position_bottom
| ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT);
zwlr_layer_surface_v1_set_size(layer_surface, width_, height_);
static const struct zwlr_layer_surface_v1_listener layer_surface_listener = {
.configure = layerSurfaceHandleConfigure,
.closed = layerSurfaceHandleClosed,
};
zwlr_layer_surface_v1_add_listener(layerSurface, &layerSurfaceListener,
this);
zwlr_layer_surface_v1_add_listener(layer_surface,
&layer_surface_listener, this);
wl_surface_commit(surface);
}
void waybar::Bar::_handleGeometry(void *data, struct wl_output *wl_output,
int32_t x, int32_t y, int32_t physical_width, int32_t physical_height,
int32_t subpixel, const char *make, const char *model, int32_t transform)
void waybar::Bar::handleLogicalPosition(void* /*data*/,
struct zxdg_output_v1* /*zxdg_output_v1*/, int32_t /*x*/, int32_t /*y*/)
{
// Nothing here
}
void waybar::Bar::_handleMode(void *data, struct wl_output *wl_output,
uint32_t f, int32_t w, int32_t h, int32_t refresh)
{
auto o = reinterpret_cast<waybar::Bar *>(data);
o->setWidth(w);
}
void waybar::Bar::_handleDone(void *data, struct wl_output *)
void waybar::Bar::handleLogicalSize(void* /*data*/,
struct zxdg_output_v1* /*zxdg_output_v1*/, int32_t /*width*/,
int32_t /*height*/)
{
// Nothing here
}
void waybar::Bar::_handleScale(void *data, struct wl_output *wl_output,
int32_t factor)
void waybar::Bar::handleDone(void* /*data*/,
struct zxdg_output_v1* /*zxdg_output_v1*/)
{
// Nothing here
}
void waybar::Bar::_layerSurfaceHandleConfigure(
void *data, struct zwlr_layer_surface_v1 *surface, uint32_t serial,
uint32_t width, uint32_t height)
void waybar::Bar::handleName(void* data, struct zxdg_output_v1* /*xdg_output*/,
const char* name)
{
auto o = reinterpret_cast<waybar::Bar *>(data);
auto o = static_cast<waybar::Bar *>(data);
o->outputName = name;
}
void waybar::Bar::handleDescription(void* /*data*/,
struct zxdg_output_v1* /*zxdg_output_v1*/, const char* /*description*/)
{
// Nothing here
}
void waybar::Bar::layerSurfaceHandleConfigure(void* data,
struct zwlr_layer_surface_v1* surface, uint32_t serial, uint32_t width,
uint32_t height)
{
auto o = static_cast<waybar::Bar *>(data);
o->window.show_all();
o->setWidth(o->config_["width"] ? o->config_["width"].asUInt() : width);
zwlr_layer_surface_v1_ack_configure(surface, serial);
if (o->client.height != height)
{
height = o->client.height;
if (o->height_ != height) {
height = o->height_;
std::cout << fmt::format("New Height: {}", height) << std::endl;
zwlr_layer_surface_v1_set_size(surface, width, height);
zwlr_layer_surface_v1_set_size(surface, o->width_, height);
zwlr_layer_surface_v1_set_exclusive_zone(surface, o->visible ? height : 0);
wl_surface_commit(o->surface);
}
}
void waybar::Bar::_layerSurfaceHandleClosed(void *data,
struct zwlr_layer_surface_v1 *surface)
void waybar::Bar::layerSurfaceHandleClosed(void* data,
struct zwlr_layer_surface_v1* /*surface*/)
{
auto o = reinterpret_cast<waybar::Bar *>(data);
zwlr_layer_surface_v1_destroy(o->layerSurface);
o->layerSurface = nullptr;
auto o = static_cast<waybar::Bar *>(data);
zwlr_layer_surface_v1_destroy(o->layer_surface);
o->layer_surface = nullptr;
wl_surface_destroy(o->surface);
o->surface = nullptr;
o->window.close();
}
auto waybar::Bar::setWidth(int width) -> void
auto waybar::Bar::setWidth(uint32_t width) -> void
{
if (width == this->_width) return;
if (width == width_) {
return;
}
std::cout << fmt::format("Bar width configured: {}", width) << std::endl;
this->_width = width;
width_ = width;
window.set_size_request(width);
window.resize(width, client.height);
zwlr_layer_surface_v1_set_size(layerSurface, width, 40);
window.resize(width, height_);
zwlr_layer_surface_v1_set_size(layer_surface, width, height_ + 1);
wl_surface_commit(surface);
}
auto waybar::Bar::toggle() -> void
{
visible = !visible;
auto zone = visible ? client.height : 0;
zwlr_layer_surface_v1_set_exclusive_zone(layerSurface, zone);
auto zone = visible ? height_ : 0;
zwlr_layer_surface_v1_set_exclusive_zone(layer_surface, zone);
wl_surface_commit(surface);
}
auto waybar::Bar::_setupConfig() -> void
auto waybar::Bar::setupConfig() -> void
{
Json::Value root;
Json::CharReaderBuilder builder;
Json::CharReader* reader = builder.newCharReader();
std::string err;
std::ifstream file(client.configFile);
if (!file.is_open())
util::JsonParser parser;
std::ifstream file(client.config_file);
if (!file.is_open()) {
throw std::runtime_error("Can't open config file");
}
std::string str((std::istreambuf_iterator<char>(file)),
std::istreambuf_iterator<char>());
bool res = reader->parse(str.c_str(), str.c_str() + str.size(), &_config, &err);
delete reader;
if (!res)
throw std::runtime_error(err);
config_ = parser.parse(str);
}
auto waybar::Bar::_setupCss() -> void
auto waybar::Bar::setupCss() -> void
{
_cssProvider = Gtk::CssProvider::create();
_styleContext = Gtk::StyleContext::create();
css_provider_ = Gtk::CssProvider::create();
style_context_ = Gtk::StyleContext::create();
// load our css file, wherever that may be hiding
if (_cssProvider->load_from_path(client.cssFile)) {
if (css_provider_->load_from_path(client.css_file)) {
Glib::RefPtr<Gdk::Screen> screen = window.get_screen();
_styleContext->add_provider_for_screen(screen, _cssProvider,
style_context_->add_provider_for_screen(screen, css_provider_,
GTK_STYLE_PROVIDER_PRIORITY_USER);
}
}
auto waybar::Bar::_setupWidgets() -> void
auto waybar::Bar::setupWidgets() -> void
{
auto &left = *Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 0));
auto &center = *Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 0));
@ -161,25 +167,31 @@ auto waybar::Bar::_setupWidgets() -> void
box1.pack_start(center, false, false);
box1.pack_end(right, true, true);
Factory factory(*this, _config);
Factory factory(*this, config_);
if (_config["modules-left"]) {
for (auto name : _config["modules-left"]) {
auto &module = factory.makeModule(name.asString());
left.pack_start(module, false, true, 0);
if (config_["modules-left"]) {
for (const auto &name : config_["modules-left"]) {
auto module = factory.makeModule(name.asString());
if (module != nullptr) {
left.pack_start(*module, false, true, 0);
}
}
}
if (_config["modules-center"]) {
for (auto name : _config["modules-center"]) {
auto &module = factory.makeModule(name.asString());
center.pack_start(module, true, false, 10);
if (config_["modules-center"]) {
for (const auto &name : config_["modules-center"]) {
auto module = factory.makeModule(name.asString());
if (module != nullptr) {
center.pack_start(*module, true, false, 10);
}
}
}
if (_config["modules-right"]) {
std::reverse(_config["modules-right"].begin(), _config["modules-right"].end());
for (auto name : _config["modules-right"]) {
auto &module = factory.makeModule(name.asString());
right.pack_end(module, false, false, 0);
if (config_["modules-right"]) {
std::reverse(config_["modules-right"].begin(), config_["modules-right"].end());
for (const auto &name : config_["modules-right"]) {
auto module = factory.makeModule(name.asString());
if (module != nullptr) {
right.pack_end(*module, false, false, 0);
}
}
}
}

View File

@ -1,35 +1,33 @@
#include "client.hpp"
waybar::Client::Client(int argc, char* argv[])
: gtk_main(argc, argv),
gdk_display(Gdk::Display::get_default()),
wlDisplay(gdk_wayland_display_get_wl_display(gdk_display->gobj()))
: gtk_main(argc, argv), gdk_display(Gdk::Display::get_default()),
wl_display(gdk_wayland_display_get_wl_display(gdk_display->gobj()))
{
auto getFirstValidPath = [] (std::vector<std::string> possiblePaths) {
wordexp_t p;
for (std::string path: possiblePaths) {
for (const std::string &path: possiblePaths) {
if (wordexp(path.c_str(), &p, 0) == 0) {
if (access(p.we_wordv[0], F_OK) == 0) {
std::string result = p.we_wordv[0];
if (access(*p.we_wordv, F_OK) == 0) {
std::string result = *p.we_wordv;
wordfree(&p);
return result;
} else {
wordfree(&p);
}
wordfree(&p);
}
}
return std::string();
};
configFile = getFirstValidPath({
config_file = getFirstValidPath({
"$XDG_CONFIG_HOME/waybar/config",
"$HOME/waybar/config",
"/etc/xdg/waybar/config",
"./resources/config",
});
cssFile = getFirstValidPath({
css_file = getFirstValidPath({
"$XDG_CONFIG_HOME/waybar/style.css",
"$HOME/waybar/style.css",
"/etc/xdg/waybar/style.css",
@ -38,47 +36,51 @@ waybar::Client::Client(int argc, char* argv[])
}
void waybar::Client::_handle_global(void *data, struct wl_registry *registry,
void waybar::Client::handleGlobal(void *data, struct wl_registry *registry,
uint32_t name, const char *interface, uint32_t version)
{
auto o = reinterpret_cast<waybar::Client *>(data);
if (!strcmp(interface, zwlr_layer_shell_v1_interface.name)) {
o->layer_shell = (zwlr_layer_shell_v1 *)wl_registry_bind(registry, name,
&zwlr_layer_shell_v1_interface, version);
} else if (!strcmp(interface, wl_output_interface.name)) {
auto o = static_cast<waybar::Client *>(data);
if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
o->layer_shell = static_cast<struct zwlr_layer_shell_v1 *>(
wl_registry_bind(registry, name, &zwlr_layer_shell_v1_interface, version));
} else if (strcmp(interface, wl_output_interface.name) == 0) {
auto output = std::make_unique<struct wl_output *>();
*output = (struct wl_output *)wl_registry_bind(registry, name,
&wl_output_interface, version);
o->bars.emplace_back(*o, std::move(output));
} else if (!strcmp(interface, org_kde_kwin_idle_interface.name)) {
o->idle_manager = (org_kde_kwin_idle *)wl_registry_bind(registry, name,
&org_kde_kwin_idle_interface, version);
} else if (!strcmp(interface, wl_seat_interface.name)) {
o->seat = (struct wl_seat *)wl_registry_bind(registry, name,
&wl_seat_interface, version);
*output = static_cast<struct wl_output *>(wl_registry_bind(registry, name,
&wl_output_interface, version));
if (o->xdg_output_manager != nullptr) {
o->bars.emplace_back(std::make_unique<Bar>(*o, std::move(output)));
}
} else if (strcmp(interface, wl_seat_interface.name) == 0) {
o->seat = static_cast<struct wl_seat *>(wl_registry_bind(registry, name,
&wl_seat_interface, version));
} else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0
&& version >= ZXDG_OUTPUT_V1_NAME_SINCE_VERSION) {
o->xdg_output_manager = static_cast<struct zxdg_output_manager_v1 *>(
wl_registry_bind(registry, name,
&zxdg_output_manager_v1_interface, ZXDG_OUTPUT_V1_NAME_SINCE_VERSION));
}
}
void waybar::Client::_handle_global_remove(void *data,
struct wl_registry *registry, uint32_t name)
void waybar::Client::handleGlobalRemove(void* /*data*/,
struct wl_registry* /*registry*/, uint32_t /*name*/)
{
// TODO
// TODO(someone)
}
void waybar::Client::bind_interfaces()
void waybar::Client::bindInterfaces()
{
registry = wl_display_get_registry(wlDisplay);
registry = wl_display_get_registry(wl_display);
static const struct wl_registry_listener registry_listener = {
.global = _handle_global,
.global_remove = _handle_global_remove,
.global = handleGlobal,
.global_remove = handleGlobalRemove,
};
wl_registry_add_listener(registry, &registry_listener, this);
wl_display_roundtrip(wlDisplay);
wl_display_roundtrip(wl_display);
}
int waybar::Client::main(int argc, char* argv[])
int waybar::Client::main(int /*argc*/, char* /*argv*/[])
{
bind_interfaces();
bindInterfaces();
gtk_main.run();
return 0;
}

View File

@ -1,26 +1,46 @@
#include "factory.hpp"
waybar::Factory::Factory(Bar &bar, Json::Value config)
: _bar(bar), _config(config)
: _bar(bar), _config(std::move(config))
{}
waybar::IModule &waybar::Factory::makeModule(std::string name)
waybar::IModule *waybar::Factory::makeModule(const std::string &name)
{
if (name == "battery")
return *new waybar::modules::Battery(_config[name]);
if (name == "workspaces")
return *new waybar::modules::Workspaces(_bar);
if (name == "memory")
return *new waybar::modules::Memory(_config[name]);
if (name == "cpu")
return *new waybar::modules::Cpu(_config[name]);
if (name == "clock")
return *new waybar::modules::Clock(_config[name]);
if (name == "network")
return *new waybar::modules::Network(_config[name]);
if (name == "pulseaudio")
return *new waybar::modules::Pulseaudio(_config[name]);
if (!name.compare(0, 7, "custom/") && name.size() > 7)
return *new waybar::modules::Custom(name.substr(7), _config[name]);
throw std::runtime_error("Unknown module: " + name);
try {
if (name == "battery") {
return new waybar::modules::Battery(_config[name]);
}
if (name == "sway/workspaces") {
return new waybar::modules::sway::Workspaces(_bar, _config[name]);
}
if (name == "sway/window") {
return new waybar::modules::sway::Window(_bar, _config[name]);
}
if (name == "memory") {
return new waybar::modules::Memory(_config[name]);
}
if (name == "cpu") {
return new waybar::modules::Cpu(_config[name]);
}
if (name == "clock") {
return new waybar::modules::Clock(_config[name]);
}
if (name == "network") {
return new waybar::modules::Network(_config[name]);
}
if (name == "pulseaudio") {
return new waybar::modules::Pulseaudio(_config[name]);
}
if (name.compare(0, 7, "custom/") == 0 && name.size() > 7) {
return new waybar::modules::Custom(name.substr(7), _config[name]);
}
std::cerr << "Unknown module: " + name << std::endl;
} catch (const std::exception& e) {
auto err = fmt::format("Disabling module \"{}\", {}", name, e.what());
std::cerr << err << std::endl;
} catch (...) {
auto err = fmt::format("Disabling module \"{}\", Unknown reason", name);
std::cerr << err << std::endl;
}
return nullptr;
}

View File

@ -1,101 +0,0 @@
#define _POSIX_C_SOURCE 200809L
#include <string>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/un.h>
#include "ipc/client.hpp"
static const char ipc_magic[] = {'i', '3', '-', 'i', 'p', 'c'};
static const size_t ipc_header_size = sizeof(ipc_magic)+8;
std::string get_socketpath(void) {
const char *env = getenv("SWAYSOCK");
if (env) return std::string(env);
std::string str;
{
std::string str_buf;
FILE* in;
char buf[512] = { 0 };
if (!(in = popen("sway --get-socketpath 2>/dev/null", "r"))) {
throw std::runtime_error("Failed to get socket path");
}
while (fgets(buf, sizeof(buf), in) != nullptr) {
str_buf.append(buf, sizeof(buf));
}
pclose(in);
str = str_buf;
}
if (str.back() == '\n') {
str.pop_back();
}
return str;
}
int ipc_open_socket(std::string socket_path) {
struct sockaddr_un addr;
int socketfd;
if ((socketfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
throw std::runtime_error("Unable to open Unix socket");
}
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, socket_path.c_str(), sizeof(addr.sun_path) - 1);
addr.sun_path[sizeof(addr.sun_path) - 1] = 0;
int l = sizeof(struct sockaddr_un);
if (connect(socketfd, (struct sockaddr *)&addr, l) == -1) {
throw std::runtime_error("Unable to connect to " + socket_path);
}
return socketfd;
}
struct ipc_response ipc_recv_response(int socketfd) {
char data[ipc_header_size];
uint32_t *data32 = (uint32_t *)(data + sizeof(ipc_magic));
size_t total = 0;
while (total < ipc_header_size) {
ssize_t received = recv(socketfd, data + total, ipc_header_size - total, 0);
if (received <= 0) {
throw std::runtime_error("Unable to receive IPC response");
}
total += received;
}
struct ipc_response response;
total = 0;
response.size = data32[0];
response.type = data32[1];
char payload[response.size + 1];
while (total < response.size) {
ssize_t received = recv(socketfd, payload + total, response.size - total, 0);
if (received < 0) {
throw std::runtime_error("Unable to receive IPC response");
}
total += received;
}
payload[response.size] = '\0';
response.payload = std::string(payload);
return response;
}
std::string ipc_single_command(int socketfd, uint32_t type, const char *payload, uint32_t *len) {
char data[ipc_header_size];
uint32_t *data32 = (uint32_t *)(data + sizeof(ipc_magic));
memcpy(data, ipc_magic, sizeof(ipc_magic));
data32[0] = *len;
data32[1] = type;
if (send(socketfd, data, ipc_header_size, 0) == -1) {
throw std::runtime_error("Unable to send IPC header");
}
if (send(socketfd, payload, *len, 0) == -1) {
throw std::runtime_error("Unable to send IPC payload");
}
struct ipc_response resp = ipc_recv_response(socketfd);
std::string response = resp.payload;
*len = resp.size;
return response;
}

View File

@ -1,12 +1,12 @@
#include <gtkmm.h>
#include <wayland-client.hpp>
#include <gdk/gdkwayland.h>
#include <csignal>
#include "client.hpp"
#include <csignal>
#include <iostream>
namespace waybar {
static Client* client;
}
static Client* client;
} // namespace waybar
int main(int argc, char* argv[])
{
@ -15,7 +15,7 @@ int main(int argc, char* argv[])
waybar::client = &c;
std::signal(SIGUSR1, [] (int signal) {
for (auto& bar : waybar::client->bars) {
bar.toggle();
(*bar).toggle();
}
});

View File

@ -1,56 +1,90 @@
#include "modules/battery.hpp"
waybar::modules::Battery::Battery(Json::Value config)
: _config(config)
: config_(std::move(config))
{
try {
for (auto &node : fs::directory_iterator(_data_dir)) {
for (auto &node : fs::directory_iterator(data_dir_)) {
if (fs::is_directory(node) && fs::exists(node / "capacity")
&& fs::exists(node / "status")) {
_batteries.push_back(node);
&& fs::exists(node / "status") && fs::exists(node / "uevent")) {
batteries_.push_back(node);
}
}
} catch (fs::filesystem_error &e) {
std::cerr << e.what() << std::endl;
throw std::runtime_error(e.what());
}
_label.get_style_context()->add_class("battery");
_thread = [this] {
update();
_thread.sleep_for(chrono::minutes(1));
if (batteries_.empty()) {
throw std::runtime_error("No batteries.");
}
auto fd = inotify_init();
if (fd == -1) {
throw std::runtime_error("Unable to listen batteries.");
}
for (auto &bat : batteries_) {
inotify_add_watch(fd, (bat / "uevent").c_str(), IN_ACCESS);
}
// Trigger first value
Glib::signal_idle().connect_once(sigc::mem_fun(*this, &Battery::update));
label_.set_name("battery");
thread_ = [this, fd] {
struct inotify_event event = {};
int nbytes = read(fd, &event, sizeof(event));
if (nbytes != sizeof(event)) {
return;
}
Glib::signal_idle().connect_once(sigc::mem_fun(*this, &Battery::update));
};
}
auto waybar::modules::Battery::update() -> void
{
try {
int total = 0;
bool charging = false;
for (auto &bat : _batteries) {
int capacity;
std::string status;
uint16_t total = 0;
std::string status;
for (auto &bat : batteries_) {
uint16_t capacity;
std::string _status;
std::ifstream(bat / "capacity") >> capacity;
total += capacity;
std::ifstream(bat / "status") >> status;
if (status == "Charging") {
charging = true;
std::ifstream(bat / "status") >> _status;
if (_status != "Unknown") {
status = _status;
}
total += capacity;
}
if (charging == true) {
_label.get_style_context()->add_class("charging");
uint16_t capacity = total / batteries_.size();
auto format = config_["format"]
? config_["format"].asString() : "{capacity}%";
label_.set_text(fmt::format(format, fmt::arg("capacity", capacity),
fmt::arg("icon", getIcon(capacity))));
label_.set_tooltip_text(status);
bool charging = status == "Charging";
if (charging) {
label_.get_style_context()->add_class("charging");
} else {
_label.get_style_context()->remove_class("charging");
label_.get_style_context()->remove_class("charging");
}
auto format = _config["format"] ? _config["format"].asString() : "{}%";
_label.set_text(fmt::format(format, total / _batteries.size()));
_label.set_tooltip_text(charging ? "Charging" : "Discharging");
} catch (std::exception &e) {
auto critical = config_["critical"] ? config_["critical"].asUInt() : 15;
if (capacity <= critical && !charging) {
label_.get_style_context()->add_class("warning");
} else {
label_.get_style_context()->remove_class("warning");
}
} catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
}
}
std::string waybar::modules::Battery::getIcon(uint16_t percentage)
{
if (!config_["format-icons"] || !config_["format-icons"].isArray()) {
return "";
}
auto size = config_["format-icons"].size();
auto idx = std::clamp(percentage / (100 / size), 0U, size - 1);
return config_["format-icons"][idx].asString();
}
waybar::modules::Battery::operator Gtk::Widget &()
{
return _label;
return label_;
}

View File

@ -1,27 +1,26 @@
#include "modules/clock.hpp"
waybar::modules::Clock::Clock(Json::Value config)
: _config(config)
: config_(std::move(config))
{
_label.get_style_context()->add_class("clock");
_thread = [this] {
update();
label_.set_name("clock");
uint32_t interval = config_["interval"] ? config_["inveral"].asUInt() : 60;
thread_ = [this, interval] {
auto now = waybar::chrono::clock::now();
auto timeout =
std::chrono::floor<std::chrono::minutes>(now + std::chrono::minutes(1));
_thread.sleep_until(timeout);
Glib::signal_idle().connect_once(sigc::mem_fun(*this, &Clock::update));
auto timeout = std::chrono::floor<std::chrono::seconds>(now
+ std::chrono::seconds(interval));
thread_.sleep_until(timeout);
};
};
auto waybar::modules::Clock::update() -> void
{
auto t = std::time(nullptr);
auto localtime = std::localtime(&t);
auto format =
_config["format"] ? _config["format"].asString() : "{:02}:{:02}";
_label.set_text(fmt::format(format, localtime->tm_hour, localtime->tm_min));
auto localtime = fmt::localtime(std::time(nullptr));
auto format = config_["format"] ? config_["format"].asString() : "{:%H:%M}";
label_.set_text(fmt::format(format, localtime));
}
waybar::modules::Clock::operator Gtk::Widget &() {
return _label;
return label_;
}

View File

@ -1,27 +1,27 @@
#include "modules/cpu.hpp"
waybar::modules::Cpu::Cpu(Json::Value config)
: _config(config)
: config_(std::move(config))
{
_label.get_style_context()->add_class("cpu");
_thread = [this] {
update();
int interval = _config["interval"] ? _config["inveral"].asInt() : 10;
_thread.sleep_for(chrono::seconds(interval));
label_.set_name("cpu");
uint32_t interval = config_["interval"] ? config_["inveral"].asUInt() : 10;
thread_ = [this, interval] {
Glib::signal_idle().connect_once(sigc::mem_fun(*this, &Cpu::update));
thread_.sleep_for(chrono::seconds(interval));
};
};
auto waybar::modules::Cpu::update() -> void
{
struct sysinfo info;
if (!sysinfo(&info)) {
float f_load = 1.f / (1 << SI_LOAD_SHIFT);
int load = info.loads[0] * f_load * 100 / get_nprocs();
auto format = _config["format"] ? _config["format"].asString() : "{}%";
_label.set_text(fmt::format(format, load));
struct sysinfo info = {};
if (sysinfo(&info) == 0) {
float f_load = 1.f / (1U << SI_LOAD_SHIFT);
uint16_t load = info.loads[0] * f_load * 100 / get_nprocs();
auto format = config_["format"] ? config_["format"].asString() : "{}%";
label_.set_text(fmt::format(format, load));
}
}
waybar::modules::Cpu::operator Gtk::Widget &() {
return _label;
return label_;
}

View File

@ -1,33 +1,37 @@
#include "modules/custom.hpp"
#include <iostream>
waybar::modules::Custom::Custom(std::string name, Json::Value config)
: _name(name), _config(config)
waybar::modules::Custom::Custom(const std::string &name, Json::Value config)
: name_(name), config_(std::move(config))
{
_label.get_style_context()->add_class("custom-" + name);
if (!_config["exec"]) {
std::cerr << name + " has no exec path." << std::endl;
return;
if (!config_["exec"]) {
throw std::runtime_error(name_ + " has no exec path.");
}
_thread = [this] {
update();
int interval = _config["interval"] ? _config["inveral"].asInt() : 30;
_thread.sleep_for(chrono::seconds(interval));
if (config_["max-length"]) {
label_.set_max_width_chars(config_["max-length"].asUInt());
label_.set_ellipsize(Pango::EllipsizeMode::ELLIPSIZE_END);
}
uint32_t interval = config_["interval"] ? config_["inveral"].asUInt() : 30;
thread_ = [this, interval] {
Glib::signal_idle().connect_once(sigc::mem_fun(*this, &Custom::update));
thread_.sleep_for(chrono::seconds(interval));
};
};
auto waybar::modules::Custom::update() -> void
{
std::array<char, 128> buffer;
std::array<char, 128> buffer = {0};
std::string output;
std::shared_ptr<FILE> fp(popen(_config["exec"].asCString(), "r"), pclose);
std::shared_ptr<FILE> fp(popen(config_["exec"].asCString(), "r"), pclose);
if (!fp) {
std::cerr << _name + " can't exec " + _config["exec"].asString() << std::endl;
std::cerr << name_ + " can't exec " + config_["exec"].asString() << std::endl;
return;
}
while (!feof(fp.get())) {
if (fgets(buffer.data(), 128, fp.get()) != nullptr)
while (feof(fp.get()) == 0) {
if (fgets(buffer.data(), 128, fp.get()) != nullptr) {
output += buffer.data();
}
}
// Remove last newline
@ -36,15 +40,19 @@ auto waybar::modules::Custom::update() -> void
}
// Hide label if output is empty
if (output.empty())
_label.hide();
else {
auto format = _config["format"] ? _config["format"].asString() : "{}";
_label.set_text(fmt::format(format, output));
_label.show();
if (output.empty()) {
label_.set_name("");
label_.hide();
} else {
label_.set_name("custom-" + name_);
auto format = config_["format"] ? config_["format"].asString() : "{}";
auto str = fmt::format(format, output);
label_.set_text(str);
label_.set_tooltip_text(str);
label_.show();
}
}
waybar::modules::Custom::operator Gtk::Widget &() {
return _label;
return label_;
}

View File

@ -1,30 +1,30 @@
#include "modules/memory.hpp"
waybar::modules::Memory::Memory(Json::Value config)
: _config(config)
: config_(std::move(config))
{
_label.get_style_context()->add_class("memory");
_thread = [this] {
update();
int interval = _config["interval"] ? _config["inveral"].asInt() : 30;
_thread.sleep_for(chrono::seconds(interval));
label_.set_name("memory");
uint32_t interval = config_["interval"] ? config_["inveral"].asUInt() : 30;
thread_ = [this, interval] {
Glib::signal_idle().connect_once(sigc::mem_fun(*this, &Memory::update));
thread_.sleep_for(chrono::seconds(interval));
};
};
auto waybar::modules::Memory::update() -> void
{
struct sysinfo info;
if (!sysinfo(&info)) {
struct sysinfo info = {};
if (sysinfo(&info) == 0) {
auto total = info.totalram * info.mem_unit;
auto freeram = info.freeram * info.mem_unit;
int used_ram_percentage = 100 * (total - freeram) / total;
auto format = _config["format"] ? _config["format"].asString() : "{}%";
_label.set_text(fmt::format(format, used_ram_percentage));
auto format = config_["format"] ? config_["format"].asString() : "{}%";
label_.set_text(fmt::format(format, used_ram_percentage));
auto used_ram_gigabytes = (total - freeram) / std::pow(1024, 3);
_label.set_tooltip_text(fmt::format("{:.{}f}Gb used", used_ram_gigabytes, 1));
label_.set_tooltip_text(fmt::format("{:.{}f}Gb used", used_ram_gigabytes, 1));
}
}
waybar::modules::Memory::operator Gtk::Widget &() {
return _label;
return label_;
}

View File

@ -1,27 +1,35 @@
#include "modules/network.hpp"
#include <iostream>
waybar::modules::Network::Network(Json::Value config)
: _config(config), _ifid(if_nametoindex(config["interface"].asString().c_str()))
: config_(std::move(config)),
ifid_(if_nametoindex(config_["interface"].asCString())),
signal_strength_dbm_(0), signal_strength_(0)
{
_label.get_style_context()->add_class("network");
_thread = [this] {
update();
_thread.sleep_for(chrono::minutes(1));
if (ifid_ == 0) {
throw std::runtime_error("Can't found network interface");
}
label_.set_name("network");
uint32_t interval = config_["interval"] ? config_["inveral"].asUInt() : 30;
thread_ = [this, interval] {
getInfo();
Glib::signal_idle().connect_once(sigc::mem_fun(*this, &Network::update));
thread_.sleep_for(chrono::seconds(interval));
};
};
auto waybar::modules::Network::update() -> void
{
_getInfo();
auto format = _config["format"] ? _config["format"].asString() : "{}";
_label.set_text(fmt::format(format,
fmt::arg("essid", _essid),
fmt::arg("signaldBm", _signalStrengthdBm),
fmt::arg("signalStrength", _signalStrength)
auto format = config_["format"] ? config_["format"].asString() : "{essid}";
label_.set_text(fmt::format(format,
fmt::arg("essid", essid_),
fmt::arg("signaldBm", signal_strength_dbm_),
fmt::arg("signalStrength", signal_strength_)
));
}
int waybar::modules::Network::_scanCb(struct nl_msg *msg, void *data) {
int waybar::modules::Network::scanCb(struct nl_msg *msg, void *data) {
auto net = static_cast<waybar::modules::Network *>(data);
auto gnlh = static_cast<genlmsghdr *>(nlmsg_data(nlmsg_hdr(msg)));
struct nlattr* tb[NL80211_ATTR_MAX + 1];
@ -37,23 +45,27 @@ int waybar::modules::Network::_scanCb(struct nl_msg *msg, void *data) {
bss_policy[NL80211_BSS_SIGNAL_UNSPEC].type = NLA_U8;
bss_policy[NL80211_BSS_STATUS].type = NLA_U32;
if (nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), nullptr) < 0)
if (nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), nullptr) < 0) {
return NL_SKIP;
if (!tb[NL80211_ATTR_BSS])
}
if (tb[NL80211_ATTR_BSS] == nullptr) {
return NL_SKIP;
if (nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS], bss_policy))
}
if (nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS], bss_policy) != 0) {
return NL_SKIP;
if (!net->_associatedOrJoined(bss))
}
if (!net->associatedOrJoined(bss)) {
return NL_SKIP;
net->_parseEssid(bss);
net->_parseSignal(bss);
// TODO: parse quality
}
net->parseEssid(bss);
net->parseSignal(bss);
// TODO(someone): parse quality
return NL_SKIP;
}
void waybar::modules::Network::_parseEssid(struct nlattr **bss)
void waybar::modules::Network::parseEssid(struct nlattr **bss)
{
_essid.clear();
essid_.clear();
if (bss[NL80211_BSS_INFORMATION_ELEMENTS] != nullptr) {
auto ies =
static_cast<char*>(nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]));
@ -66,29 +78,30 @@ void waybar::modules::Network::_parseEssid(struct nlattr **bss)
if (ies_len > hdr_len && ies_len > ies[1] + hdr_len) {
auto essid_begin = ies + hdr_len;
auto essid_end = essid_begin + ies[1];
std::copy(essid_begin, essid_end, std::back_inserter(_essid));
std::copy(essid_begin, essid_end, std::back_inserter(essid_));
}
}
}
void waybar::modules::Network::_parseSignal(struct nlattr **bss) {
void waybar::modules::Network::parseSignal(struct nlattr **bss) {
if (bss[NL80211_BSS_SIGNAL_MBM] != nullptr) {
// signalstrength in dBm
_signalStrengthdBm =
signal_strength_dbm_ =
static_cast<int>(nla_get_u32(bss[NL80211_BSS_SIGNAL_MBM])) / 100;
// WiFi-hardware usually operates in the range -90 to -20dBm.
const int hardwareMax = -20;
const int hardwareMin = -90;
_signalStrength = ((double)(_signalStrengthdBm - hardwareMin)
/ (double)(hardwareMax - hardwareMin)) * 100;
signal_strength_ = ((signal_strength_dbm_ - hardwareMin)
/ double{hardwareMax - hardwareMin}) * 100;
}
}
bool waybar::modules::Network::_associatedOrJoined(struct nlattr** bss)
bool waybar::modules::Network::associatedOrJoined(struct nlattr** bss)
{
if (!bss[NL80211_BSS_STATUS])
if (bss[NL80211_BSS_STATUS] == nullptr) {
return false;
}
auto status = nla_get_u32(bss[NL80211_BSS_STATUS]);
switch (status) {
case NL80211_BSS_STATUS_ASSOCIATED:
@ -100,16 +113,14 @@ bool waybar::modules::Network::_associatedOrJoined(struct nlattr** bss)
}
}
auto waybar::modules::Network::_getInfo() -> void
auto waybar::modules::Network::getInfo() -> void
{
if (_ifid == 0)
return;
struct nl_sock *sk = nl_socket_alloc();
if (genl_connect(sk) != 0) {
nl_socket_free(sk);
return;
}
if (nl_socket_modify_cb(sk, NL_CB_VALID, NL_CB_CUSTOM, _scanCb, this) < 0) {
if (nl_socket_modify_cb(sk, NL_CB_VALID, NL_CB_CUSTOM, scanCb, this) < 0) {
nl_socket_free(sk);
return;
}
@ -119,13 +130,13 @@ auto waybar::modules::Network::_getInfo() -> void
return;
}
struct nl_msg *msg = nlmsg_alloc();
if (!msg) {
if (msg == nullptr) {
nl_socket_free(sk);
return;
}
if (!genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, nl80211_id, 0,
NLM_F_DUMP, NL80211_CMD_GET_SCAN, 0) ||
nla_put_u32(msg, NL80211_ATTR_IFINDEX, _ifid) < 0) {
if (genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, nl80211_id, 0, NLM_F_DUMP,
NL80211_CMD_GET_SCAN, 0) == nullptr
|| nla_put_u32(msg, NL80211_ATTR_IFINDEX, ifid_) < 0) {
nlmsg_free(msg);
return;
}
@ -134,5 +145,5 @@ auto waybar::modules::Network::_getInfo() -> void
}
waybar::modules::Network::operator Gtk::Widget &() {
return _label;
return label_;
}

View File

@ -1,41 +1,44 @@
#include "modules/pulseaudio.hpp"
waybar::modules::Pulseaudio::Pulseaudio(Json::Value config)
: _config(config), _mainloop(nullptr), _mainloop_api(nullptr),
_context(nullptr), _sinkIdx(0), _volume(0), _muted(false)
: config_(std::move(config)), mainloop_(nullptr), mainloop_api_(nullptr),
context_(nullptr), sink_idx_(0), volume_(0), muted_(false)
{
_label.get_style_context()->add_class("pulseaudio");
_mainloop = pa_threaded_mainloop_new();
if (!_mainloop)
label_.set_name("pulseaudio");
mainloop_ = pa_threaded_mainloop_new();
if (mainloop_ == nullptr) {
throw std::runtime_error("pa_mainloop_new() failed.");
pa_threaded_mainloop_lock(_mainloop);
_mainloop_api = pa_threaded_mainloop_get_api(_mainloop);
if (pa_signal_init(_mainloop_api) != 0)
throw std::runtime_error("pa_signal_init() failed.");
_context = pa_context_new(_mainloop_api, "waybar");
if (!_context)
}
pa_threaded_mainloop_lock(mainloop_);
mainloop_api_ = pa_threaded_mainloop_get_api(mainloop_);
context_ = pa_context_new(mainloop_api_, "waybar");
if (context_ == nullptr) {
throw std::runtime_error("pa_context_new() failed.");
if (pa_context_connect(_context, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL) < 0)
throw std::runtime_error(fmt::format("pa_context_connect() failed: {}",
pa_strerror(pa_context_errno(_context))));
pa_context_set_state_callback(_context, _contextStateCb, this);
if (pa_threaded_mainloop_start(_mainloop) < 0)
}
if (pa_context_connect(context_, nullptr, PA_CONTEXT_NOAUTOSPAWN,
nullptr) < 0) {
auto err = fmt::format("pa_context_connect() failed: {}",
pa_strerror(pa_context_errno(context_)));
throw std::runtime_error(err);
}
pa_context_set_state_callback(context_, contextStateCb, this);
if (pa_threaded_mainloop_start(mainloop_) < 0) {
throw std::runtime_error("pa_mainloop_run() failed.");
pa_threaded_mainloop_unlock(_mainloop);
}
pa_threaded_mainloop_unlock(mainloop_);
};
void waybar::modules::Pulseaudio::_contextStateCb(pa_context *c, void *data)
void waybar::modules::Pulseaudio::contextStateCb(pa_context *c, void *data)
{
auto pa = static_cast<waybar::modules::Pulseaudio *>(data);
switch (pa_context_get_state(c)) {
case PA_CONTEXT_TERMINATED:
pa->_mainloop_api->quit(pa->_mainloop_api, 0);
pa->mainloop_api_->quit(pa->mainloop_api_, 0);
break;
case PA_CONTEXT_READY:
pa_context_get_server_info(c, _serverInfoCb, data);
pa_context_set_subscribe_callback(c, _subscribeCb, data);
pa_context_subscribe(c, PA_SUBSCRIPTION_MASK_SINK, nullptr,
nullptr);
pa_context_get_server_info(c, serverInfoCb, data);
pa_context_set_subscribe_callback(c, subscribeCb, data);
pa_context_subscribe(c, PA_SUBSCRIPTION_MASK_SINK, nullptr, nullptr);
break;
case PA_CONTEXT_CONNECTING:
case PA_CONTEXT_AUTHORIZING:
@ -43,7 +46,7 @@ void waybar::modules::Pulseaudio::_contextStateCb(pa_context *c, void *data)
break;
case PA_CONTEXT_FAILED:
default:
pa->_mainloop_api->quit(pa->_mainloop_api, 1);
pa->mainloop_api_->quit(pa->mainloop_api_, 1);
break;
}
}
@ -51,38 +54,35 @@ void waybar::modules::Pulseaudio::_contextStateCb(pa_context *c, void *data)
/*
* Called when an event we subscribed to occurs.
*/
void waybar::modules::Pulseaudio::_subscribeCb(pa_context *context,
pa_subscription_event_type_t type, uint32_t idx, void *data)
void waybar::modules::Pulseaudio::subscribeCb(pa_context* context,
pa_subscription_event_type_t type, uint32_t idx, void* data)
{
unsigned facility = type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK;
pa_operation *op = nullptr;
switch (facility) {
case PA_SUBSCRIPTION_EVENT_SINK:
pa_context_get_sink_info_by_index(context, idx, _sinkInfoCb, data);
break;
default:
assert(0);
break;
case PA_SUBSCRIPTION_EVENT_SINK:
pa_context_get_sink_info_by_index(context, idx, sinkInfoCb, data);
break;
default:
break;
}
if (op)
pa_operation_unref(op);
}
/*
* Called when the requested sink information is ready.
*/
void waybar::modules::Pulseaudio::_sinkInfoCb(pa_context *context,
const pa_sink_info *i, int eol, void *data)
void waybar::modules::Pulseaudio::sinkInfoCb(pa_context* /*context*/,
const pa_sink_info* i, int /*eol*/, void* data)
{
if (i) {
if (i != nullptr) {
auto pa = static_cast<waybar::modules::Pulseaudio *>(data);
float volume = (float)pa_cvolume_avg(&(i->volume)) / (float)PA_VOLUME_NORM;
pa->_sinkIdx = i->index;
pa->_volume = volume * 100.0f;
pa->_muted = i->mute;
pa->_desc = i->description;
pa->update();
float volume = static_cast<float>(pa_cvolume_avg(&(i->volume)))
/ float{PA_VOLUME_NORM};
pa->sink_idx_ = i->index;
pa->volume_ = volume * 100.0f;
pa->muted_ = i->mute != 0;
pa->desc_ = i->description;
Glib::signal_idle().connect_once(sigc::mem_fun(*pa, &Pulseaudio::update));
}
}
@ -90,27 +90,40 @@ void waybar::modules::Pulseaudio::_sinkInfoCb(pa_context *context,
* Called when the requested information on the server is ready. This is
* used to find the default PulseAudio sink.
*/
void waybar::modules::Pulseaudio::_serverInfoCb(pa_context *context,
void waybar::modules::Pulseaudio::serverInfoCb(pa_context *context,
const pa_server_info *i, void *data)
{
pa_context_get_sink_info_by_name(context, i->default_sink_name, _sinkInfoCb,
data);
pa_context_get_sink_info_by_name(context, i->default_sink_name,
sinkInfoCb, data);
}
auto waybar::modules::Pulseaudio::update() -> void
{
auto format = _config["format"] ? _config["format"].asString() : "{}%";
if (_muted) {
auto format =
config_["format"] ? config_["format"].asString() : "{volume}%";
if (muted_) {
format =
_config["format-muted"] ? _config["format-muted"].asString() : format;
if (!_label.get_style_context()->has_class("muted"))
_label.get_style_context()->add_class("muted");
} else if (_label.get_style_context()->has_class("muted"))
_label.get_style_context()->remove_class("muted");
_label.set_label(fmt::format(format, _volume));
_label.set_tooltip_text(_desc);
config_["format-muted"] ? config_["format-muted"].asString() : format;
label_.get_style_context()->add_class("muted");
} else {
label_.get_style_context()->remove_class("muted");
}
label_.set_label(fmt::format(format,
fmt::arg("volume", volume_),
fmt::arg("icon", getIcon(volume_))));
label_.set_tooltip_text(desc_);
}
std::string waybar::modules::Pulseaudio::getIcon(uint16_t percentage)
{
if (!config_["format-icons"] || !config_["format-icons"].isArray()) {
return "";
}
auto size = config_["format-icons"].size();
auto idx = std::clamp(percentage / (100 / size), 0U, size - 1);
return config_["format-icons"][idx].asString();
}
waybar::modules::Pulseaudio::operator Gtk::Widget &() {
return _label;
return label_;
}

View File

@ -0,0 +1,99 @@
#define _POSIX_C_SOURCE 200809L
#include "modules/sway/ipc/client.hpp"
#include <cstdio>
#include <string>
#include <sys/socket.h>
#include <sys/un.h>
static const char ipc_magic[] = {'i', '3', '-', 'i', 'p', 'c'};
static const size_t ipc_header_size = sizeof(ipc_magic)+8;
std::string getSocketPath() {
const char *env = getenv("SWAYSOCK");
if (env != nullptr) {
return std::string(env);
}
std::string str;
{
std::string str_buf;
FILE* in;
char buf[512] = { 0 };
if ((in = popen("sway --get-socketpath 2>/dev/null", "r")) == nullptr) {
throw std::runtime_error("Failed to get socket path");
}
while (fgets(buf, sizeof(buf), in) != nullptr) {
str_buf.append(buf, sizeof(buf));
}
pclose(in);
str = str_buf;
}
if (str.back() == '\n') {
str.pop_back();
}
return str;
}
int ipcOpenSocket(const std::string &socketPath) {
struct sockaddr_un addr = {};
int socketfd;
if ((socketfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
throw std::runtime_error("Unable to open Unix socket");
}
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, socketPath.c_str(), sizeof(addr.sun_path) - 1);
addr.sun_path[sizeof(addr.sun_path) - 1] = 0;
int l = sizeof(struct sockaddr_un);
if (connect(socketfd, reinterpret_cast<struct sockaddr *>(&addr), l) == -1) {
throw std::runtime_error("Unable to connect to " + socketPath);
}
return socketfd;
}
struct ipc_response ipcRecvResponse(int socketfd) {
struct ipc_response response;
char data[ipc_header_size];
auto data32 = reinterpret_cast<uint32_t *>(data + sizeof(ipc_magic));
size_t total = 0;
while (total < ipc_header_size) {
ssize_t received = recv(socketfd, data + total, ipc_header_size - total, 0);
if (received <= 0) {
throw std::runtime_error("Unable to receive IPC response");
}
total += received;
}
total = 0;
response.size = data32[0];
response.type = data32[1];
char payload[response.size + 1];
while (total < response.size) {
ssize_t received = recv(socketfd, payload + total, response.size - total, 0);
if (received < 0) {
throw std::runtime_error("Unable to receive IPC response");
}
total += received;
}
payload[response.size] = '\0';
response.payload = std::string(payload);
return response;
}
std::string ipcSingleCommand(int socketfd, uint32_t type, const char *payload, uint32_t *len) {
char data[ipc_header_size];
auto data32 = reinterpret_cast<uint32_t *>(data + sizeof(ipc_magic));
memcpy(data, ipc_magic, sizeof(ipc_magic));
data32[0] = *len;
data32[1] = type;
if (send(socketfd, data, ipc_header_size, 0) == -1) {
throw std::runtime_error("Unable to send IPC header");
}
if (send(socketfd, payload, *len, 0) == -1) {
throw std::runtime_error("Unable to send IPC payload");
}
struct ipc_response resp = ipcRecvResponse(socketfd);
*len = resp.size;
return resp.payload;
}

View File

@ -0,0 +1,65 @@
#include "modules/sway/window.hpp"
#include "modules/sway/ipc/client.hpp"
waybar::modules::sway::Window::Window(Bar &bar, Json::Value config)
: bar_(bar), config_(std::move(config))
{
label_.set_name("window");
std::string socketPath = getSocketPath();
ipcfd_ = ipcOpenSocket(socketPath);
ipc_eventfd_ = ipcOpenSocket(socketPath);
const char *subscribe = "[ \"window\" ]";
uint32_t len = strlen(subscribe);
ipcSingleCommand(ipc_eventfd_, IPC_SUBSCRIBE, subscribe, &len);
getFocusedWindow();
thread_ = [this] {
try {
auto res = ipcRecvResponse(ipc_eventfd_);
auto parsed = parser_.parse(res.payload);
if ((parsed["change"] == "focus" || parsed["change"] == "title")
&& parsed["container"]["focused"].asBool()) {
window_ = parsed["container"]["name"].asString();
Glib::signal_idle().connect_once(sigc::mem_fun(*this, &Window::update));
}
} catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
}
};
}
auto waybar::modules::sway::Window::update() -> void
{
label_.set_text(window_);
label_.set_tooltip_text(window_);
}
std::string waybar::modules::sway::Window::getFocusedNode(Json::Value nodes)
{
for (auto &node : nodes) {
if (node["focused"].asBool()) {
return node["name"].asString();
}
auto res = getFocusedNode(node["nodes"]);
if (!res.empty()) {
return res;
}
}
return std::string();
}
void waybar::modules::sway::Window::getFocusedWindow()
{
try {
uint32_t len = 0;
auto res = ipcSingleCommand(ipcfd_, IPC_GET_TREE, nullptr, &len);
auto parsed = parser_.parse(res);
window_ = getFocusedNode(parsed["nodes"]);
Glib::signal_idle().connect_once(sigc::mem_fun(*this, &Window::update));
} catch (const std::exception &e) {
std::cerr << e.what() << std::endl;
}
}
waybar::modules::sway::Window::operator Gtk::Widget &() {
return label_;
}

View File

@ -0,0 +1,211 @@
#include "modules/sway/workspaces.hpp"
#include "modules/sway/ipc/client.hpp"
waybar::modules::sway::Workspaces::Workspaces(Bar &bar, Json::Value config)
: bar_(bar), config_(std::move(config)), scrolling_(false)
{
box_.set_name("workspaces");
std::string socketPath = getSocketPath();
ipcfd_ = ipcOpenSocket(socketPath);
ipc_eventfd_ = ipcOpenSocket(socketPath);
const char *subscribe = "[ \"workspace\" ]";
uint32_t len = strlen(subscribe);
ipcSingleCommand(ipc_eventfd_, IPC_SUBSCRIBE, subscribe, &len);
thread_ = [this] {
try {
// Wait for the name of the output
if (!config_["all-outputs"].asBool() && bar_.outputName.empty()) {
while (bar_.outputName.empty()) {
thread_.sleep_for(chrono::milliseconds(150));
}
} else if (!workspaces_.empty()) {
ipcRecvResponse(ipc_eventfd_);
}
uint32_t len = 0;
std::lock_guard<std::mutex> lock(mutex_);
auto str = ipcSingleCommand(ipcfd_, IPC_GET_WORKSPACES, nullptr, &len);
workspaces_ = parser_.parse(str);
Glib::signal_idle()
.connect_once(sigc::mem_fun(*this, &Workspaces::update));
} catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
}
};
}
auto waybar::modules::sway::Workspaces::update() -> void
{
std::lock_guard<std::mutex> lock(mutex_);
bool needReorder = false;
for (auto it = buttons_.begin(); it != buttons_.end();) {
auto ws = std::find_if(workspaces_.begin(), workspaces_.end(),
[it](auto node) -> bool { return node["num"].asInt() == it->first; });
if (ws == workspaces_.end()) {
it = buttons_.erase(it);
needReorder = true;
} else {
++it;
}
}
for (auto node : workspaces_) {
if (!config_["all-outputs"].asBool()
&& bar_.outputName != node["output"].asString()) {
continue;
}
auto it = buttons_.find(node["num"].asInt());
if (it == buttons_.end()) {
addWorkspace(node);
needReorder = true;
} else {
auto &button = it->second;
if (node["focused"].asBool()) {
button.get_style_context()->add_class("focused");
} else {
button.get_style_context()->remove_class("focused");
}
if (node["visible"].asBool()) {
button.get_style_context()->add_class("visible");
} else {
button.get_style_context()->remove_class("visible");
}
if (node["urgent"].asBool()) {
button.get_style_context()->add_class("urgent");
} else {
button.get_style_context()->remove_class("urgent");
}
if (needReorder) {
box_.reorder_child(button, node["num"].asInt());
}
button.show();
}
}
if (scrolling_) {
scrolling_ = false;
}
}
void waybar::modules::sway::Workspaces::addWorkspace(Json::Value node)
{
auto icon = getIcon(node["name"].asString());
auto pair = buttons_.emplace(node["num"].asInt(), icon);
auto &button = pair.first->second;
if (icon != node["name"].asString()) {
button.get_style_context()->add_class("icon");
}
box_.pack_start(button, false, false, 0);
button.set_relief(Gtk::RELIEF_NONE);
button.signal_clicked().connect([this, pair] {
try {
std::lock_guard<std::mutex> lock(mutex_);
auto value = fmt::format("workspace \"{}\"", pair.first->first);
uint32_t size = value.size();
ipcSingleCommand(ipcfd_, IPC_COMMAND, value.c_str(), &size);
} catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
}
});
button.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK);
if (!config_["disable-scroll"].asBool()) {
button.signal_scroll_event()
.connect(sigc::mem_fun(*this, &Workspaces::handleScroll));
}
box_.reorder_child(button, node["num"].asInt());
if (node["focused"].asBool()) {
button.get_style_context()->add_class("focused");
}
if (node["visible"].asBool()) {
button.get_style_context()->add_class("visible");
}
if (node["urgent"].asBool()) {
button.get_style_context()->add_class("urgent");
}
button.show();
}
std::string waybar::modules::sway::Workspaces::getIcon(std::string name)
{
if (config_["format-icons"][name]) {
return config_["format-icons"][name].asString();
}
if (config_["format-icons"]["default"]) {
return config_["format-icons"]["default"].asString();
}
return name;
}
bool waybar::modules::sway::Workspaces::handleScroll(GdkEventScroll *e)
{
std::lock_guard<std::mutex> lock(mutex_);
// Avoid concurrent scroll event
if (scrolling_) {
return false;
}
scrolling_ = true;
int id = -1;
uint16_t idx = 0;
for (; idx < workspaces_.size(); idx += 1) {
if (workspaces_[idx]["focused"].asBool()) {
id = workspaces_[idx]["num"].asInt();
break;
}
}
if (id == -1) {
scrolling_ = false;
return false;
}
if (e->direction == GDK_SCROLL_UP) {
id = getNextWorkspace();
}
if (e->direction == GDK_SCROLL_DOWN) {
id = getPrevWorkspace();
}
if (e->direction == GDK_SCROLL_SMOOTH) {
gdouble delta_x, delta_y;
gdk_event_get_scroll_deltas(reinterpret_cast<const GdkEvent *>(e),
&delta_x, &delta_y);
if (delta_y < 0) {
id = getNextWorkspace();
} else if (delta_y > 0) {
id = getPrevWorkspace();
}
}
if (id == workspaces_[idx]["num"].asInt()) {
scrolling_ = false;
return false;
}
auto value = fmt::format("workspace \"{}\"", id);
uint32_t size = value.size();
ipcSingleCommand(ipcfd_, IPC_COMMAND, value.c_str(), &size);
std::this_thread::sleep_for(std::chrono::milliseconds(150));
return true;
}
int waybar::modules::sway::Workspaces::getPrevWorkspace()
{
for (uint16_t i = 0; i != workspaces_.size(); i += 1) {
if (workspaces_[i]["focused"].asBool()) {
if (i > 0) {
return workspaces_[i - 1]["num"].asInt();
}
return workspaces_[workspaces_.size() - 1]["num"].asInt();
}
}
return -1;
}
int waybar::modules::sway::Workspaces::getNextWorkspace()
{
for (uint16_t i = 0; i != workspaces_.size(); i += 1) {
if (workspaces_[i]["focused"].asBool()) {
if (i + 1U < workspaces_.size()) {
return workspaces_[i + 1]["num"].asInt();
}
return workspaces_[0]["num"].asInt();
}
}
return -1;
}
waybar::modules::sway::Workspaces::operator Gtk::Widget &() {
return box_;
}

View File

@ -1,121 +0,0 @@
#include "modules/workspaces.hpp"
#include "ipc/client.hpp"
waybar::modules::Workspaces::Workspaces(Bar &bar)
: _bar(bar), _thread(nullptr), _box(Gtk::manage(new Gtk::Box))
{
_box->get_style_context()->add_class("workspaces");
std::string socketPath = get_socketpath();
_ipcSocketfd = ipc_open_socket(socketPath);
_ipcEventSocketfd = ipc_open_socket(socketPath);
const char *subscribe = "[ \"workspace\", \"mode\" ]";
uint32_t len = strlen(subscribe);
ipc_single_command(_ipcEventSocketfd, IPC_SUBSCRIBE, subscribe, &len);
_idle_timer =
org_kde_kwin_idle_get_idle_timeout(_bar.client.idle_manager,
_bar.client.seat, 10000); // 10 seconds
static const struct org_kde_kwin_idle_timeout_listener idle_timer_listener = {
.idle = _handle_idle,
.resumed = _handle_resume,
};
org_kde_kwin_idle_timeout_add_listener(_idle_timer,
&idle_timer_listener, this);
_updateThread();
}
auto waybar::modules::Workspaces::update() -> void
{
Json::Value workspaces = _getWorkspaces();
bool hided = false;
for (auto it = _buttons.begin(); it != _buttons.end(); ++it) {
auto ws = std::find_if(workspaces.begin(), workspaces.end(),
[it](auto node) -> bool { return node["num"].asInt() == it->first; });
if (ws == workspaces.end()) {
it->second.hide();
hided = true;
}
}
for (auto node : workspaces) {
auto it = _buttons.find(node["num"].asInt());
if (it == _buttons.end()) {
_addWorkspace(node);
} else {
auto styleContext = it->second.get_style_context();
bool isCurrent = node["focused"].asBool();
if (styleContext->has_class("current") && !isCurrent) {
styleContext->remove_class("current");
} else if (!styleContext->has_class("current") && isCurrent) {
styleContext->add_class("current");
}
if (hided) {
_box->reorder_child(it->second, node["num"].asInt() - 1);
}
it->second.show();
}
}
}
void waybar::modules::Workspaces::_updateThread()
{
_thread = new waybar::util::SleeperThread([this] {
update();
_thread->sleep_for(waybar::chrono::milliseconds(150));
});
}
void waybar::modules::Workspaces::_handle_idle(void *data,
struct org_kde_kwin_idle_timeout *timer) {
auto o = reinterpret_cast<waybar::modules::Workspaces *>(data);
if (o->_thread) {
delete o->_thread;
o->_thread = nullptr;
}
}
void waybar::modules::Workspaces::_handle_resume(void *data,
struct org_kde_kwin_idle_timeout *timer) {
auto o = reinterpret_cast<waybar::modules::Workspaces *>(data);
if (!o->_thread) {
o->_updateThread();
}
}
void waybar::modules::Workspaces::_addWorkspace(Json::Value node)
{
auto pair = _buttons.emplace(node["num"].asInt(), node["name"].asString());
auto &button = pair.first->second;
button.set_relief(Gtk::RELIEF_NONE);
button.signal_clicked().connect([this, pair] {
auto value = fmt::format("workspace \"{}\"", pair.first->first);
uint32_t size = value.size();
ipc_single_command(_ipcSocketfd, IPC_COMMAND, value.c_str(), &size);
});
_box->pack_start(button, false, false, 0);
_box->reorder_child(button, node["num"].asInt() - 1);
if (node["focused"].asBool()) {
button.get_style_context()->add_class("current");
}
button.show();
}
Json::Value waybar::modules::Workspaces::_getWorkspaces()
{
uint32_t len = 0;
Json::Value root;
Json::CharReaderBuilder builder;
Json::CharReader* reader = builder.newCharReader();
std::string err;
std::string str = ipc_single_command(_ipcSocketfd, IPC_GET_WORKSPACES,
nullptr, &len);
bool res = reader->parse(str.c_str(), str.c_str() + str.size(), &root, &err);
delete reader;
if (!res) {
std::cerr << err << std::endl;
return nullptr;
}
return root;
}
waybar::modules::Workspaces::operator Gtk::Widget &() {
return *_box;
}