Compare commits

...

76 Commits
0.5.1 ... 0.6.1

Author SHA1 Message Date
82bed9dd5e chore: v0.6.1 2019-05-02 14:31:02 +02:00
d027243a19 fix: json thread safe 2019-05-02 14:24:54 +02:00
e6d59f05cc fix s/hidded/hidden/ typo (#295)
fix s/hidded/hidden/ typo
2019-05-02 13:59:57 +02:00
4d4562aade fix s/hidded/hidden/ typo 2019-05-01 12:40:12 +01:00
e8f31a0c4f revert: infinite seconds for once custom modules 2019-04-26 21:57:15 +02:00
f8c06b27ae Revert "feat(Cpu): dynamic fmt args"
This reverts commit 2d9bcb1a2d.
2019-04-26 21:49:16 +02:00
717a07d584 refactor(Window): simpler conditions 2019-04-26 15:29:54 +02:00
2d9bcb1a2d feat(Cpu): dynamic fmt args 2019-04-26 14:07:31 +02:00
4dd36890c1 style: background color transition 2019-04-26 12:37:35 +02:00
66acaeca7f style: workspaces button color for chromium class 2019-04-26 12:01:42 +02:00
20cf7592aa Merge pull request #287 from jonvaldes/master
Allow rotating label contents by specifying a new "rotate" property
2019-04-26 09:39:52 +02:00
9fe29c37b4 Fix indentation 2019-04-25 22:56:14 +02:00
f8ae1534db Allow rotating label contents by specifying a new "rotate" property in the label config 2019-04-25 22:47:58 +02:00
46c91a26ac style: workspaces button color for chromium class 2019-04-25 17:14:16 +02:00
07c592cc86 chore: v0.6.0 2019-04-25 16:59:22 +02:00
bb8ff5a99f feat(Bar): add class depend of window in the workspace 2019-04-25 16:47:51 +02:00
79a5e9ecee feat: multiple bar with same process 2019-04-25 13:25:06 +02:00
9504b7ac03 fix(Bar): typo 2019-04-24 12:42:16 +02:00
311c34ecbc feat(Bar): handle widget size changes 2019-04-24 12:37:24 +02:00
90d89fe974 refactor: kill custom modules scripts en destroy 2019-04-23 15:56:38 +02:00
cccf60c30e fix(Workspaces): fix concurrence and move json parser to ipc client 2019-04-23 11:42:08 +02:00
07dba791cf Update fmt build dependency (#284)
Update fmt build dependency
2019-04-23 09:46:03 +02:00
3ee99946c7 chore: update fmt build dependency
Closes #279
2019-04-23 04:40:27 +02:00
0d0f5ed7db chore: update fmt wrap 2019-04-21 19:19:38 +02:00
263a32c9af Merge pull request #278 from minijackson/mpd-timeout
fix(mpd): regularly timeout the event listener to prevent timeout
2019-04-21 19:13:38 +02:00
b50650f63f fix(mpd): regularly timeout the event listener to prevent timeout
The MPD server has a connection_timeout that defaults to 60. If no data
is transferred in this timespan, the connection is closed. If the
connection is closed while the event listener is listening for events,
it will block forever. By timing out the event listening and
re-connecting regularly, we prevent this issue. An option "timeout" has
been added for users that have a lower server connection_timeout than
default. Fixes #277
2019-04-21 10:58:40 +02:00
768bc29bc1 Merge pull request #275 from cole-h/issue-273
Ensure no NULL tags are set
2019-04-20 23:32:13 +02:00
12e1233d38 Fix compile-time warning of catch by value 2019-04-20 09:16:11 -07:00
160837b900 Ensure no NULL tags are set
Because `mpd_song_get_tag` from libmpdclient can return NULL, verify the
value of tag is valid. Otherwise, set a default string of "N/A". Also
adds configuration to specify what this default string should be.
2019-04-20 09:12:30 -07:00
d03997bdaa Merge pull request #272 from Alexays/refactoring
Refactoring
2019-04-19 17:33:49 +02:00
471b5b1ea1 Merge branch 'master' into refactoring 2019-04-19 17:33:18 +02:00
29d8f365f8 refactor(Tray): proper lookup in the default theme 2019-04-19 17:30:40 +02:00
8cf19826aa fix(Tray): Unexport on exit 2019-04-19 17:03:46 +02:00
cbb6f2a307 refactor(Workspaces, IPC): no more mutex in the workspaces modules, moved to the IPC client for a proper handling 2019-04-19 16:48:02 +02:00
e77c155ede fix(workspaces): avoid mutex block 2019-04-19 12:11:55 +02:00
811468c0ab Merge pull request #274 from minijackson/mpd-escape
fix(mpd): Escape MPD values in the label
2019-04-19 11:57:36 +02:00
171ecd53aa refactor(Bar): roundtrip before setup widgets 2019-04-19 11:56:40 +02:00
66b0420391 fix(mpd): Escape MPD values in the label 2019-04-19 11:11:44 +02:00
42dc0c4c71 fix(ipc): typo 2019-04-19 11:10:48 +02:00
bb1cf7570e refactor(IPC): use sigc signal 2019-04-19 11:09:06 +02:00
a14b933d3e fix(config): add missing comma 2019-04-18 17:55:02 +02:00
4c8f4f82dc fix(config): add missing comma 2019-04-18 17:54:38 +02:00
6ed8f94dab refactor: format code 2019-04-18 17:52:00 +02:00
807ef32357 refactor: format && better output management 2019-04-18 17:47:40 +02:00
817c42841b Merge pull request #268 from minijackson/mpd
Add MPD support
2019-04-18 16:11:16 +02:00
3e54c3c669 fix(mpd): better sample theme 2019-04-18 15:57:58 +02:00
3656035c89 fix(mpd): slightly better and safer error handling 2019-04-18 15:57:57 +02:00
0ce8821aec feat(mpd): Add playing / paused classes 2019-04-18 15:57:57 +02:00
38e37f3680 chore(mpd): add sample MPD config 2019-04-18 15:57:57 +02:00
ab43d34a1e refactor(mpd): Add module name to log messages 2019-04-18 15:57:57 +02:00
22eccc2ac2 feat(mpd): reset player state when connection drops 2019-04-18 15:57:57 +02:00
cd92b475ad chore: Add clang-format configuration and format MPD module 2019-04-18 15:57:57 +02:00
235997fa73 feat(mpd): Add support for elapsed and total time 2019-04-18 15:55:46 +02:00
80a12d0238 feat(mpd): play/pause on click & stop on right-click 2019-04-18 15:55:46 +02:00
07dab2baec feat(mpd): Add support for options (random, repeat, etc.) 2019-04-18 15:55:45 +02:00
cbfcec4867 feat(mpd): Add support for play/pause icons 2019-04-18 15:55:45 +02:00
557b786ce0 feat(mpd): Allow for specifying the reconnect interval 2019-04-18 15:55:45 +02:00
8c9dd94670 feat(mpd): Add support for setting tooltip label when disconnected 2019-04-18 15:55:45 +02:00
06aff70e2e feat: Add basic support for MPD 2019-04-18 15:55:45 +02:00
85b6e6f709 chore: Add EditorConfig file 2019-04-18 15:55:45 +02:00
6d6df4be00 refactor(sni-item): better way to search in default theme 2019-04-17 22:15:18 +02:00
9564adb5b4 refactor(Bar): avoid reinterpret_cast 2019-04-17 19:33:49 +02:00
aeaa1927d9 fix: add default_paths on init 2019-04-17 19:23:52 +02:00
346ec68578 refactor: format tray && partial fix for #235 2019-04-17 14:19:04 +02:00
93a1bafb46 chore: add clang-format 2019-04-17 13:47:34 +02:00
5f37abbd3f Update README.md 2019-04-16 13:42:49 +02:00
3273ee8b42 fix(Tray): icon size lookup 2019-04-15 12:10:37 +02:00
d05b8398fa fix: prefer to hold running even when no window is open 2019-04-15 11:42:16 +02:00
ecc5f48dd7 feat: partially hide waybar on toggle 2019-04-15 11:11:16 +02:00
316b948d86 Merge pull request #265 from Alexays/custom-multiple-classes
Custom: Ability to add multiple classes
2019-04-15 10:58:34 +02:00
5828d34fa0 Merge branch 'master' into custom-multiple-classes 2019-04-15 10:58:27 +02:00
bc9a49787a feat: enable pango markup on sway workspaces 2019-04-15 10:55:44 +02:00
6aee51479d feat: ability to add multiple classes 2019-04-15 10:18:27 +02:00
57c99dc526 refactor(Tray): also search in default theme 2019-04-11 15:28:38 +02:00
78067462be fix(Tray): icons update 2019-04-11 15:20:39 +02:00
5870421f84 refactor(temperature): check if file exist on init 2019-04-11 15:08:23 +02:00
59 changed files with 2488 additions and 1858 deletions

6
.clang-format Normal file
View File

@ -0,0 +1,6 @@
---
BasedOnStyle: Google
AlignConsecutiveDeclarations: true
BinPackArguments: false
ColumnLimit: 100
...

19
.editorconfig Normal file
View File

@ -0,0 +1,19 @@
# EditorConfig configuration for Waybar
# http://EditorConfig.org
# Top-most EditorConfig file
root = true
[*]
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
charset = utf-8
[*.{build,css}]
indent_style = space
indent_size = 4
[*.{hpp,cpp}]
indent_style = space
indent_size = 2

View File

@ -1,5 +1,4 @@
# Waybar [![Travis](https://travis-ci.org/Alexays/Waybar.svg?branch=master)](https://travis-ci.org/Alexays/Waybar) [![Licence](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) [![Paypal Donate](https://img.shields.io/badge/Donate-Paypal-2244dd.svg)](https://paypal.me/ARouillard)<br>![Waybar](https://raw.githubusercontent.com/alexays/waybar/master/preview-2.png) # Waybar [![Travis](https://travis-ci.org/Alexays/Waybar.svg?branch=master)](https://travis-ci.org/Alexays/Waybar) [![Licence](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) [![Paypal Donate](https://img.shields.io/badge/Donate-Paypal-2244dd.svg)](https://paypal.me/ARouillard)<br>![Waybar](https://raw.githubusercontent.com/alexays/waybar/master/preview-2.png)
**Proof of concept**
> Highly customizable Wayland bar for Sway and Wlroots based compositors.<br> > Highly customizable Wayland bar for Sway and Wlroots based compositors.<br>
> Available in Arch [community](https://www.archlinux.org/packages/community/x86_64/waybar/) or > Available in Arch [community](https://www.archlinux.org/packages/community/x86_64/waybar/) or
@ -7,7 +6,7 @@
**Current features** **Current features**
- Sway (Workspaces, Binding mode, Focused window name) - Sway (Workspaces, Binding mode, Focused window name)
- Tray (Beta) [#21](https://github.com/Alexays/Waybar/issues/21) - Tray [#21](https://github.com/Alexays/Waybar/issues/21)
- Local time - Local time
- Battery - Battery
- Network - Network
@ -15,6 +14,7 @@
- Memory - Memory
- Cpu load average - Cpu load average
- Temperature - Temperature
- MPD
- Custom scripts - Custom scripts
- Multiple output configuration - Multiple output configuration
- And much more customizations - And much more customizations
@ -50,6 +50,7 @@ libpulse [Pulseaudio module]
libnl [Network module] libnl [Network module]
sway [Sway modules] sway [Sway modules]
libdbusmenu-gtk3 [Tray module] libdbusmenu-gtk3 [Tray module]
libmpdclient [MPD module]
``` ```
Contributions welcome! - have fun :)<br> Contributions welcome! - have fun :)<br>

View File

@ -1,35 +1,38 @@
#pragma once #pragma once
#include <json/json.h>
#include "IModule.hpp"
#include <glibmm/markup.h> #include <glibmm/markup.h>
#include <gtkmm/eventbox.h> #include <gtkmm/eventbox.h>
#include <gtkmm/label.h> #include <gtkmm/label.h>
#include <json/json.h>
#include "IModule.hpp"
namespace waybar { namespace waybar {
class ALabel : public IModule { class ALabel : public IModule {
public: public:
ALabel(const Json::Value &, const std::string format, uint16_t interval = 0); ALabel(const Json::Value &, const std::string &format, uint16_t interval = 0);
virtual ~ALabel() = default; virtual ~ALabel();
virtual auto update() -> void; virtual auto update() -> void;
virtual std::string getIcon(uint16_t, const std::string &alt = ""); virtual std::string getIcon(uint16_t, const std::string &alt = "");
virtual operator Gtk::Widget &(); virtual operator Gtk::Widget &();
protected: protected:
bool tooltipEnabled(); bool tooltipEnabled();
Gtk::EventBox event_box_; Gtk::EventBox event_box_;
Gtk::Label label_; Gtk::Label label_;
const Json::Value &config_; const Json::Value & config_;
std::string format_; std::string format_;
std::mutex mutex_; std::mutex mutex_;
const std::chrono::seconds interval_; const std::chrono::seconds interval_;
bool alt_ = false; bool alt_ = false;
std::string default_format_; std::string default_format_;
virtual bool handleToggle(GdkEventButton *const &ev); virtual bool handleToggle(GdkEventButton *const &ev);
virtual bool handleScroll(GdkEventScroll *); virtual bool handleScroll(GdkEventScroll *);
private:
std::vector<int> pid_;
}; };
} // namespace waybar } // namespace waybar

View File

@ -7,11 +7,11 @@
namespace waybar { namespace waybar {
class IModule { class IModule {
public: public:
virtual ~IModule() = default; virtual ~IModule() = default;
virtual auto update() -> void = 0; virtual auto update() -> void = 0;
virtual operator Gtk::Widget &() = 0; virtual operator Gtk::Widget &() = 0;
Glib::Dispatcher dp; // Hmmm Maybe I should create an abstract class ? Glib::Dispatcher dp; // Hmmm Maybe I should create an abstract class ?
}; };
} } // namespace waybar

View File

@ -1,73 +1,71 @@
#pragma once #pragma once
#include <json/json.h>
#include <glibmm/refptr.h> #include <glibmm/refptr.h>
#include <gtkmm/main.h>
#include <gtkmm/cssprovider.h> #include <gtkmm/cssprovider.h>
#include <gtkmm/main.h>
#include <gtkmm/window.h> #include <gtkmm/window.h>
#include <json/json.h>
#include "IModule.hpp"
#include "idle-inhibit-unstable-v1-client-protocol.h"
#include "wlr-layer-shell-unstable-v1-client-protocol.h" #include "wlr-layer-shell-unstable-v1-client-protocol.h"
#include "xdg-output-unstable-v1-client-protocol.h" #include "xdg-output-unstable-v1-client-protocol.h"
#include "idle-inhibit-unstable-v1-client-protocol.h"
#include "IModule.hpp"
namespace waybar { namespace waybar {
class Client;
class Factory; class Factory;
struct waybar_output {
class Bar { struct wl_output * output;
public: std::string name;
Bar(const Client&, std::unique_ptr<struct wl_output *>&&, uint32_t); uint32_t wl_name;
Bar(const Bar&) = delete; struct zxdg_output_v1 *xdg_output;
~Bar() = default;
auto toggle() -> void;
void handleSignal(int);
const Client& client;
Gtk::Window window;
struct wl_surface *surface;
struct zwlr_layer_surface_v1 *layer_surface;
std::unique_ptr<struct wl_output *> output;
std::string output_name;
uint32_t wl_name;
bool visible = true;
bool vertical = false;
private:
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 *);
void initBar();
bool isValidOutput(const Json::Value &config);
void destroyOutput();
auto setupConfig() -> void;
auto setupWidgets() -> void;
auto setupCss() -> void;
void getModules(const Factory&, const std::string&);
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_;
Gtk::Box left_;
Gtk::Box center_;
Gtk::Box right_;
Gtk::Box box_;
std::vector<std::unique_ptr<waybar::IModule>> modules_left_;
std::vector<std::unique_ptr<waybar::IModule>> modules_center_;
std::vector<std::unique_ptr<waybar::IModule>> modules_right_;
}; };
} class Bar {
public:
Bar(struct waybar_output *w_output, const Json::Value&);
Bar(const Bar &) = delete;
~Bar() = default;
auto toggle() -> void;
void handleSignal(int);
struct waybar_output * output;
Json::Value config;
Gtk::Window window;
struct wl_surface * surface;
struct zwlr_layer_surface_v1 *layer_surface;
bool visible = true;
bool vertical = false;
private:
static inline const std::string MIN_HEIGHT_MSG =
"Requested height: {} exceeds the minimum height: {} required by the modules";
static inline const std::string MIN_WIDTH_MSG =
"Requested width: {} exceeds the minimum width: {} required by the modules";
static inline const std::string BAR_SIZE_MSG =
"Bar configured (width: {}, height: {}) for output: {}";
static inline const std::string SIZE_DEFINED =
"{} size is defined in the config file so it will stay like that";
static void layerSurfaceHandleConfigure(void *, struct zwlr_layer_surface_v1 *, uint32_t,
uint32_t, uint32_t);
static void layerSurfaceHandleClosed(void *, struct zwlr_layer_surface_v1 *);
void destroyOutput();
void onWindowRealize();
auto setupWidgets() -> void;
void getModules(const Factory &, const std::string &);
void setupAltFormatKeyForModule(const std::string &module_name);
void setupAltFormatKeyForModuleList(const char *module_list_name);
uint32_t width_ = 0;
uint32_t height_ = 1;
Gtk::Box left_;
Gtk::Box center_;
Gtk::Box right_;
Gtk::Box box_;
std::vector<std::unique_ptr<waybar::IModule>> modules_left_;
std::vector<std::unique_ptr<waybar::IModule>> modules_center_;
std::vector<std::unique_ptr<waybar::IModule>> modules_right_;
};
} // namespace waybar

View File

@ -1,41 +1,56 @@
#pragma once #pragma once
#include <unistd.h>
#include <wordexp.h>
#include <fmt/format.h> #include <fmt/format.h>
#include <gdk/gdk.h> #include <gdk/gdk.h>
#include <wayland-client.h>
#include <gdk/gdkwayland.h> #include <gdk/gdkwayland.h>
#include <unistd.h>
#include <wayland-client.h>
#include <wordexp.h>
#include "bar.hpp" #include "bar.hpp"
namespace waybar { namespace waybar {
class Client { class Client {
public: public:
Client(int argc, char *argv[]); static Client *inst();
int main(int argc, char *argv[]); int main(int argc, char *argv[]);
Gtk::Main gtk_main; Glib::RefPtr<Gtk::Application> gtk_app;
std::string css_file; Glib::RefPtr<Gdk::Display> gdk_display;
std::string config_file; struct wl_display * wl_display = nullptr;
Glib::RefPtr<Gdk::Display> gdk_display; struct wl_registry * registry = nullptr;
struct wl_display *wl_display = nullptr; struct zwlr_layer_shell_v1 * layer_shell = nullptr;
struct wl_registry *registry = nullptr; struct zxdg_output_manager_v1 * xdg_output_manager = nullptr;
struct zwlr_layer_shell_v1 *layer_shell = nullptr; struct zwp_idle_inhibit_manager_v1 *idle_inhibit_manager = nullptr;
struct zxdg_output_manager_v1 *xdg_output_manager = nullptr; std::vector<std::unique_ptr<Bar>> bars;
struct wl_seat *seat = nullptr;
struct zwp_idle_inhibit_manager_v1 *idle_inhibit_manager = nullptr;
std::vector<std::unique_ptr<Bar>> bars;
private: private:
void setupConfigs(const std::string& config, const std::string& style); Client() = default;
void bindInterfaces(); void setupConfigs(const std::string &config, const std::string &style);
const std::string getValidPath(std::vector<std::string> paths); void bindInterfaces();
const std::string getValidPath(const std::vector<std::string> &paths);
void handleOutput(std::unique_ptr<struct waybar_output> &output);
bool isValidOutput(const Json::Value &config, std::unique_ptr<struct waybar_output> &output);
auto setupConfig() -> void;
auto setupCss() -> void;
std::unique_ptr<struct waybar_output>& getOutput(uint32_t wl_name);
std::vector<Json::Value> getOutputConfigs(std::unique_ptr<struct waybar_output> &output);
static void handleGlobal(void *data, struct wl_registry *registry, static void handleGlobal(void *data, struct wl_registry *registry, uint32_t name,
uint32_t name, const char *interface, uint32_t version); const char *interface, uint32_t version);
static void handleGlobalRemove(void *data, static void handleGlobalRemove(void *data, struct wl_registry *registry, uint32_t name);
struct wl_registry *registry, uint32_t name); 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 *);
Json::Value config_;
std::string css_file_;
std::string config_file_;
Glib::RefPtr<Gtk::StyleContext> style_context_;
Glib::RefPtr<Gtk::CssProvider> css_provider_;
std::vector<std::unique_ptr<struct waybar_output>> outputs_;
}; };
} } // namespace waybar

View File

@ -4,13 +4,13 @@
#include "modules/clock.hpp" #include "modules/clock.hpp"
#ifdef HAVE_SWAY #ifdef HAVE_SWAY
#include "modules/sway/mode.hpp" #include "modules/sway/mode.hpp"
#include "modules/sway/workspaces.hpp"
#include "modules/sway/window.hpp" #include "modules/sway/window.hpp"
#include "modules/sway/workspaces.hpp"
#endif #endif
#include "modules/idle_inhibitor.hpp"
#include "modules/battery.hpp" #include "modules/battery.hpp"
#include "modules/memory.hpp"
#include "modules/cpu.hpp" #include "modules/cpu.hpp"
#include "modules/idle_inhibitor.hpp"
#include "modules/memory.hpp"
#ifdef HAVE_DBUSMENU #ifdef HAVE_DBUSMENU
#include "modules/sni/tray.hpp" #include "modules/sni/tray.hpp"
#endif #endif
@ -23,20 +23,23 @@
#ifdef HAVE_LIBPULSE #ifdef HAVE_LIBPULSE
#include "modules/pulseaudio.hpp" #include "modules/pulseaudio.hpp"
#endif #endif
#include "modules/temperature.hpp" #ifdef HAVE_LIBMPDCLIENT
#include "modules/mpd.hpp"
#endif
#include "bar.hpp"
#include "modules/custom.hpp" #include "modules/custom.hpp"
#include "modules/temperature.hpp"
namespace waybar { namespace waybar {
class Bar;
class Factory { class Factory {
public: public:
Factory(const Bar& bar, const Json::Value& config); Factory(const Bar& bar, const Json::Value& config);
IModule* makeModule(const std::string &name) const; IModule* makeModule(const std::string& name) const;
private:
const Bar& bar_; private:
const Json::Value& config_; const Bar& bar_;
const Json::Value& config_;
}; };
} } // namespace waybar

View File

@ -16,52 +16,47 @@ namespace waybar::modules {
class Backlight : public ALabel { class Backlight : public ALabel {
class BacklightDev { class BacklightDev {
public: public:
BacklightDev() = default; BacklightDev() = default;
BacklightDev(std::string name, int actual, int max); BacklightDev(std::string name, int actual, int max);
std::string_view name() const; std::string_view name() const;
int get_actual() const; int get_actual() const;
void set_actual(int actual); void set_actual(int actual);
int get_max() const; int get_max() const;
void set_max(int max); void set_max(int max);
friend inline bool operator==(const BacklightDev &lhs, friend inline bool operator==(const BacklightDev &lhs, const BacklightDev &rhs) {
const BacklightDev &rhs) { return lhs.name_ == rhs.name_ && lhs.actual_ == rhs.actual_ && lhs.max_ == rhs.max_;
return lhs.name_ == rhs.name_ && lhs.actual_ == rhs.actual_ &&
lhs.max_ == rhs.max_;
} }
private: private:
std::string name_; std::string name_;
int actual_ = 1; int actual_ = 1;
int max_ = 1; int max_ = 1;
}; };
public: public:
Backlight(const std::string &, const Json::Value &); Backlight(const std::string &, const Json::Value &);
~Backlight(); ~Backlight();
auto update() -> void; auto update() -> void;
private: private:
template <class ForwardIt> template <class ForwardIt>
static const BacklightDev *best_device(ForwardIt first, ForwardIt last, static const BacklightDev *best_device(ForwardIt first, ForwardIt last, std::string_view);
std::string_view);
template <class ForwardIt, class Inserter> template <class ForwardIt, class Inserter>
static void upsert_device(ForwardIt first, ForwardIt last, Inserter inserter, static void upsert_device(ForwardIt first, ForwardIt last, Inserter inserter, udev_device *dev);
udev_device *dev);
template <class ForwardIt, class Inserter> template <class ForwardIt, class Inserter>
static void enumerate_devices(ForwardIt first, ForwardIt last, static void enumerate_devices(ForwardIt first, ForwardIt last, Inserter inserter, udev *udev);
Inserter inserter, udev *udev);
const std::string name_; const std::string name_;
const std::string preferred_device_; const std::string preferred_device_;
static constexpr int EPOLL_MAX_EVENTS = 16; static constexpr int EPOLL_MAX_EVENTS = 16;
std::optional<BacklightDev> previous_best_; std::optional<BacklightDev> previous_best_;
std::string previous_format_; std::string previous_format_;
std::mutex udev_thread_mutex_; std::mutex udev_thread_mutex_;
std::vector<BacklightDev> devices_; std::vector<BacklightDev> devices_;
// thread must destruct before shared data // thread must destruct before shared data
waybar::util::SleeperThread udev_thread_; waybar::util::SleeperThread udev_thread_;
}; };
} // namespace waybar::modules } // namespace waybar::modules

View File

@ -5,13 +5,13 @@
#else #else
#include <filesystem> #include <filesystem>
#endif #endif
#include <fstream>
#include <iostream>
#include <fmt/format.h> #include <fmt/format.h>
#include <sys/inotify.h> #include <sys/inotify.h>
#include <algorithm> #include <algorithm>
#include "util/sleeper_thread.hpp" #include <fstream>
#include <iostream>
#include "ALabel.hpp" #include "ALabel.hpp"
#include "util/sleeper_thread.hpp"
namespace waybar::modules { namespace waybar::modules {
@ -22,26 +22,27 @@ namespace fs = std::filesystem;
#endif #endif
class Battery : public ALabel { class Battery : public ALabel {
public: public:
Battery(const std::string&, const Json::Value&); Battery(const std::string&, const Json::Value&);
~Battery(); ~Battery();
auto update() -> void; auto update() -> void;
private:
static inline const fs::path data_dir_ = "/sys/class/power_supply/";
void getBatteries(); private:
void worker(); static inline const fs::path data_dir_ = "/sys/class/power_supply/";
const std::string getAdapterStatus(uint8_t capacity) const;
const std::tuple<uint8_t, std::string> getInfos() const;
const std::string getState(uint8_t) const;
util::SleeperThread thread_; void getBatteries();
util::SleeperThread thread_timer_; void worker();
std::vector<fs::path> batteries_; const std::string getAdapterStatus(uint8_t capacity) const;
fs::path adapter_; const std::tuple<uint8_t, std::string> getInfos() const;
int fd_; const std::string getState(uint8_t) const;
std::vector<int> wds_;
std::string old_status_; util::SleeperThread thread_;
util::SleeperThread thread_timer_;
std::vector<fs::path> batteries_;
fs::path adapter_;
int fd_;
std::vector<int> wds_;
std::string old_status_;
}; };
} } // namespace waybar::modules

View File

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

View File

@ -3,27 +3,28 @@
#include <fmt/format.h> #include <fmt/format.h>
#include <sys/sysinfo.h> #include <sys/sysinfo.h>
#include <fstream> #include <fstream>
#include <vector>
#include <numeric>
#include <iostream> #include <iostream>
#include "util/sleeper_thread.hpp" #include <numeric>
#include <vector>
#include "ALabel.hpp" #include "ALabel.hpp"
#include "util/sleeper_thread.hpp"
namespace waybar::modules { namespace waybar::modules {
class Cpu : public ALabel { class Cpu : public ALabel {
public: public:
Cpu(const std::string&, const Json::Value&); Cpu(const std::string&, const Json::Value&);
~Cpu() = default; ~Cpu() = default;
auto update() -> void; auto update() -> void;
private:
static inline const std::string data_dir_ = "/proc/stat";
uint16_t getCpuLoad();
std::tuple<uint16_t, std::string> getCpuUsage();
std::vector<std::tuple<size_t, size_t>> parseCpuinfo();
std::vector<std::tuple<size_t, size_t>> prev_times_; private:
waybar::util::SleeperThread thread_; static inline const std::string data_dir_ = "/proc/stat";
uint16_t getCpuLoad();
std::tuple<uint16_t, std::string> getCpuUsage();
std::vector<std::tuple<size_t, size_t>> parseCpuinfo();
std::vector<std::tuple<size_t, size_t>> prev_times_;
waybar::util::SleeperThread thread_;
}; };
} } // namespace waybar::modules

View File

@ -1,38 +1,39 @@
#pragma once #pragma once
#include <fmt/format.h> #include <fmt/format.h>
#include <iostream>
#include <csignal> #include <csignal>
#include "util/sleeper_thread.hpp" #include <iostream>
#include "ALabel.hpp"
#include "util/command.hpp" #include "util/command.hpp"
#include "util/json.hpp" #include "util/json.hpp"
#include "ALabel.hpp" #include "util/sleeper_thread.hpp"
namespace waybar::modules { namespace waybar::modules {
class Custom : public ALabel { class Custom : public ALabel {
public: public:
Custom(const std::string&, const Json::Value&); Custom(const std::string&, const Json::Value&);
~Custom(); ~Custom();
auto update() -> void; auto update() -> void;
void refresh(int /*signal*/); void refresh(int /*signal*/);
private:
void delayWorker();
void continuousWorker();
void parseOutputRaw();
void parseOutputJson();
const std::string name_; private:
std::string text_; void delayWorker();
std::string alt_; void continuousWorker();
std::string tooltip_; void parseOutputRaw();
std::string class_; void parseOutputJson();
std::string prevclass_;
int percentage_; const std::string name_;
waybar::util::SleeperThread thread_; std::string text_;
waybar::util::command::res output_; std::string alt_;
waybar::util::JsonParser parser_; std::string tooltip_;
FILE* fp_; std::vector<std::string> class_;
int percentage_;
waybar::util::SleeperThread thread_;
waybar::util::command::res output_;
waybar::util::JsonParser parser_;
FILE* fp_;
int pid_;
}; };
} } // namespace waybar::modules

View File

@ -1,23 +1,25 @@
#pragma once #pragma once
#include <fmt/format.h> #include <fmt/format.h>
#include "ALabel.hpp"
#include "bar.hpp" #include "bar.hpp"
#include "client.hpp" #include "client.hpp"
#include "ALabel.hpp"
namespace waybar::modules { namespace waybar::modules {
class IdleInhibitor: public ALabel { class IdleInhibitor : public ALabel {
public: public:
IdleInhibitor(const std::string&, const waybar::Bar&, const Json::Value&); IdleInhibitor(const std::string&, const waybar::Bar&, const Json::Value&);
~IdleInhibitor(); ~IdleInhibitor();
auto update() -> void; auto update() -> void;
private:
bool handleToggle(GdkEventButton* const& e);
const Bar& bar_; private:
std::string status_; bool handleToggle(GdkEventButton* const& e);
struct zwp_idle_inhibitor_v1 *idle_inhibitor_;
const Bar& bar_;
std::string status_;
struct zwp_idle_inhibitor_v1* idle_inhibitor_;
int pid_;
}; };
} } // namespace waybar::modules

View File

@ -2,22 +2,23 @@
#include <fmt/format.h> #include <fmt/format.h>
#include <fstream> #include <fstream>
#include "util/sleeper_thread.hpp"
#include "ALabel.hpp" #include "ALabel.hpp"
#include "util/sleeper_thread.hpp"
namespace waybar::modules { namespace waybar::modules {
class Memory : public ALabel { class Memory : public ALabel {
public: public:
Memory(const std::string&, const Json::Value&); Memory(const std::string&, const Json::Value&);
~Memory() = default; ~Memory() = default;
auto update() -> void; auto update() -> void;
private:
static inline const std::string data_dir_ = "/proc/meminfo"; private:
unsigned long memtotal_; static inline const std::string data_dir_ = "/proc/meminfo";
unsigned long memfree_; unsigned long memtotal_;
void parseMeminfo(); unsigned long memfree_;
waybar::util::SleeperThread thread_; void parseMeminfo();
waybar::util::SleeperThread thread_;
}; };
} } // namespace waybar::modules

70
include/modules/mpd.hpp Normal file
View File

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

View File

@ -1,62 +1,63 @@
#pragma once #pragma once
#include <net/if.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <ifaddrs.h>
#include <netlink/netlink.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/ctrl.h>
#include <linux/nl80211.h>
#include <sys/epoll.h>
#include <fmt/format.h> #include <fmt/format.h>
#include "util/sleeper_thread.hpp" #include <ifaddrs.h>
#include <linux/nl80211.h>
#include <net/if.h>
#include <netlink/genl/ctrl.h>
#include <netlink/genl/genl.h>
#include <netlink/netlink.h>
#include <sys/epoll.h>
#include "ALabel.hpp" #include "ALabel.hpp"
#include "util/sleeper_thread.hpp"
namespace waybar::modules { namespace waybar::modules {
class Network : public ALabel { class Network : public ALabel {
public: public:
Network(const std::string&, const Json::Value&); Network(const std::string&, const Json::Value&);
~Network(); ~Network();
auto update() -> void; auto update() -> void;
private:
static const uint8_t MAX_RETRY = 5;
static const uint8_t EPOLL_MAX = 255;
static int handleEvents(struct nl_msg*, void*); private:
static int handleScan(struct nl_msg*, void*); static const uint8_t MAX_RETRY = 5;
static const uint8_t EPOLL_MAX = 200;
void worker(); static int handleEvents(struct nl_msg*, void*);
void disconnected(); static int handleScan(struct nl_msg*, void*);
void createInfoSocket();
void createEventSocket();
int getExternalInterface();
void getInterfaceAddress();
int netlinkRequest(void*, uint32_t, uint32_t groups = 0);
int netlinkResponse(void*, uint32_t, uint32_t groups = 0);
void parseEssid(struct nlattr**);
void parseSignal(struct nlattr**);
bool associatedOrJoined(struct nlattr**);
auto getInfo() -> void;
waybar::util::SleeperThread thread_; void worker();
waybar::util::SleeperThread thread_timer_; void disconnected();
int ifid_; void createInfoSocket();
sa_family_t family_; void createEventSocket();
struct sockaddr_nl nladdr_ = {0}; int getExternalInterface();
struct nl_sock* sk_ = nullptr; void getInterfaceAddress();
struct nl_sock* info_sock_ = nullptr; int netlinkRequest(void*, uint32_t, uint32_t groups = 0);
int efd_; int netlinkResponse(void*, uint32_t, uint32_t groups = 0);
int ev_fd_; void parseEssid(struct nlattr**);
int nl80211_id_; void parseSignal(struct nlattr**);
bool associatedOrJoined(struct nlattr**);
auto getInfo() -> void;
std::string essid_; waybar::util::SleeperThread thread_;
std::string ifname_; waybar::util::SleeperThread thread_timer_;
std::string ipaddr_; int ifid_;
std::string netmask_; sa_family_t family_;
int cidr_; struct sockaddr_nl nladdr_ = {0};
int32_t signal_strength_dbm_; struct nl_sock* sk_ = nullptr;
uint8_t signal_strength_; struct nl_sock* info_sock_ = nullptr;
int efd_;
int ev_fd_;
int nl80211_id_;
std::string essid_;
std::string ifname_;
std::string ipaddr_;
std::string netmask_;
int cidr_;
int32_t signal_strength_dbm_;
uint8_t signal_strength_;
}; };
} } // namespace waybar::modules

View File

@ -9,31 +9,31 @@
namespace waybar::modules { namespace waybar::modules {
class Pulseaudio : public ALabel { class Pulseaudio : public ALabel {
public: public:
Pulseaudio(const std::string&, const Json::Value&); Pulseaudio(const std::string&, const Json::Value&);
~Pulseaudio(); ~Pulseaudio();
auto update() -> void; auto update() -> void;
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*);
static void volumeModifyCb(pa_context*, int, void*);
bool handleScroll(GdkEventScroll* e);
const std::string getPortIcon() const; 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*);
static void volumeModifyCb(pa_context*, int, void*);
bool handleScroll(GdkEventScroll* e);
pa_threaded_mainloop* mainloop_; const std::string getPortIcon() const;
pa_mainloop_api* mainloop_api_;
pa_context* context_; pa_threaded_mainloop* mainloop_;
uint32_t sink_idx_{0}; pa_mainloop_api* mainloop_api_;
uint16_t volume_; pa_context* context_;
pa_cvolume pa_volume_; uint32_t sink_idx_{0};
bool muted_; uint16_t volume_;
std::string port_name_; pa_cvolume pa_volume_;
std::string desc_; bool muted_;
bool scrolling_; std::string port_name_;
std::string desc_;
bool scrolling_;
}; };
} // namespace waybar::modules } // namespace waybar::modules

View File

@ -1,42 +1,43 @@
#pragma once #pragma once
#include <glibmm/refptr.h> #include <dbus-status-notifier-watcher.h>
#include <giomm.h> #include <giomm.h>
#include <glibmm/refptr.h>
#include <json/json.h> #include <json/json.h>
#include <tuple> #include <tuple>
#include <dbus-status-notifier-watcher.h>
#include "modules/sni/item.hpp" #include "modules/sni/item.hpp"
namespace waybar::modules::SNI { namespace waybar::modules::SNI {
class Host { class Host {
public: public:
Host(const std::size_t id, const Json::Value&, Host(const std::size_t id, const Json::Value&, const std::function<void(std::unique_ptr<Item>&)>&,
const std::function<void(std::unique_ptr<Item>&)>&, const std::function<void(std::unique_ptr<Item>&)>&);
const std::function<void(std::unique_ptr<Item>&)>&); ~Host();
~Host();
private:
void busAcquired(const Glib::RefPtr<Gio::DBus::Connection>&, Glib::ustring);
void nameAppeared(const Glib::RefPtr<Gio::DBus::Connection>&, Glib::ustring, const Glib::ustring&);
void nameVanished(const Glib::RefPtr<Gio::DBus::Connection>&, Glib::ustring);
static void proxyReady(GObject*, GAsyncResult*, gpointer);
static void registerHost(GObject*, GAsyncResult*, gpointer);
static void itemRegistered(SnWatcher*, const gchar*, gpointer);
static void itemUnregistered(SnWatcher*, const gchar*, gpointer);
std::tuple<std::string, std::string> getBusNameAndObjectPath(const std::string); private:
void addRegisteredItem(std::string service); void busAcquired(const Glib::RefPtr<Gio::DBus::Connection>&, Glib::ustring);
void nameAppeared(const Glib::RefPtr<Gio::DBus::Connection>&, Glib::ustring,
const Glib::ustring&);
void nameVanished(const Glib::RefPtr<Gio::DBus::Connection>&, Glib::ustring);
static void proxyReady(GObject*, GAsyncResult*, gpointer);
static void registerHost(GObject*, GAsyncResult*, gpointer);
static void itemRegistered(SnWatcher*, const gchar*, gpointer);
static void itemUnregistered(SnWatcher*, const gchar*, gpointer);
std::vector<std::unique_ptr<Item>> items_; std::tuple<std::string, std::string> getBusNameAndObjectPath(const std::string);
const std::string bus_name_; void addRegisteredItem(std::string service);
const std::string object_path_;
std::size_t bus_name_id_; std::vector<std::unique_ptr<Item>> items_;
std::size_t watcher_id_; const std::string bus_name_;
GCancellable* cancellable_ = nullptr; const std::string object_path_;
SnWatcher* watcher_ = nullptr; std::size_t bus_name_id_;
const Json::Value &config_; std::size_t watcher_id_;
const std::function<void(std::unique_ptr<Item>&)> on_add_; GCancellable* cancellable_ = nullptr;
const std::function<void(std::unique_ptr<Item>&)> on_remove_; SnWatcher* watcher_ = nullptr;
const Json::Value& config_;
const std::function<void(std::unique_ptr<Item>&)> on_add_;
const std::function<void(std::unique_ptr<Item>&)> on_remove_;
}; };
} } // namespace waybar::modules::SNI

View File

@ -1,11 +1,11 @@
#pragma once #pragma once
#include <dbus-status-notifier-item.h> #include <dbus-status-notifier-item.h>
#include <glibmm/refptr.h>
#include <giomm/dbusproxy.h> #include <giomm/dbusproxy.h>
#include <glibmm/refptr.h>
#include <gtkmm/eventbox.h> #include <gtkmm/eventbox.h>
#include <gtkmm/image.h>
#include <gtkmm/icontheme.h> #include <gtkmm/icontheme.h>
#include <gtkmm/image.h>
#include <gtkmm/menu.h> #include <gtkmm/menu.h>
#include <json/json.h> #include <json/json.h>
#include <libdbusmenu-gtk/dbusmenu-gtk.h> #include <libdbusmenu-gtk/dbusmenu-gtk.h>
@ -19,52 +19,53 @@
namespace waybar::modules::SNI { namespace waybar::modules::SNI {
class Item : public sigc::trackable { class Item : public sigc::trackable {
public: public:
Item(std::string, std::string, const Json::Value&); Item(const std::string&, const std::string&, const Json::Value&);
~Item() = default; ~Item() = default;
std::string bus_name; std::string bus_name;
std::string object_path; std::string object_path;
int icon_size; int icon_size;
int effective_icon_size; int effective_icon_size;
Gtk::Image image; Gtk::Image image;
Gtk::EventBox event_box; Gtk::EventBox event_box;
std::string category; std::string category;
std::string id; std::string id;
std::string status; std::string status;
std::string title; std::string title;
int32_t window_id; int32_t window_id;
std::string icon_name; std::string icon_name;
Glib::RefPtr<Gdk::Pixbuf> icon_pixmap; Glib::RefPtr<Gdk::Pixbuf> icon_pixmap;
std::string overlay_icon_name; Glib::RefPtr<Gtk::IconTheme> icon_theme;
std::string attention_icon_name; std::string overlay_icon_name;
std::string attention_movie_name; std::string attention_icon_name;
std::string icon_theme_path; std::string attention_movie_name;
std::string menu; std::string icon_theme_path;
DbusmenuGtkMenu *dbus_menu = nullptr; std::string menu;
Gtk::Menu *gtk_menu = nullptr; DbusmenuGtkMenu* dbus_menu = nullptr;
bool item_is_menu; Gtk::Menu* gtk_menu = nullptr;
bool item_is_menu;
private: private:
void proxyReady(Glib::RefPtr<Gio::AsyncResult>& result); void proxyReady(Glib::RefPtr<Gio::AsyncResult>& result);
void setProperty(const Glib::ustring& name, Glib::VariantBase& value); void setProperty(const Glib::ustring& name, Glib::VariantBase& value);
void getUpdatedProperties(); void getUpdatedProperties();
void processUpdatedProperties(Glib::RefPtr<Gio::AsyncResult>& result); void processUpdatedProperties(Glib::RefPtr<Gio::AsyncResult>& result);
void onSignal(const Glib::ustring& sender_name, const Glib::ustring& signal_name, void onSignal(const Glib::ustring& sender_name, const Glib::ustring& signal_name,
const Glib::VariantContainerBase& arguments); const Glib::VariantContainerBase& arguments);
void updateImage(); void updateImage();
Glib::RefPtr<Gdk::Pixbuf> extractPixBuf(GVariant *variant); Glib::RefPtr<Gdk::Pixbuf> extractPixBuf(GVariant* variant);
Glib::RefPtr<Gdk::Pixbuf> getIconByName(std::string name, int size); Glib::RefPtr<Gdk::Pixbuf> getIconByName(const std::string& name, int size);
static void onMenuDestroyed(Item *self); static void onMenuDestroyed(Item* self);
bool makeMenu(GdkEventButton *const &ev); bool makeMenu(GdkEventButton* const& ev);
bool handleClick(GdkEventButton *const & /*ev*/); bool handleClick(GdkEventButton* const& /*ev*/);
Glib::RefPtr<Gio::Cancellable> cancellable_; Glib::RefPtr<Gio::Cancellable> cancellable_;
Glib::RefPtr<Gio::DBus::Proxy> proxy_; Glib::RefPtr<Gio::DBus::Proxy> proxy_;
bool update_pending_; bool update_pending_;
}; };
} // namespace waybar::modules::SNI } // namespace waybar::modules::SNI

View File

@ -1,29 +1,30 @@
#pragma once #pragma once
#include <fmt/format.h> #include <fmt/format.h>
#include "bar.hpp"
#include "util/json.hpp"
#include "IModule.hpp" #include "IModule.hpp"
#include "modules/sni/watcher.hpp" #include "bar.hpp"
#include "modules/sni/host.hpp" #include "modules/sni/host.hpp"
#include "modules/sni/watcher.hpp"
#include "util/json.hpp"
namespace waybar::modules::SNI { namespace waybar::modules::SNI {
class Tray : public IModule { class Tray : public IModule {
public: public:
Tray(const std::string&, const Bar&, const Json::Value&); Tray(const std::string&, const Bar&, const Json::Value&);
~Tray() = default; ~Tray() = default;
auto update() -> void; auto update() -> void;
operator Gtk::Widget &(); operator Gtk::Widget&();
private:
void onAdd(std::unique_ptr<Item>& item);
void onRemove(std::unique_ptr<Item>& item);
static inline std::size_t nb_hosts_ = 0; private:
const Json::Value& config_; void onAdd(std::unique_ptr<Item>& item);
Gtk::Box box_; void onRemove(std::unique_ptr<Item>& item);
SNI::Watcher watcher_ ;
SNI::Host host_; static inline std::size_t nb_hosts_ = 0;
const Json::Value& config_;
Gtk::Box box_;
SNI::Watcher watcher_;
SNI::Host host_;
}; };
} } // namespace waybar::modules::SNI

View File

@ -1,48 +1,43 @@
#pragma once #pragma once
#include <dbus-status-notifier-watcher.h>
#include <giomm.h> #include <giomm.h>
#include <glibmm/refptr.h> #include <glibmm/refptr.h>
#include <dbus-status-notifier-watcher.h>
namespace waybar::modules::SNI { namespace waybar::modules::SNI {
class Watcher { class Watcher {
public: public:
Watcher(); Watcher();
~Watcher() = default; ~Watcher();
private: private:
typedef enum { GF_WATCH_TYPE_HOST, GF_WATCH_TYPE_ITEM } GfWatchType; typedef enum { GF_WATCH_TYPE_HOST, GF_WATCH_TYPE_ITEM } GfWatchType;
typedef struct { typedef struct {
GfWatchType type; GfWatchType type;
Watcher *watcher; Watcher * watcher;
gchar *service; gchar * service;
gchar *bus_name; gchar * bus_name;
gchar *object_path; gchar * object_path;
guint watch_id; guint watch_id;
} GfWatch; } GfWatch;
void busAcquired(const Glib::RefPtr<Gio::DBus::Connection>&, Glib::ustring); void busAcquired(const Glib::RefPtr<Gio::DBus::Connection> &, Glib::ustring);
static gboolean handleRegisterHost(Watcher *, GDBusMethodInvocation *, static gboolean handleRegisterHost(Watcher *, GDBusMethodInvocation *, const gchar *);
const gchar *); static gboolean handleRegisterItem(Watcher *, GDBusMethodInvocation *, const gchar *);
static gboolean handleRegisterItem(Watcher *, GDBusMethodInvocation *, static GfWatch *gfWatchFind(GSList *list, const gchar *bus_name, const gchar *object_path);
const gchar *); static GfWatch *gfWatchNew(GfWatchType, const gchar *, const gchar *, const gchar *, Watcher *);
static GfWatch *gfWatchFind(GSList *list, const gchar *bus_name, static void nameVanished(GDBusConnection *connection, const char *name, gpointer data);
const gchar *object_path); static void gfWatchFree(gpointer data);
static GfWatch *gfWatchNew(GfWatchType, const gchar *, const gchar *,
const gchar *, Watcher *);
static void nameVanished(GDBusConnection *connection, const char *name,
gpointer data);
void updateRegisteredItems(SnWatcher *obj); void updateRegisteredItems(SnWatcher *obj);
uint32_t bus_name_id_; uint32_t bus_name_id_;
uint32_t watcher_id_; uint32_t watcher_id_;
GSList *hosts_ = nullptr; GSList * hosts_ = nullptr;
GSList *items_ = nullptr; GSList * items_ = nullptr;
SnWatcher *watcher_ = nullptr; SnWatcher *watcher_ = nullptr;
}; };
} // namespace waybar::modules::SNI } // namespace waybar::modules::SNI

View File

@ -1,40 +1,51 @@
#pragma once #pragma once
#include <iostream> #include <sigc++/sigc++.h>
#include <cstring>
#include <unistd.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/un.h> #include <sys/un.h>
#include <unistd.h>
#include <cstring>
#include <iostream>
#include <memory>
#include <mutex>
#include "ipc.hpp" #include "ipc.hpp"
#include "util/json.hpp"
namespace waybar::modules::sway { namespace waybar::modules::sway {
class Ipc { class Ipc {
public: public:
Ipc(); Ipc();
~Ipc(); ~Ipc();
struct ipc_response { struct ipc_response {
uint32_t size; uint32_t size;
uint32_t type; uint32_t type;
std::string payload; Json::Value payload;
}; };
struct ipc_response sendCmd(uint32_t type, const std::string &payload = "") const; sigc::signal<void, const struct ipc_response&> signal_event;
void subscribe(const std::string &payload) const; sigc::signal<void, const struct ipc_response&> signal_cmd;
struct ipc_response handleEvent() const;
protected: void sendCmd(uint32_t type, const std::string &payload = "");
void subscribe(const std::string &payload);
void handleEvent();
protected:
static inline const std::string ipc_magic_ = "i3-ipc"; static inline const std::string ipc_magic_ = "i3-ipc";
static inline const size_t ipc_header_size_ = ipc_magic_.size() + 8; static inline const size_t ipc_header_size_ = ipc_magic_.size() + 8;
const std::string getSocketPath() const; const std::string getSocketPath() const;
int open(const std::string &) const; int open(const std::string &) const;
struct ipc_response send(int fd, uint32_t type, const std::string &payload = "") const; struct ipc_response send(int fd, uint32_t type, const std::string &payload = "");
struct ipc_response recv(int fd) const; struct ipc_response recv(int fd);
int fd_; int fd_;
int fd_event_; int fd_event_;
std::mutex mutex_;
std::mutex mutex_event_;
std::mutex mutex_parser_;
util::JsonParser parser_;
}; };
} // namespace waybar::modules::sway } // namespace waybar::modules::sway

View File

@ -1,28 +1,28 @@
#pragma once #pragma once
#include <fmt/format.h> #include <fmt/format.h>
#include "ALabel.hpp"
#include "bar.hpp" #include "bar.hpp"
#include "client.hpp" #include "client.hpp"
#include "util/sleeper_thread.hpp"
#include "util/json.hpp"
#include "ALabel.hpp"
#include "modules/sway/ipc/client.hpp" #include "modules/sway/ipc/client.hpp"
#include "util/sleeper_thread.hpp"
namespace waybar::modules::sway { namespace waybar::modules::sway {
class Mode : public ALabel { class Mode : public ALabel {
public: public:
Mode(const std::string&, const waybar::Bar&, const Json::Value&); Mode(const std::string&, const waybar::Bar&, const Json::Value&);
~Mode() = default; ~Mode() = default;
auto update() -> void; auto update() -> void;
private:
void worker();
const Bar& bar_; private:
waybar::util::SleeperThread thread_; void onEvent(const struct Ipc::ipc_response&);
util::JsonParser parser_; void worker();
Ipc ipc_;
std::string mode_; const Bar& bar_;
waybar::util::SleeperThread thread_;
Ipc ipc_;
std::string mode_;
}; };
} } // namespace waybar::modules::sway

View File

@ -2,31 +2,33 @@
#include <fmt/format.h> #include <fmt/format.h>
#include <tuple> #include <tuple>
#include "ALabel.hpp"
#include "bar.hpp" #include "bar.hpp"
#include "client.hpp" #include "client.hpp"
#include "util/sleeper_thread.hpp"
#include "util/json.hpp"
#include "ALabel.hpp"
#include "modules/sway/ipc/client.hpp" #include "modules/sway/ipc/client.hpp"
#include "util/sleeper_thread.hpp"
namespace waybar::modules::sway { namespace waybar::modules::sway {
class Window : public ALabel { class Window : public ALabel {
public: public:
Window(const std::string&, const waybar::Bar&, const Json::Value&); Window(const std::string&, const waybar::Bar&, const Json::Value&);
~Window() = default; ~Window() = default;
auto update() -> void; auto update() -> void;
private:
void worker();
std::tuple<int, std::string> getFocusedNode(Json::Value nodes);
void getFocusedWindow();
const Bar& bar_; private:
waybar::util::SleeperThread thread_; void onEvent(const struct Ipc::ipc_response&);
util::JsonParser parser_; void onCmd(const struct Ipc::ipc_response&);
Ipc ipc_; void worker();
std::string window_; std::tuple<std::size_t, int, std::string, std::string> getFocusedNode(const Json::Value& nodes);
int windowId_; void getTree();
const Bar& bar_;
waybar::util::SleeperThread thread_;
Ipc ipc_;
std::string window_;
int windowId_;
std::string app_id_;
}; };
} } // namespace waybar::modules::sway

View File

@ -1,42 +1,45 @@
#pragma once #pragma once
#include <fmt/format.h> #include <fmt/format.h>
#include <gtkmm/button.h>
#include <gtkmm/label.h>
#include "IModule.hpp"
#include "bar.hpp" #include "bar.hpp"
#include "client.hpp" #include "client.hpp"
#include "util/sleeper_thread.hpp"
#include "util/json.hpp"
#include "IModule.hpp"
#include "modules/sway/ipc/client.hpp" #include "modules/sway/ipc/client.hpp"
#include <gtkmm/button.h> #include "util/sleeper_thread.hpp"
namespace waybar::modules::sway { namespace waybar::modules::sway {
class Workspaces : public IModule { class Workspaces : public IModule {
public: public:
Workspaces(const std::string&, const waybar::Bar&, const Json::Value&); Workspaces(const std::string&, const waybar::Bar&, const Json::Value&);
~Workspaces() = default; ~Workspaces() = default;
auto update() -> void; auto update() -> void;
operator Gtk::Widget &(); operator Gtk::Widget&();
private:
void worker();
void addWorkspace(const Json::Value&);
void onButtonReady(const Json::Value&, Gtk::Button&);
std::string getIcon(const std::string&, const Json::Value&);
bool handleScroll(GdkEventScroll*);
const std::string getCycleWorkspace(uint8_t current, bool prev) const;
uint16_t getWorkspaceIndex(const std::string &name) const;
std::string trimWorkspaceName(std::string);
const Bar& bar_; private:
const Json::Value& config_; void onCmd(const struct Ipc::ipc_response&);
Json::Value workspaces_; void onEvent(const struct Ipc::ipc_response&);
waybar::util::SleeperThread thread_; void worker();
Gtk::Box box_; bool filterButtons();
util::JsonParser parser_; Gtk::Button& addButton(const Json::Value&);
Ipc ipc_; void onButtonReady(const Json::Value&, Gtk::Button&);
std::mutex mutex_; std::string getIcon(const std::string&, const Json::Value&);
bool scrolling_; bool handleScroll(GdkEventScroll*);
std::unordered_map<std::string, Gtk::Button> buttons_; const std::string getCycleWorkspace(std::vector<Json::Value>::iterator, bool prev) const;
uint16_t getWorkspaceIndex(const std::string& name) const;
std::string trimWorkspaceName(std::string);
const Bar& bar_;
const Json::Value& config_;
std::vector<Json::Value> workspaces_;
waybar::util::SleeperThread thread_;
std::mutex mutex_;
Gtk::Box box_;
Ipc ipc_;
bool scrolling_;
std::unordered_map<std::string, Gtk::Button> buttons_;
}; };
} } // namespace waybar::modules::sway

View File

@ -2,22 +2,28 @@
#include <fmt/format.h> #include <fmt/format.h>
#include <fstream> #include <fstream>
#include "util/sleeper_thread.hpp"
#include "ALabel.hpp" #include "ALabel.hpp"
#include "util/sleeper_thread.hpp"
#ifdef FILESYSTEM_EXPERIMENTAL
#include <experimental/filesystem>
#else
#include <filesystem>
#endif
namespace waybar::modules { namespace waybar::modules {
class Temperature : public ALabel { class Temperature : public ALabel {
public: public:
Temperature(const std::string&, const Json::Value&); Temperature(const std::string&, const Json::Value&);
~Temperature() = default; ~Temperature() = default;
auto update() -> void; auto update() -> void;
private:
std::tuple<uint16_t, uint16_t> getTemperature();
bool isCritical(uint16_t);
std::string file_path_; private:
waybar::util::SleeperThread thread_; std::tuple<uint16_t, uint16_t> getTemperature();
bool isCritical(uint16_t);
std::string file_path_;
waybar::util::SleeperThread thread_;
}; };
} } // namespace waybar::modules

View File

@ -1,25 +1,19 @@
#pragma once #pragma once
#include <sys/wait.h>
#include <giomm.h> #include <giomm.h>
#include <sys/wait.h>
#include <unistd.h> #include <unistd.h>
namespace waybar::util::command { namespace waybar::util::command {
struct res { struct res {
int exit_code; int exit_code;
std::string out; std::string out;
}; };
inline struct res exec(const std::string cmd) inline std::string read(FILE* fp) {
{
FILE* fp(popen(cmd.c_str(), "r"));
if (!fp) {
return { -1, "" };
}
std::array<char, 128> buffer = {0}; std::array<char, 128> buffer = {0};
std::string output; std::string output;
while (feof(fp) == 0) { while (feof(fp) == 0) {
if (fgets(buffer.data(), 128, fp) != nullptr) { if (fgets(buffer.data(), 128, fp) != nullptr) {
output += buffer.data(); output += buffer.data();
@ -27,27 +21,78 @@ inline struct res exec(const std::string cmd)
} }
// Remove last newline // Remove last newline
if (!output.empty() && output[output.length()-1] == '\n') { if (!output.empty() && output[output.length() - 1] == '\n') {
output.erase(output.length()-1); output.erase(output.length() - 1);
} }
int exit_code = WEXITSTATUS(pclose(fp)); return output;
return {exit_code, output};
} }
inline bool forkExec(std::string cmd) { inline int close(FILE* fp, pid_t pid) {
if (cmd == "") return true; int stat;
fclose(fp);
while (waitpid(pid, &stat, 0) == -1) {
if (errno != EINTR) {
stat = -1;
break;
}
}
return stat;
}
inline FILE* open(const std::string cmd, int& pid) {
if (cmd == "") return nullptr;
int fd[2];
pipe(fd);
pid_t child_pid = fork();
if (child_pid < 0) {
printf("Unable to exec cmd %s, error %s", cmd.c_str(), strerror(errno));
return nullptr;
}
if (!child_pid) {
::close(fd[0]);
dup2(fd[1], 1);
setpgid(child_pid, child_pid);
execl("/bin/sh", "sh", "-c", cmd.c_str(), (char*)0);
exit(0);
} else {
::close(fd[1]);
}
pid = child_pid;
return fdopen(fd[0], "r");
}
inline struct res exec(std::string cmd) {
int pid;
auto fp = command::open(cmd, pid);
if (!fp) return {-1, ""};
auto output = command::read(fp);
auto stat = command::close(fp, pid);
return {WEXITSTATUS(stat), output};
}
inline int32_t forkExec(std::string cmd) {
if (cmd == "") return -1;
int32_t pid = fork(); int32_t pid = fork();
if (pid < 0) { if (pid < 0) {
printf("Unable to exec cmd %s, error %s", cmd.c_str(), strerror(errno)); printf("Unable to exec cmd %s, error %s", cmd.c_str(), strerror(errno));
return false; return pid;
} }
// Child executes the command // Child executes the command
if (!pid) execl("/bin/sh", "sh", "-c", cmd.c_str(), (char*)0); if (!pid) {
setpgid(pid, pid);
execl("/bin/sh", "sh", "-c", cmd.c_str(), (char*)0);
exit(0);
}
return true; return pid;
} }
} // namespace waybar::util::command } // namespace waybar::util::command

View File

@ -5,27 +5,24 @@
namespace waybar::util { namespace waybar::util {
struct JsonParser { struct JsonParser {
JsonParser() : reader_(builder_.newCharReader()) {}
JsonParser() const Json::Value parse(const std::string& data) const {
: reader_(builder_.newCharReader()) Json::Value root(Json::objectValue);
{} if (data.empty()) {
return root;
const Json::Value parse(const std::string& data) const }
{
Json::Value root;
std::string err; std::string err;
bool res = bool res = reader_->parse(data.c_str(), data.c_str() + data.size(), &root, &err);
reader_->parse(data.c_str(), data.c_str() + data.size(), &root, &err); if (!res) throw std::runtime_error(err);
if (!res)
throw std::runtime_error(err);
return root; return root;
} }
~JsonParser() = default; ~JsonParser() = default;
private: private:
Json::CharReaderBuilder builder_; Json::CharReaderBuilder builder_;
std::unique_ptr<Json::CharReader> const reader_; std::unique_ptr<Json::CharReader> const reader_;
}; };
} } // namespace waybar::util

View File

@ -1,25 +1,23 @@
#pragma once #pragma once
#include <chrono> #include <chrono>
#include <condition_variable>
#include <ctime> #include <ctime>
#include <functional> #include <functional>
#include <condition_variable>
#include <thread> #include <thread>
namespace waybar::util { namespace waybar::util {
class SleeperThread { class SleeperThread {
public: public:
SleeperThread() = default; SleeperThread() = default;
SleeperThread(std::function<void()> func) SleeperThread(std::function<void()> func)
: thread_{[this, func] { : thread_{[this, func] {
while (do_run_) func(); while (do_run_) func();
}} }} {}
{}
SleeperThread& operator=(std::function<void()> func) SleeperThread& operator=(std::function<void()> func) {
{
thread_ = std::thread([this, func] { thread_ = std::thread([this, func] {
while (do_run_) { while (do_run_) {
signal_ = false; signal_ = false;
@ -29,32 +27,26 @@ public:
return *this; return *this;
} }
bool isRunning() const bool isRunning() const { return do_run_; }
{
return do_run_;
}
auto sleep_for(std::chrono::system_clock::duration dur) auto sleep_for(std::chrono::system_clock::duration dur) {
{
std::unique_lock lk(mutex_); std::unique_lock lk(mutex_);
return condvar_.wait_for(lk, dur, [this] { return signal_ || !do_run_; }); return condvar_.wait_for(lk, dur, [this] { return signal_ || !do_run_; });
} }
auto sleep_until(std::chrono::time_point<std::chrono::system_clock, auto sleep_until(
std::chrono::system_clock::duration> time_point) std::chrono::time_point<std::chrono::system_clock, std::chrono::system_clock::duration>
{ time_point) {
std::unique_lock lk(mutex_); std::unique_lock lk(mutex_);
return condvar_.wait_until(lk, time_point, [this] { return signal_ || !do_run_; }); return condvar_.wait_until(lk, time_point, [this] { return signal_ || !do_run_; });
} }
auto wake_up() auto wake_up() {
{
signal_ = true; signal_ = true;
condvar_.notify_all(); condvar_.notify_all();
} }
auto stop() auto stop() {
{
{ {
std::lock_guard<std::mutex> lck(mutex_); std::lock_guard<std::mutex> lck(mutex_);
signal_ = true; signal_ = true;
@ -63,20 +55,19 @@ public:
condvar_.notify_all(); condvar_.notify_all();
} }
~SleeperThread() ~SleeperThread() {
{
stop(); stop();
if (thread_.joinable()) { if (thread_.joinable()) {
thread_.join(); thread_.join();
} }
} }
private: private:
std::thread thread_; std::thread thread_;
std::condition_variable condvar_; std::condition_variable condvar_;
std::mutex mutex_; std::mutex mutex_;
bool do_run_ = true; bool do_run_ = true;
bool signal_ = false; bool signal_ = false;
}; };
} } // namespace waybar::util

View File

@ -1,6 +1,6 @@
project( project(
'waybar', 'cpp', 'c', 'waybar', 'cpp', 'c',
version: '0.5.1', version: '0.6.1',
license: 'MIT', license: 'MIT',
default_options : [ default_options : [
'cpp_std=c++17', 'cpp_std=c++17',
@ -42,7 +42,7 @@ add_global_link_arguments(cpp_link_args, language : 'cpp')
thread_dep = dependency('threads') thread_dep = dependency('threads')
libinput = dependency('libinput') libinput = dependency('libinput')
fmt = dependency('fmt', version : ['>=5.2.1'], fallback : ['fmt', 'fmt_dep']) fmt = dependency('fmt', version : ['>=5.3.0'], fallback : ['fmt', 'fmt_dep'])
wayland_client = dependency('wayland-client') wayland_client = dependency('wayland-client')
wayland_cursor = dependency('wayland-cursor') wayland_cursor = dependency('wayland-cursor')
wayland_protos = dependency('wayland-protocols') wayland_protos = dependency('wayland-protocols')
@ -56,6 +56,7 @@ libnl = dependency('libnl-3.0', required: get_option('libnl'))
libnlgen = dependency('libnl-genl-3.0', required: get_option('libnl')) libnlgen = dependency('libnl-genl-3.0', required: get_option('libnl'))
libpulse = dependency('libpulse', required: get_option('pulseaudio')) libpulse = dependency('libpulse', required: get_option('pulseaudio'))
libudev = dependency('libudev', required: get_option('libudev')) libudev = dependency('libudev', required: get_option('libudev'))
libmpdclient = dependency('libmpdclient', required: get_option('mpd'))
src_files = files( src_files = files(
'src/factory.cpp', 'src/factory.cpp',
@ -107,6 +108,11 @@ if libudev.found()
src_files += 'src/modules/backlight.cpp' src_files += 'src/modules/backlight.cpp'
endif endif
if libmpdclient.found()
add_project_arguments('-DHAVE_LIBMPDCLIENT', language: 'cpp')
src_files += 'src/modules/mpd.cpp'
endif
subdir('protocol') subdir('protocol')
executable( executable(
@ -128,7 +134,8 @@ executable(
libnl, libnl,
libnlgen, libnlgen,
libpulse, libpulse,
libudev libudev,
libmpdclient
], ],
include_directories: [include_directories('include')], include_directories: [include_directories('include')],
install: true, install: true,

View File

@ -2,4 +2,5 @@ option('libnl', type: 'feature', value: 'auto', description: 'Enable libnl suppo
option('libudev', type: 'feature', value: 'auto', description: 'Enable libudev support for udev related features') option('libudev', type: 'feature', value: 'auto', description: 'Enable libudev support for udev related features')
option('pulseaudio', type: 'feature', value: 'auto', description: 'Enable support for pulseaudio') option('pulseaudio', type: 'feature', value: 'auto', description: 'Enable support for pulseaudio')
option('dbusmenu-gtk', type: 'feature', value: 'auto', description: 'Enable support for tray') option('dbusmenu-gtk', type: 'feature', value: 'auto', description: 'Enable support for tray')
option('mpd', type: 'feature', value: 'auto', description: 'Enable support for the Music Player Daemon')
option('out', type: 'string', value : '/', description: 'output prefix directory') option('out', type: 'string', value : '/', description: 'output prefix directory')

View File

@ -1,12 +1,12 @@
{ {
"layer": "top", // Waybar at top layer "layer": "top", // Waybar at top layer
// "position": "bottom", // Waybar position (top|bottom|left|right) // "position": "bottom", // Waybar position (top|bottom|left|right)
// "height": 30, // Waybar height "height": 30, // Waybar height (to be removed for auto height)
// "width": 1280, // Waybar width // "width": 1280, // Waybar width
// Choose the order of the modules // Choose the order of the modules
"modules-left": ["sway/workspaces", "sway/mode", "custom/media"], "modules-left": ["sway/workspaces", "sway/mode", "custom/media"],
"modules-center": ["sway/window"], "modules-center": ["sway/window"],
"modules-right": ["idle_inhibitor", "pulseaudio", "network", "cpu", "memory", "temperature", "backlight", "battery", "battery#bat2", "clock", "tray"], "modules-right": ["mpd", "idle_inhibitor", "pulseaudio", "network", "cpu", "memory", "temperature", "backlight", "battery", "battery#bat2", "clock", "tray"],
// Modules configuration // Modules configuration
// "sway/workspaces": { // "sway/workspaces": {
// "disable-scroll": true, // "disable-scroll": true,
@ -26,6 +26,32 @@
"sway/mode": { "sway/mode": {
"format": "<span style=\"italic\">{}</span>" "format": "<span style=\"italic\">{}</span>"
}, },
"mpd": {
"format": "{stateIcon} {consumeIcon}{randomIcon}{repeatIcon}{singleIcon}{artist} - {album} - {title} ({elapsedTime:%M:%S}/{totalTime:%M:%S}) ",
"format-disconnected": "Disconnected ",
"format-stopped": "{consumeIcon}{randomIcon}{repeatIcon}{singleIcon}Stopped ",
"unknown-tag": "N/A",
"interval": 2,
"consume-icons": {
"on": " "
},
"random-icons": {
"off": "<span color=\"#f53c3c\"></span> ",
"on": " "
},
"repeat-icons": {
"on": " "
},
"single-icons": {
"on": "1 "
},
"state-icons": {
"paused": "",
"playing": ""
},
"tooltip-format": "MPD (connected)",
"tooltip-format-disconnected": "MPD (disconnected)"
},
"idle_inhibitor": { "idle_inhibitor": {
"format": "{icon}", "format": "{icon}",
"format-icons": { "format-icons": {
@ -42,7 +68,8 @@
"format-alt": "{:%Y-%m-%d}" "format-alt": "{:%Y-%m-%d}"
}, },
"cpu": { "cpu": {
"format": "{usage}% " "format": "{usage}% ",
"tooltip": false
}, },
"memory": { "memory": {
"format": "{}% " "format": "{}% "

View File

@ -10,6 +10,31 @@ window#waybar {
background: rgba(43, 48, 59, 0.5); background: rgba(43, 48, 59, 0.5);
border-bottom: 3px solid rgba(100, 114, 125, 0.5); border-bottom: 3px solid rgba(100, 114, 125, 0.5);
color: #ffffff; color: #ffffff;
transition-property: background, background-color;
transition-duration: .5s;
}
window#waybar.hidden {
opacity: 0.2;
}
/*
window#waybar.empty {
background: transparent;
}
window#waybar.solo {
background: #FFFFFF;
}
*/
window#waybar.termite {
background-color: #3F3F3F;
}
window#waybar.chromium {
background-color: #DEE1E6;
color: #000000;
border: none;
} }
/* https://github.com/Alexays/Waybar/wiki/FAQ#the-workspace-buttons-have-a-strange-hover-effect */ /* https://github.com/Alexays/Waybar/wiki/FAQ#the-workspace-buttons-have-a-strange-hover-effect */
@ -20,11 +45,19 @@ window#waybar {
border-bottom: 3px solid transparent; border-bottom: 3px solid transparent;
} }
window#waybar.chromium #workspaces button {
color: #3F3F3F;
}
#workspaces button.focused { #workspaces button.focused {
background: #64727D; background: #64727D;
border-bottom: 3px solid #ffffff; border-bottom: 3px solid #ffffff;
} }
window#waybar.chromium #workspaces button.focused {
color: #ffffff;
}
#workspaces button.urgent { #workspaces button.urgent {
background-color: #eb4d4b; background-color: #eb4d4b;
} }
@ -37,6 +70,7 @@ window#waybar {
#clock, #battery, #cpu, #memory, #temperature, #backlight, #network, #pulseaudio, #custom-media, #tray, #mode, #idle_inhibitor { #clock, #battery, #cpu, #memory, #temperature, #backlight, #network, #pulseaudio, #custom-media, #tray, #mode, #idle_inhibitor {
padding: 0 10px; padding: 0 10px;
margin: 0 5px; margin: 0 5px;
color: #ffffff;
} }
#clock { #clock {
@ -70,6 +104,10 @@ window#waybar {
animation-direction: alternate; animation-direction: alternate;
} }
label:focus {
background-color: #000000;
}
#cpu { #cpu {
background: #2ecc71; background: #2ecc71;
color: #000000; color: #000000;
@ -134,3 +172,20 @@ window#waybar {
background-color: #ecf0f1; background-color: #ecf0f1;
color: #2d3436; color: #2d3436;
} }
#mpd {
background: #66cc99;
color: #2a5c45;
}
#mpd.disconnected {
background: #f53c3c;
}
#mpd.stopped {
background: #90b1b1;
}
#mpd.paused {
background: #51a37a;
}

View File

@ -3,34 +3,45 @@
#include <iostream> #include <iostream>
waybar::ALabel::ALabel(const Json::Value& config, const std::string format, uint16_t interval) waybar::ALabel::ALabel(const Json::Value& config, const std::string& format, uint16_t interval)
: config_(config), : config_(config),
format_(config_["format"].isString() ? config_["format"].asString() : format), format_(config_["format"].isString() ? config_["format"].asString() : format),
interval_(config_["interval"] == "once" ? std::chrono::seconds(100000000) : interval_(config_["interval"] == "once"
std::chrono::seconds(config_["interval"].isUInt() ? ? std::chrono::seconds(100000000)
config_["interval"].asUInt() : interval)), default_format_(format_) : std::chrono::seconds(
{ config_["interval"].isUInt() ? config_["interval"].asUInt() : interval)),
default_format_(format_) {
event_box_.add(label_); event_box_.add(label_);
if (config_["max-length"].isUInt()) { if (config_["max-length"].isUInt()) {
label_.set_max_width_chars(config_["max-length"].asUInt()); label_.set_max_width_chars(config_["max-length"].asUInt());
label_.set_ellipsize(Pango::EllipsizeMode::ELLIPSIZE_END); label_.set_ellipsize(Pango::EllipsizeMode::ELLIPSIZE_END);
} }
if (config_["rotate"].isUInt()) {
label_.set_angle(config["rotate"].asUInt());
}
if (config_["format-alt"].isString()) { if (config_["format-alt"].isString()) {
event_box_.add_events(Gdk::BUTTON_PRESS_MASK); event_box_.add_events(Gdk::BUTTON_PRESS_MASK);
event_box_.signal_button_press_event().connect( event_box_.signal_button_press_event().connect(sigc::mem_fun(*this, &ALabel::handleToggle));
sigc::mem_fun(*this, &ALabel::handleToggle));
} }
// configure events' user commands // configure events' user commands
if (config_["on-click"].isString() || config_["on-click-right"].isString()) { if (config_["on-click"].isString() || config_["on-click-right"].isString()) {
event_box_.add_events(Gdk::BUTTON_PRESS_MASK); event_box_.add_events(Gdk::BUTTON_PRESS_MASK);
event_box_.signal_button_press_event().connect( event_box_.signal_button_press_event().connect(sigc::mem_fun(*this, &ALabel::handleToggle));
sigc::mem_fun(*this, &ALabel::handleToggle));
} }
if (config_["on-scroll-up"].isString() || config_["on-scroll-down"].isString()) { if (config_["on-scroll-up"].isString() || config_["on-scroll-down"].isString()) {
event_box_.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK); event_box_.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK);
event_box_.signal_scroll_event().connect( event_box_.signal_scroll_event().connect(sigc::mem_fun(*this, &ALabel::handleScroll));
sigc::mem_fun(*this, &ALabel::handleScroll)); }
}
waybar::ALabel::~ALabel() {
for (const auto& pid : pid_) {
if (pid != -1) {
kill(-pid, 9);
}
} }
} }
@ -40,17 +51,15 @@ auto waybar::ALabel::update() -> void {
bool waybar::ALabel::handleToggle(GdkEventButton* const& e) { bool waybar::ALabel::handleToggle(GdkEventButton* const& e) {
if (config_["on-click"].isString() && e->button == 1) { if (config_["on-click"].isString() && e->button == 1) {
waybar::util::command::forkExec(config_["on-click"].asString()); pid_.push_back(waybar::util::command::forkExec(config_["on-click"].asString()));
} else if (config_["on-click-middle"].isString() && e->button == 2) { } else if (config_["on-click-middle"].isString() && e->button == 2) {
waybar::util::command::forkExec(config_["on-click-middle"].asString()); pid_.push_back(waybar::util::command::forkExec(config_["on-click-middle"].asString()));
} else if (config_["on-click-right"].isString() && e->button == 3) { } else if (config_["on-click-right"].isString() && e->button == 3) {
waybar::util::command::forkExec(config_["on-click-right"].asString()); pid_.push_back(waybar::util::command::forkExec(config_["on-click-right"].asString()));
} else if (config_["on-click-forward"].isString() && e->button == 8) { } else if (config_["on-click-forward"].isString() && e->button == 8) {
waybar::util::command::forkExec(config_["on-click-backward"].asString()); pid_.push_back(waybar::util::command::forkExec(config_["on-click-backward"].asString()));
} else if (config_["on-click-backward"].isString() && e->button == 9) { } else if (config_["on-click-backward"].isString() && e->button == 9) {
waybar::util::command::forkExec(config_["on-click-forward"].asString()); pid_.push_back(waybar::util::command::forkExec(config_["on-click-forward"].asString()));
} }
if (config_["format-alt-click"].isUInt() && e->button == config_["format-alt-click"].asUInt()) { if (config_["format-alt-click"].isUInt() && e->button == config_["format-alt-click"].asUInt()) {
alt_ = !alt_; alt_ = !alt_;
@ -66,10 +75,9 @@ bool waybar::ALabel::handleToggle(GdkEventButton* const& e) {
} }
bool waybar::ALabel::handleScroll(GdkEventScroll* e) { bool waybar::ALabel::handleScroll(GdkEventScroll* e) {
// Avoid concurrent scroll event // Avoid concurrent scroll event
std::lock_guard<std::mutex> lock(mutex_); std::lock_guard<std::mutex> lock(mutex_);
bool direction_up = false; bool direction_up = false;
if (e->direction == GDK_SCROLL_UP) { if (e->direction == GDK_SCROLL_UP) {
direction_up = true; direction_up = true;
@ -79,8 +87,7 @@ bool waybar::ALabel::handleScroll(GdkEventScroll* e) {
} }
if (e->direction == GDK_SCROLL_SMOOTH) { if (e->direction == GDK_SCROLL_SMOOTH) {
gdouble delta_x, delta_y; gdouble delta_x, delta_y;
gdk_event_get_scroll_deltas(reinterpret_cast<const GdkEvent*>(e), gdk_event_get_scroll_deltas(reinterpret_cast<const GdkEvent*>(e), &delta_x, &delta_y);
&delta_x, &delta_y);
if (delta_y < 0) { if (delta_y < 0) {
direction_up = true; direction_up = true;
} else if (delta_y > 0) { } else if (delta_y > 0) {
@ -88,16 +95,15 @@ bool waybar::ALabel::handleScroll(GdkEventScroll* e) {
} }
} }
if (direction_up && config_["on-scroll-up"].isString()) { if (direction_up && config_["on-scroll-up"].isString()) {
waybar::util::command::forkExec(config_["on-scroll-up"].asString()); pid_.push_back(waybar::util::command::forkExec(config_["on-scroll-up"].asString()));
} else if (config_["on-scroll-down"].isString()) { } else if (config_["on-scroll-down"].isString()) {
waybar::util::command::forkExec(config_["on-scroll-down"].asString()); pid_.push_back(waybar::util::command::forkExec(config_["on-scroll-down"].asString()));
} }
dp.emit(); dp.emit();
return true; return true;
} }
std::string waybar::ALabel::getIcon(uint16_t percentage, const std::string& alt) std::string waybar::ALabel::getIcon(uint16_t percentage, const std::string& alt) {
{
auto format_icons = config_["format-icons"]; auto format_icons = config_["format-icons"];
if (format_icons.isObject()) { if (format_icons.isObject()) {
if (!alt.empty() && (format_icons[alt].isString() || format_icons[alt].isArray())) { if (!alt.empty() && (format_icons[alt].isString() || format_icons[alt].isArray())) {
@ -117,8 +123,7 @@ std::string waybar::ALabel::getIcon(uint16_t percentage, const std::string& alt)
return ""; return "";
} }
bool waybar::ALabel::tooltipEnabled() bool waybar::ALabel::tooltipEnabled() {
{
return config_["tooltip"].isBool() ? config_["tooltip"].asBool() : true; return config_["tooltip"].isBool() ? config_["tooltip"].asBool() : true;
} }

View File

@ -1,119 +1,90 @@
#include "bar.hpp" #include "bar.hpp"
#include "client.hpp" #include "client.hpp"
#include "factory.hpp" #include "factory.hpp"
#include "util/json.hpp"
waybar::Bar::Bar(const Client& client, waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config)
std::unique_ptr<struct wl_output *> &&p_output, uint32_t p_wl_name) : output(w_output),
: client(client), window{Gtk::WindowType::WINDOW_TOPLEVEL}, config(w_config),
surface(nullptr), layer_surface(nullptr), window{Gtk::WindowType::WINDOW_TOPLEVEL},
output(std::move(p_output)), wl_name(p_wl_name), surface(nullptr),
left_(Gtk::ORIENTATION_HORIZONTAL, 0), center_(Gtk::ORIENTATION_HORIZONTAL, 0), layer_surface(nullptr),
right_(Gtk::ORIENTATION_HORIZONTAL, 0), box_(Gtk::ORIENTATION_HORIZONTAL, 0) left_(Gtk::ORIENTATION_HORIZONTAL, 0),
{ center_(Gtk::ORIENTATION_HORIZONTAL, 0),
static const struct zxdg_output_v1_listener xdgOutputListener = { right_(Gtk::ORIENTATION_HORIZONTAL, 0),
.logical_position = handleLogicalPosition, box_(Gtk::ORIENTATION_HORIZONTAL, 0) {
.logical_size = handleLogicalSize,
.done = handleDone,
.name = handleName,
.description = handleDescription,
};
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_title("waybar");
window.set_name("waybar"); window.set_name("waybar");
window.set_decorated(false); window.set_decorated(false);
window.set_resizable(false);
setupConfig();
setupCss();
auto wrap = reinterpret_cast<GtkWidget*>(window.gobj()); if (config["position"] == "right" || config["position"] == "left") {
gtk_widget_realize(wrap); height_ = 0;
GdkWindow *gdk_window = gtk_widget_get_window(wrap); width_ = 1;
}
window.set_size_request(width_, height_);
auto gtk_window = window.gobj();
auto gtk_widget = GTK_WIDGET(gtk_window);
gtk_widget_realize(gtk_widget);
auto gdk_window = window.get_window()->gobj();
gdk_wayland_window_set_use_custom_surface(gdk_window); gdk_wayland_window_set_use_custom_surface(gdk_window);
surface = gdk_wayland_window_get_wl_surface(gdk_window); surface = gdk_wayland_window_get_wl_surface(gdk_window);
}
void waybar::Bar::initBar() std::size_t layer = config["layer"] == "top" ? ZWLR_LAYER_SHELL_V1_LAYER_TOP
{ : ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM;
// Converting string to button code rn as to avoid doing it later auto client = waybar::Client::inst();
auto setupAltFormatKeyForModule = [this](const std::string& module_name){
if (config_.isMember(module_name)) {
Json::Value& module = config_[module_name];
if (module.isMember("format-alt")) {
if (module.isMember("format-alt-click")) {
Json::Value& click = module["format-alt-click"];
if (click.isString()) {
std::string str_click = click.asString();
if (str_click == "click-right") {
module["format-alt-click"] = 3u;
} else if (str_click == "click-middle") {
module["format-alt-click"] = 2u;
} else if (str_click == "click-backward") {
module["format-alt-click"] = 8u;
} else if (str_click == "click-forward") {
module["format-alt-click"] = 9u;
} else {
module["format-alt-click"] = 1u; // default click-left
}
} else {
module["format-alt-click"] = 1u;
}
} else {
module["format-alt-click"] = 1u;
}
}
}
};
auto setupAltFormatKeyForModuleList = [this, &setupAltFormatKeyForModule](const char* module_list_name) {
if (config_.isMember(module_list_name)) {
Json::Value& modules = config_[module_list_name];
for (const Json::Value& module_name : modules) {
if (module_name.isString()) {
setupAltFormatKeyForModule(module_name.asString());
}
}
}
};
// Convert to button code for every module that is used.
setupAltFormatKeyForModuleList("modules-left");
setupAltFormatKeyForModuleList("modules-right");
setupAltFormatKeyForModuleList("modules-center");
std::size_t layer = config_["layer"] == "top"
? ZWLR_LAYER_SHELL_V1_LAYER_TOP : ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM;
layer_surface = zwlr_layer_shell_v1_get_layer_surface( layer_surface = zwlr_layer_shell_v1_get_layer_surface(
client.layer_shell, surface, *output, layer, "waybar"); client->layer_shell, surface, output->output, layer, "waybar");
static const struct zwlr_layer_surface_v1_listener layer_surface_listener = { static const struct zwlr_layer_surface_v1_listener layer_surface_listener = {
.configure = layerSurfaceHandleConfigure, .configure = layerSurfaceHandleConfigure,
.closed = layerSurfaceHandleClosed, .closed = layerSurfaceHandleClosed,
}; };
zwlr_layer_surface_v1_add_listener(layer_surface, &layer_surface_listener, this); zwlr_layer_surface_v1_add_listener(layer_surface, &layer_surface_listener, this);
if (config_["position"] == "right" || config_["position"] == "left") { auto height = config["height"].isUInt() ? config["height"].asUInt() : height_;
height_ = 0; auto width = config["width"].isUInt() ? config["width"].asUInt() : width_;
width_ = 30;
}
auto height = config_["height"].isUInt() ? config_["height"].asUInt() : height_; window.signal_configure_event().connect_notify([&](GdkEventConfigure* ev) {
auto width = config_["width"].isUInt() ? config_["width"].asUInt() : width_; auto tmp_height = height_;
auto tmp_width = width_;
if (ev->height > static_cast<int>(height_)) {
// Default minimal value
if (height_ != 1) {
std::cout << fmt::format(MIN_HEIGHT_MSG, height_, ev->height) << std::endl;
}
if (config["height"].isUInt()) {
std::cout << fmt::format(SIZE_DEFINED, "Height") << std::endl;
} else {
tmp_height = ev->height;
}
}
if (ev->width > static_cast<int>(width_)) {
// Default minimal value
if (width_ != 1) {
std::cout << fmt::format(MIN_WIDTH_MSG, width_, ev->width) << std::endl;
}
if (config["width"].isUInt()) {
std::cout << fmt::format(SIZE_DEFINED, "Width") << std::endl;
} else {
tmp_width = ev->width;
}
}
if (tmp_width != width_ || tmp_height != height_) {
zwlr_layer_surface_v1_set_size(layer_surface, tmp_width, tmp_height);
}
});
std::size_t anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP; std::size_t anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP;
if (config_["position"] == "bottom") { if (config["position"] == "bottom") {
anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
} else if (config_["position"] == "left") { } else if (config["position"] == "left") {
anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT; anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT;
} else if (config_["position"] == "right") { } else if (config["position"] == "right") {
anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
} }
if (anchor == ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM || anchor == ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP) { if (anchor == ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM || anchor == ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP) {
anchor |= ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; anchor |= ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
} else if (anchor == ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT || anchor == ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT) { } else if (anchor == ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT ||
anchor == ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT) {
anchor |= ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; anchor |= ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
left_ = Gtk::Box(Gtk::ORIENTATION_VERTICAL, 0); left_ = Gtk::Box(Gtk::ORIENTATION_VERTICAL, 0);
center_ = Gtk::Box(Gtk::ORIENTATION_VERTICAL, 0); center_ = Gtk::Box(Gtk::ORIENTATION_VERTICAL, 0);
@ -127,178 +98,117 @@ void waybar::Bar::initBar()
zwlr_layer_surface_v1_set_size(layer_surface, width, height); zwlr_layer_surface_v1_set_size(layer_surface, width, height);
wl_surface_commit(surface); wl_surface_commit(surface);
wl_display_roundtrip(client->wl_display);
setupWidgets(); setupWidgets();
} }
void waybar::Bar::handleLogicalPosition(void* /*data*/, // Converting string to button code rn as to avoid doing it later
struct zxdg_output_v1* /*zxdg_output_v1*/, int32_t /*x*/, int32_t /*y*/) void waybar::Bar::setupAltFormatKeyForModule(const std::string& module_name) {
{ if (config.isMember(module_name)) {
// Nothing here Json::Value& module = config[module_name];
} if (module.isMember("format-alt")) {
if (module.isMember("format-alt-click")) {
Json::Value& click = module["format-alt-click"];
if (click.isString()) {
std::string str_click = click.asString();
void waybar::Bar::handleLogicalSize(void* /*data*/, if (str_click == "click-right") {
struct zxdg_output_v1* /*zxdg_output_v1*/, int32_t /*width*/, module["format-alt-click"] = 3U;
int32_t /*height*/) } else if (str_click == "click-middle") {
{ module["format-alt-click"] = 2U;
// Nothing here } else if (str_click == "click-backward") {
} module["format-alt-click"] = 8U;
} else if (str_click == "click-forward") {
void waybar::Bar::handleDone(void* /*data*/, module["format-alt-click"] = 9U;
struct zxdg_output_v1* /*zxdg_output_v1*/) } else {
{ module["format-alt-click"] = 1U; // default click-left
// Nothing here }
} } else {
module["format-alt-click"] = 1U;
bool waybar::Bar::isValidOutput(const Json::Value &config) }
{ } else {
bool found = true; module["format-alt-click"] = 1U;
if (config["output"].isArray()) {
bool in_array = false;
for (auto const &output : config["output"]) {
if (output.isString() && output.asString() == output_name) {
in_array = true;
break;
} }
} }
found = in_array;
} }
if (config["output"].isString() && config["output"].asString() != output_name) {
found = false;
}
return found;
} }
void waybar::Bar::handleName(void* data, struct zxdg_output_v1* /*xdg_output*/, void waybar::Bar::setupAltFormatKeyForModuleList(const char* module_list_name) {
const char* name) if (config.isMember(module_list_name)) {
{ Json::Value& modules = config[module_list_name];
auto o = static_cast<waybar::Bar *>(data); for (const Json::Value& module_name : modules) {
o->output_name = name; if (module_name.isString()) {
bool found = true; setupAltFormatKeyForModule(module_name.asString());
if (o->config_.isArray()) {
bool in_array = false;
for (auto const &config : o->config_) {
if (config.isObject() && o->isValidOutput(config)) {
in_array = true;
o->config_ = config;
break;
} }
} }
found = in_array;
} else {
found = o->isValidOutput(o->config_);
}
if (!found) {
wl_output_destroy(*o->output);
zxdg_output_v1_destroy(o->xdg_output_);
} else {
o->initBar();
} }
} }
void waybar::Bar::handleDescription(void* /*data*/, void waybar::Bar::handleSignal(int signal) {
struct zxdg_output_v1* /*zxdg_output_v1*/, const char* /*description*/)
{
// Nothing here
}
void waybar::Bar::handleSignal(int signal)
{
for (auto& module : modules_left_) { for (auto& module : modules_left_) {
auto* custom = dynamic_cast<waybar::modules::Custom*>(module.get()); auto* custom = dynamic_cast<waybar::modules::Custom*>(module.get());
if(custom) custom->refresh(signal); if (custom != nullptr) {
custom->refresh(signal);
}
} }
for (auto& module : modules_center_) { for (auto& module : modules_center_) {
auto* custom = dynamic_cast<waybar::modules::Custom*>(module.get()); auto* custom = dynamic_cast<waybar::modules::Custom*>(module.get());
if(custom) custom->refresh(signal); if (custom != nullptr) {
custom->refresh(signal);
}
} }
for (auto& module : modules_right_) { for (auto& module : modules_right_) {
auto* custom = dynamic_cast<waybar::modules::Custom*>(module.get()); auto* custom = dynamic_cast<waybar::modules::Custom*>(module.get());
if(custom) custom->refresh(signal); if (custom != nullptr) {
custom->refresh(signal);
}
} }
} }
void waybar::Bar::layerSurfaceHandleConfigure(void* data, void waybar::Bar::layerSurfaceHandleConfigure(void* data, struct zwlr_layer_surface_v1* surface,
struct zwlr_layer_surface_v1* surface, uint32_t serial, uint32_t width, uint32_t serial, uint32_t width, uint32_t height) {
uint32_t height) auto o = static_cast<waybar::Bar*>(data);
{
auto o = static_cast<waybar::Bar *>(data);
zwlr_layer_surface_v1_ack_configure(surface, serial);
if (width != o->width_ || height != o->height_) { if (width != o->width_ || height != o->height_) {
o->width_ = width; o->width_ = width;
o->height_ = height; o->height_ = height;
o->window.set_size_request(o->width_, o->height_); o->window.set_size_request(o->width_, o->height_);
o->window.resize(o->width_, o->height_); o->window.resize(o->width_, o->height_);
zwlr_layer_surface_v1_set_exclusive_zone(o->layer_surface,
int min_width, min_height; o->vertical ? o->width_ : o->height_);
o->window.get_size(min_width, min_height); std::cout << fmt::format(BAR_SIZE_MSG,
if (o->height_ < static_cast<uint32_t>(min_height)) { o->width_ == 1 ? "auto" : std::to_string(o->width_),
std::cout << fmt::format("Requested height: {} exceeds the minimum \ o->height_ == 1 ? "auto" : std::to_string(o->height_),
height: {} required by the modules", o->height_, min_height) << std::endl; o->output->name)
o->height_ = min_height; << std::endl;
}
if (o->width_ < static_cast<uint32_t>(min_width)) {
std::cout << fmt::format("Requested width: {} exceeds the minimum \
width: {} required by the modules", o->height_, min_width) << std::endl;
o->width_ = min_width;
}
std::cout << fmt::format(
"Bar configured (width: {}, height: {}) for output: {}",
o->width_, o->height_, o->output_name) << std::endl;
wl_surface_commit(o->surface); wl_surface_commit(o->surface);
} }
zwlr_layer_surface_v1_ack_configure(surface, serial);
} }
void waybar::Bar::layerSurfaceHandleClosed(void* data, void waybar::Bar::layerSurfaceHandleClosed(void* data, struct zwlr_layer_surface_v1* /*surface*/) {
struct zwlr_layer_surface_v1* /*surface*/) auto o = static_cast<waybar::Bar*>(data);
{
auto o = static_cast<waybar::Bar *>(data);
zwlr_layer_surface_v1_destroy(o->layer_surface); zwlr_layer_surface_v1_destroy(o->layer_surface);
wl_output_destroy(*o->output);
zxdg_output_v1_destroy(o->xdg_output_);
o->modules_left_.clear(); o->modules_left_.clear();
o->modules_center_.clear(); o->modules_center_.clear();
o->modules_right_.clear(); o->modules_right_.clear();
} }
auto waybar::Bar::toggle() -> void auto waybar::Bar::toggle() -> void {
{
visible = !visible; visible = !visible;
auto zone = visible ? height_ : 0; auto zone = visible ? height_ : 0;
if (!visible) {
window.get_style_context()->add_class("hidden");
} else {
window.get_style_context()->remove_class("hidden");
}
zwlr_layer_surface_v1_set_exclusive_zone(layer_surface, zone); zwlr_layer_surface_v1_set_exclusive_zone(layer_surface, zone);
wl_surface_commit(surface); wl_surface_commit(surface);
} }
auto waybar::Bar::setupConfig() -> void void waybar::Bar::getModules(const Factory& factory, const std::string& pos) {
{ if (config[pos].isArray()) {
std::ifstream file(client.config_file); for (const auto& name : config[pos]) {
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>());
util::JsonParser parser;
config_ = parser.parse(str);
}
auto waybar::Bar::setupCss() -> void
{
css_provider_ = Gtk::CssProvider::create();
style_context_ = Gtk::StyleContext::create();
// Load our css file, wherever that may be hiding
if (css_provider_->load_from_path(client.css_file)) {
Glib::RefPtr<Gdk::Screen> screen = window.get_screen();
style_context_->add_provider_for_screen(screen, css_provider_,
GTK_STYLE_PROVIDER_PRIORITY_USER);
}
}
void waybar::Bar::getModules(const Factory& factory, const std::string& pos)
{
if (config_[pos].isArray()) {
for (const auto &name : config_[pos]) {
try { try {
auto module = factory.makeModule(name.asString()); auto module = factory.makeModule(name.asString());
if (pos == "modules-left") { if (pos == "modules-left") {
@ -324,14 +234,18 @@ void waybar::Bar::getModules(const Factory& factory, const std::string& pos)
} }
} }
auto waybar::Bar::setupWidgets() -> void auto waybar::Bar::setupWidgets() -> void {
{
window.add(box_); window.add(box_);
box_.pack_start(left_, true, true); box_.pack_start(left_, true, true);
box_.set_center_widget(center_); box_.set_center_widget(center_);
box_.pack_end(right_, true, true); box_.pack_end(right_, true, true);
Factory factory(*this, config_); // Convert to button code for every module that is used.
setupAltFormatKeyForModuleList("modules-left");
setupAltFormatKeyForModuleList("modules-right");
setupAltFormatKeyForModuleList("modules-center");
Factory factory(*this, config);
getModules(factory, "modules-left"); getModules(factory, "modules-left");
getModules(factory, "modules-center"); getModules(factory, "modules-center");
getModules(factory, "modules-right"); getModules(factory, "modules-right");

View File

@ -1,25 +1,18 @@
#include "client.hpp" #include "client.hpp"
#include "util/clara.hpp" #include <fstream>
#include <iostream> #include <iostream>
#include "util/clara.hpp"
#include "util/json.hpp"
waybar::Client::Client(int argc, char* argv[]) waybar::Client *waybar::Client::inst() {
: gtk_main(argc, argv), static auto c = new Client();
gdk_display(Gdk::Display::get_default()) return c;
{
if (!gdk_display) {
throw std::runtime_error("Can't find display");
}
if (!GDK_IS_WAYLAND_DISPLAY(gdk_display->gobj())) {
throw std::runtime_error("Bar need to run under Wayland");
}
wl_display = gdk_wayland_display_get_wl_display(gdk_display->gobj());
} }
const std::string waybar::Client::getValidPath(std::vector<std::string> paths) const std::string waybar::Client::getValidPath(const std::vector<std::string> &paths) {
{
wordexp_t p; wordexp_t p;
for (const std::string &path: paths) { for (const std::string &path : paths) {
if (wordexp(path.c_str(), &p, 0) == 0) { if (wordexp(path.c_str(), &p, 0) == 0) {
if (access(*p.we_wordv, F_OK) == 0) { if (access(*p.we_wordv, F_OK) == 0) {
std::string result = *p.we_wordv; std::string result = *p.we_wordv;
@ -33,98 +26,226 @@ const std::string waybar::Client::getValidPath(std::vector<std::string> paths)
return std::string(); return std::string();
} }
void waybar::Client::handleGlobal(void *data, struct wl_registry *registry, void waybar::Client::handleGlobal(void *data, struct wl_registry *registry, uint32_t name,
uint32_t name, const char *interface, uint32_t version) const char *interface, uint32_t version) {
{ auto client = static_cast<Client *>(data);
auto o = static_cast<waybar::Client *>(data);
if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
o->layer_shell = static_cast<struct zwlr_layer_shell_v1 *>( client->layer_shell = static_cast<struct zwlr_layer_shell_v1 *>(
wl_registry_bind(registry, name, &zwlr_layer_shell_v1_interface, version)); wl_registry_bind(registry, name, &zwlr_layer_shell_v1_interface, version));
} else if (strcmp(interface, wl_output_interface.name) == 0) { } else if (strcmp(interface, wl_output_interface.name) == 0) {
auto output = std::make_unique<struct wl_output *>(); auto wl_output = static_cast<struct wl_output *>(
*output = static_cast<struct wl_output *>(wl_registry_bind(registry, name, wl_registry_bind(registry, name, &wl_output_interface, version));
&wl_output_interface, version)); client->outputs_.emplace_back(new struct waybar_output({wl_output, "", name, nullptr}));
if (o->xdg_output_manager != nullptr) { client->handleOutput(client->outputs_.back());
o->bars.emplace_back(std::make_unique<Bar>(*o, std::move(output), name)); } else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0 &&
} version >= ZXDG_OUTPUT_V1_NAME_SINCE_VERSION) {
} else if (strcmp(interface, wl_seat_interface.name) == 0) { client->xdg_output_manager = static_cast<struct zxdg_output_manager_v1 *>(wl_registry_bind(
o->seat = static_cast<struct wl_seat *>(wl_registry_bind(registry, name, registry, name, &zxdg_output_manager_v1_interface, ZXDG_OUTPUT_V1_NAME_SINCE_VERSION));
&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));
} else if (strcmp(interface, zwp_idle_inhibit_manager_v1_interface.name) == 0) { } else if (strcmp(interface, zwp_idle_inhibit_manager_v1_interface.name) == 0) {
o->idle_inhibit_manager = static_cast<struct zwp_idle_inhibit_manager_v1 *>( client->idle_inhibit_manager = static_cast<struct zwp_idle_inhibit_manager_v1 *>(
wl_registry_bind(registry, name, wl_registry_bind(registry, name, &zwp_idle_inhibit_manager_v1_interface, 1));
&zwp_idle_inhibit_manager_v1_interface, 1));
} }
} }
void waybar::Client::handleGlobalRemove(void* data, void waybar::Client::handleGlobalRemove(void * data, struct wl_registry * /*registry*/,
struct wl_registry* /*registry*/, uint32_t name) uint32_t name) {
{ auto client = static_cast<Client *>(data);
auto o = static_cast<waybar::Client *>(data); for (auto it = client->bars.begin(); it != client->bars.end();) {
for (auto it = o->bars.begin(); it != o->bars.end(); ++it) { if ((*it)->output->wl_name == name) {
if ((*it)->wl_name == name) { auto output_name = (*it)->output->name;
auto output_name = (*it)->output_name; (*it)->window.close();
o->bars.erase(it); it = client->bars.erase(it);
std::cout << "Bar removed from output: " + output_name << std::endl; std::cout << "Bar removed from output: " + output_name << std::endl;
break; } else {
++it;
} }
} }
auto it = std::find_if(client->outputs_.begin(),
client->outputs_.end(),
[&name](const auto &output) { return output->wl_name == name; });
if (it != client->outputs_.end()) {
zxdg_output_v1_destroy((*it)->xdg_output);
wl_output_destroy((*it)->output);
client->outputs_.erase(it);
}
} }
void waybar::Client::setupConfigs(const std::string& config, const std::string& style) void waybar::Client::handleOutput(std::unique_ptr<struct waybar_output> &output) {
{ static const struct zxdg_output_v1_listener xdgOutputListener = {
config_file = config.empty() ? getValidPath({ .logical_position = handleLogicalPosition,
"$XDG_CONFIG_HOME/waybar/config", .logical_size = handleLogicalSize,
"$HOME/.config/waybar/config", .done = handleDone,
"$HOME/waybar/config", .name = handleName,
"/etc/xdg/waybar/config", .description = handleDescription,
"./resources/config", };
}) : config; output->xdg_output = zxdg_output_manager_v1_get_xdg_output(xdg_output_manager, output->output);
css_file = style.empty() ? getValidPath({ zxdg_output_v1_add_listener(output->xdg_output, &xdgOutputListener, &output->wl_name);
"$XDG_CONFIG_HOME/waybar/style.css", }
"$HOME/.config/waybar/style.css",
"$HOME/waybar/style.css", void waybar::Client::handleLogicalPosition(void * /*data*/,
"/etc/xdg/waybar/style.css", struct zxdg_output_v1 * /*zxdg_output_v1*/,
"./resources/style.css", int32_t /*x*/, int32_t /*y*/) {
}) : style; // Nothing here
if (css_file.empty() || config_file.empty()) { }
void waybar::Client::handleLogicalSize(void * /*data*/, struct zxdg_output_v1 * /*zxdg_output_v1*/,
int32_t /*width*/, int32_t /*height*/) {
// Nothing here
}
void waybar::Client::handleDone(void * /*data*/, struct zxdg_output_v1 * /*zxdg_output_v1*/) {
// Nothing here
}
bool waybar::Client::isValidOutput(const Json::Value & config,
std::unique_ptr<struct waybar_output> &output) {
bool found = true;
if (config["output"].isArray()) {
bool in_array = false;
for (auto const &output_conf : config["output"]) {
if (output_conf.isString() && output_conf.asString() == output->name) {
in_array = true;
break;
}
}
found = in_array;
}
if (config["output"].isString() && config["output"].asString() != output->name) {
found = false;
}
return found;
}
std::unique_ptr<struct waybar::waybar_output> &waybar::Client::getOutput(uint32_t wl_name) {
auto it = std::find_if(outputs_.begin(), outputs_.end(), [&wl_name](const auto &output) {
return output->wl_name == wl_name;
});
if (it == outputs_.end()) {
throw std::runtime_error("Unable to find valid output");
}
return *it;
}
std::vector<Json::Value> waybar::Client::getOutputConfigs(
std::unique_ptr<struct waybar_output> &output) {
std::vector<Json::Value> configs;
if (config_.isArray()) {
for (auto const &config : config_) {
if (config.isObject() && isValidOutput(config, output)) {
configs.push_back(config);
}
}
} else if (isValidOutput(config_, output)) {
configs.push_back(config_);
}
return configs;
}
void waybar::Client::handleName(void * data, struct zxdg_output_v1 * /*xdg_output*/,
const char *name) {
auto wl_name = *static_cast<uint32_t *>(data);
auto client = waybar::Client::inst();
try {
auto &output = client->getOutput(wl_name);
output->name = name;
auto configs = client->getOutputConfigs(output);
if (configs.empty()) {
wl_output_destroy(output->output);
zxdg_output_v1_destroy(output->xdg_output);
} else {
for (const auto &config : configs) {
client->bars.emplace_back(std::make_unique<Bar>(output.get(), config));
Glib::RefPtr<Gdk::Screen> screen = client->bars.back()->window.get_screen();
client->style_context_->add_provider_for_screen(
screen, client->css_provider_, GTK_STYLE_PROVIDER_PRIORITY_USER);
}
}
} catch (const std::exception &e) {
std::cerr << e.what() << std::endl;
}
}
void waybar::Client::handleDescription(void * /*data*/, struct zxdg_output_v1 * /*zxdg_output_v1*/,
const char * /*description*/) {
// Nothing here
}
void waybar::Client::setupConfigs(const std::string &config, const std::string &style) {
config_file_ = config.empty() ? getValidPath({
"$XDG_CONFIG_HOME/waybar/config",
"$HOME/.config/waybar/config",
"$HOME/waybar/config",
"/etc/xdg/waybar/config",
"./resources/config",
})
: config;
css_file_ = style.empty() ? getValidPath({
"$XDG_CONFIG_HOME/waybar/style.css",
"$HOME/.config/waybar/style.css",
"$HOME/waybar/style.css",
"/etc/xdg/waybar/style.css",
"./resources/style.css",
})
: style;
if (css_file_.empty() || config_file_.empty()) {
throw std::runtime_error("Missing required resources files"); throw std::runtime_error("Missing required resources files");
} }
std::cout << "Resources files: " + config_file + ", " + css_file << std::endl; std::cout << "Resources files: " + config_file_ + ", " + css_file_ << std::endl;
} }
void waybar::Client::bindInterfaces() auto waybar::Client::setupConfig() -> void {
{ std::ifstream file(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>());
util::JsonParser parser;
config_ = parser.parse(str);
}
auto waybar::Client::setupCss() -> void {
css_provider_ = Gtk::CssProvider::create();
style_context_ = Gtk::StyleContext::create();
// Load our css file, wherever that may be hiding
if (!css_provider_->load_from_path(css_file_)) {
throw std::runtime_error("Can't open style file");
}
}
void waybar::Client::bindInterfaces() {
registry = wl_display_get_registry(wl_display); registry = wl_display_get_registry(wl_display);
static const struct wl_registry_listener registry_listener = { static const struct wl_registry_listener registry_listener = {
.global = handleGlobal, .global = handleGlobal,
.global_remove = handleGlobalRemove, .global_remove = handleGlobalRemove,
}; };
wl_registry_add_listener(registry, &registry_listener, this); wl_registry_add_listener(registry, &registry_listener, this);
wl_display_roundtrip(wl_display); wl_display_roundtrip(wl_display);
if (!layer_shell || !seat || !xdg_output_manager) { if (layer_shell == nullptr || xdg_output_manager == nullptr) {
throw std::runtime_error("Failed to acquire required resources."); throw std::runtime_error("Failed to acquire required resources.");
} }
wl_display_roundtrip(wl_display);
} }
int waybar::Client::main(int argc, char* argv[]) int waybar::Client::main(int argc, char *argv[]) {
{ gtk_app = Gtk::Application::create(argc, argv, "fr.arouillard.waybar");
bool show_help = false; gdk_display = Gdk::Display::get_default();
bool show_version = false; if (!gdk_display) {
throw std::runtime_error("Can't find display");
}
if (!GDK_IS_WAYLAND_DISPLAY(gdk_display->gobj())) {
throw std::runtime_error("Bar need to run under Wayland");
}
wl_display = gdk_wayland_display_get_wl_display(gdk_display->gobj());
bool show_help = false;
bool show_version = false;
std::string config; std::string config;
std::string style; std::string style;
std::string bar_id; std::string bar_id;
auto cli = clara::detail::Help(show_help) auto cli = clara::detail::Help(show_help) |
| clara::detail::Opt(show_version)["-v"]["--version"]("Show version") clara::detail::Opt(show_version)["-v"]["--version"]("Show version") |
| clara::detail::Opt(config, "config")["-c"]["--config"]("Config path") clara::detail::Opt(config, "config")["-c"]["--config"]("Config path") |
| clara::detail::Opt(style, "style")["-s"]["--style"]("Style path") clara::detail::Opt(style, "style")["-s"]["--style"]("Style path") |
| clara::detail::Opt(bar_id, "id")["-b"]["--bar"]("Bar id"); clara::detail::Opt(bar_id, "id")["-b"]["--bar"]("Bar id");
auto res = cli.parse(clara::detail::Args(argc, argv)); auto res = cli.parse(clara::detail::Args(argc, argv));
if (!res) { if (!res) {
std::cerr << "Error in command line: " << res.errorMessage() << std::endl; std::cerr << "Error in command line: " << res.errorMessage() << std::endl;
@ -139,14 +260,16 @@ int waybar::Client::main(int argc, char* argv[])
return 0; return 0;
} }
setupConfigs(config, style); setupConfigs(config, style);
setupConfig();
setupCss();
bindInterfaces(); bindInterfaces();
gtk_main.run(); gtk_app->hold();
gtk_app->run();
bars.clear(); bars.clear();
zxdg_output_manager_v1_destroy(xdg_output_manager); zxdg_output_manager_v1_destroy(xdg_output_manager);
zwlr_layer_shell_v1_destroy(layer_shell); zwlr_layer_shell_v1_destroy(layer_shell);
zwp_idle_inhibit_manager_v1_destroy(idle_inhibit_manager); zwp_idle_inhibit_manager_v1_destroy(idle_inhibit_manager);
wl_registry_destroy(registry); wl_registry_destroy(registry);
wl_seat_destroy(seat);
wl_display_disconnect(wl_display); wl_display_disconnect(wl_display);
return 0; return 0;
} }

View File

@ -1,19 +1,16 @@
#include "factory.hpp" #include "factory.hpp"
waybar::Factory::Factory(const Bar& bar, const Json::Value& config) waybar::Factory::Factory(const Bar& bar, const Json::Value& config) : bar_(bar), config_(config) {}
: bar_(bar), config_(config)
{}
waybar::IModule* waybar::Factory::makeModule(const std::string &name) const waybar::IModule* waybar::Factory::makeModule(const std::string& name) const {
{
try { try {
auto hash_pos = name.find("#"); auto hash_pos = name.find('#');
auto ref = name.substr(0, hash_pos); auto ref = name.substr(0, hash_pos);
auto id = hash_pos != std::string::npos ? name.substr(hash_pos + 1) : ""; auto id = hash_pos != std::string::npos ? name.substr(hash_pos + 1) : "";
if (ref == "battery") { if (ref == "battery") {
return new waybar::modules::Battery(id, config_[name]); return new waybar::modules::Battery(id, config_[name]);
} }
#ifdef HAVE_SWAY #ifdef HAVE_SWAY
if (ref == "sway/mode") { if (ref == "sway/mode") {
return new waybar::modules::sway::Mode(id, bar_, config_[name]); return new waybar::modules::sway::Mode(id, bar_, config_[name]);
} }
@ -23,7 +20,7 @@ waybar::IModule* waybar::Factory::makeModule(const std::string &name) const
if (ref == "sway/window") { if (ref == "sway/window") {
return new waybar::modules::sway::Window(id, bar_, config_[name]); return new waybar::modules::sway::Window(id, bar_, config_[name]);
} }
#endif #endif
if (ref == "idle_inhibitor") { if (ref == "idle_inhibitor") {
return new waybar::modules::IdleInhibitor(id, bar_, config_[name]); return new waybar::modules::IdleInhibitor(id, bar_, config_[name]);
} }
@ -36,26 +33,31 @@ waybar::IModule* waybar::Factory::makeModule(const std::string &name) const
if (ref == "clock") { if (ref == "clock") {
return new waybar::modules::Clock(id, config_[name]); return new waybar::modules::Clock(id, config_[name]);
} }
#ifdef HAVE_DBUSMENU #ifdef HAVE_DBUSMENU
if (ref == "tray") { if (ref == "tray") {
return new waybar::modules::SNI::Tray(id, bar_, config_[name]); return new waybar::modules::SNI::Tray(id, bar_, config_[name]);
} }
#endif #endif
#ifdef HAVE_LIBNL #ifdef HAVE_LIBNL
if (ref == "network") { if (ref == "network") {
return new waybar::modules::Network(id, config_[name]); return new waybar::modules::Network(id, config_[name]);
} }
#endif #endif
#ifdef HAVE_LIBUDEV #ifdef HAVE_LIBUDEV
if (ref == "backlight") { if (ref == "backlight") {
return new waybar::modules::Backlight(id, config_[name]); return new waybar::modules::Backlight(id, config_[name]);
} }
#endif #endif
#ifdef HAVE_LIBPULSE #ifdef HAVE_LIBPULSE
if (ref == "pulseaudio") { if (ref == "pulseaudio") {
return new waybar::modules::Pulseaudio(id, config_[name]); return new waybar::modules::Pulseaudio(id, config_[name]);
} }
#endif #endif
#ifdef HAVE_LIBMPDCLIENT
if (ref == "mpd") {
return new waybar::modules::MPD(id, config_[name]);
}
#endif
if (ref == "temperature") { if (ref == "temperature") {
return new waybar::modules::Temperature(id, config_[name]); return new waybar::modules::Temperature(id, config_[name]);
} }

View File

@ -1,33 +1,27 @@
#include "client.hpp"
#include <csignal> #include <csignal>
#include <iostream> #include <iostream>
#include "client.hpp"
namespace waybar { int main(int argc, char* argv[]) {
static Client* client;
} // namespace waybar
int main(int argc, char* argv[])
{
try { try {
waybar::Client c(argc, argv); auto client = waybar::Client::inst();
waybar::client = &c; std::signal(SIGUSR1, [](int /*signal*/) {
std::signal(SIGUSR1, [] (int /*signal*/) { for (auto& bar : waybar::Client::inst()->bars) {
for (auto& bar : waybar::client->bars) {
bar->toggle(); bar->toggle();
} }
}); });
for (int sig = SIGRTMIN + 1; sig <= SIGRTMAX; ++sig) { for (int sig = SIGRTMIN + 1; sig <= SIGRTMAX; ++sig) {
std::signal(sig, [] (int sig/*signal*/) { std::signal(sig, [](int sig) {
for (auto& bar : waybar::client->bars) { for (auto& bar : waybar::Client::inst()->bars) {
bar->handleSignal(sig); bar->handleSignal(sig);
} }
}); });
} }
return c.main(argc, argv); auto ret = client->main(argc, argv);
delete client;
return ret;
} catch (const std::exception& e) { } catch (const std::exception& e) {
std::cerr << e.what() << std::endl; std::cerr << e.what() << std::endl;
return 1; return 1;

View File

@ -13,7 +13,7 @@
namespace { namespace {
class FileDescriptor { class FileDescriptor {
public: public:
explicit FileDescriptor(int fd) : fd_(fd) {} explicit FileDescriptor(int fd) : fd_(fd) {}
FileDescriptor(const FileDescriptor &other) = delete; FileDescriptor(const FileDescriptor &other) = delete;
FileDescriptor(FileDescriptor &&other) noexcept = delete; FileDescriptor(FileDescriptor &&other) noexcept = delete;
@ -28,7 +28,7 @@ public:
} }
int get() const { return fd_; } int get() const { return fd_; }
private: private:
int fd_; int fd_;
}; };
@ -60,9 +60,7 @@ void check_neq(int rc, int bad_rc, const char *message = "neq, rc was: ") {
} }
} }
void check0(int rc, const char *message = "rc wasn't 0") { void check0(int rc, const char *message = "rc wasn't 0") { check_eq(rc, 0, message); }
check_eq(rc, 0, message);
}
void check_gte(int rc, int gte, const char *message = "rc was: ") { void check_gte(int rc, int gte, const char *message = "rc was: ") {
if (rc < gte) { if (rc < gte) {
@ -75,41 +73,33 @@ void check_nn(const void *ptr, const char *message = "ptr was null") {
throw std::runtime_error(message); throw std::runtime_error(message);
} }
} }
} // namespace } // namespace
waybar::modules::Backlight::BacklightDev::BacklightDev(std::string name, waybar::modules::Backlight::BacklightDev::BacklightDev(std::string name, int actual, int max)
int actual, int max)
: name_(std::move(name)), actual_(actual), max_(max) {} : name_(std::move(name)), actual_(actual), max_(max) {}
std::string_view waybar::modules::Backlight::BacklightDev::name() const { std::string_view waybar::modules::Backlight::BacklightDev::name() const { return name_; }
return name_;
}
int waybar::modules::Backlight::BacklightDev::get_actual() const { int waybar::modules::Backlight::BacklightDev::get_actual() const { return actual_; }
return actual_;
}
void waybar::modules::Backlight::BacklightDev::set_actual(int actual) { void waybar::modules::Backlight::BacklightDev::set_actual(int actual) { actual_ = actual; }
actual_ = actual;
}
int waybar::modules::Backlight::BacklightDev::get_max() const { return max_; } int waybar::modules::Backlight::BacklightDev::get_max() const { return max_; }
void waybar::modules::Backlight::BacklightDev::set_max(int max) { max_ = max; } void waybar::modules::Backlight::BacklightDev::set_max(int max) { max_ = max; }
waybar::modules::Backlight::Backlight(const std::string &name, waybar::modules::Backlight::Backlight(const std::string &name, const Json::Value &config)
const Json::Value &config) : ALabel(config, "{percent}%", 2),
: ALabel(config, "{percent}%", 2), name_(name), name_(name),
preferred_device_( preferred_device_(config["device"].isString() ? config["device"].asString() : "") {
config["device"].isString() ? config["device"].asString() : "") {
label_.set_name("backlight"); label_.set_name("backlight");
// Get initial state // Get initial state
{ {
std::unique_ptr<udev, UdevDeleter> udev_check{udev_new()}; std::unique_ptr<udev, UdevDeleter> udev_check{udev_new()};
check_nn(udev_check.get(), "Udev check new failed"); check_nn(udev_check.get(), "Udev check new failed");
enumerate_devices(devices_.begin(), devices_.end(), enumerate_devices(
std::back_inserter(devices_), udev_check.get()); devices_.begin(), devices_.end(), std::back_inserter(devices_), udev_check.get());
if (devices_.empty()) { if (devices_.empty()) {
throw std::runtime_error("No backlight found"); throw std::runtime_error("No backlight found");
} }
@ -123,28 +113,26 @@ waybar::modules::Backlight::Backlight(const std::string &name,
std::unique_ptr<udev_monitor, UdevMonitorDeleter> mon{ std::unique_ptr<udev_monitor, UdevMonitorDeleter> mon{
udev_monitor_new_from_netlink(udev.get(), "udev")}; udev_monitor_new_from_netlink(udev.get(), "udev")};
check_nn(mon.get(), "udev monitor new failed"); check_nn(mon.get(), "udev monitor new failed");
check_gte(udev_monitor_filter_add_match_subsystem_devtype( check_gte(udev_monitor_filter_add_match_subsystem_devtype(mon.get(), "backlight", nullptr),
mon.get(), "backlight", nullptr), 0,
0, "udev failed to add monitor filter: "); "udev failed to add monitor filter: ");
udev_monitor_enable_receiving(mon.get()); udev_monitor_enable_receiving(mon.get());
auto udev_fd = udev_monitor_get_fd(mon.get()); auto udev_fd = udev_monitor_get_fd(mon.get());
auto epoll_fd = FileDescriptor{epoll_create1(0)}; auto epoll_fd = FileDescriptor{epoll_create1(EPOLL_CLOEXEC)};
check_neq(epoll_fd.get(), -1, "epoll init failed: "); check_neq(epoll_fd.get(), -1, "epoll init failed: ");
epoll_event ctl_event; epoll_event ctl_event{};
ctl_event.events = EPOLLIN; ctl_event.events = EPOLLIN;
ctl_event.data.fd = udev_fd; ctl_event.data.fd = udev_fd;
check0( check0(epoll_ctl(epoll_fd.get(), EPOLL_CTL_ADD, ctl_event.data.fd, &ctl_event),
epoll_ctl(epoll_fd.get(), EPOLL_CTL_ADD, ctl_event.data.fd, &ctl_event), "epoll_ctl failed: {}");
"epoll_ctl failed: {}");
epoll_event events[EPOLL_MAX_EVENTS]; epoll_event events[EPOLL_MAX_EVENTS];
while (udev_thread_.isRunning()) { while (udev_thread_.isRunning()) {
const int event_count = const int event_count = epoll_wait(
epoll_wait(epoll_fd.get(), events, EPOLL_MAX_EVENTS, epoll_fd.get(), events, EPOLL_MAX_EVENTS, std::chrono::milliseconds{interval_}.count());
std::chrono::milliseconds{interval_}.count());
if (!udev_thread_.isRunning()) { if (!udev_thread_.isRunning()) {
break; break;
} }
@ -156,17 +144,14 @@ waybar::modules::Backlight::Backlight(const std::string &name,
for (int i = 0; i < event_count; ++i) { for (int i = 0; i < event_count; ++i) {
const auto &event = events[i]; const auto &event = events[i];
check_eq(event.data.fd, udev_fd, "unexpected udev fd"); check_eq(event.data.fd, udev_fd, "unexpected udev fd");
std::unique_ptr<udev_device, UdevDeviceDeleter> dev{ std::unique_ptr<udev_device, UdevDeviceDeleter> dev{udev_monitor_receive_device(mon.get())};
udev_monitor_receive_device(mon.get())};
check_nn(dev.get(), "epoll dev was null"); check_nn(dev.get(), "epoll dev was null");
upsert_device(devices.begin(), devices.end(), upsert_device(devices.begin(), devices.end(), std::back_inserter(devices), dev.get());
std::back_inserter(devices), dev.get());
} }
// Refresh state if timed out // Refresh state if timed out
if (event_count == 0) { if (event_count == 0) {
enumerate_devices(devices.begin(), devices.end(), enumerate_devices(devices.begin(), devices.end(), std::back_inserter(devices), udev.get());
std::back_inserter(devices), udev.get());
} }
{ {
std::scoped_lock<std::mutex> lock(udev_thread_mutex_); std::scoped_lock<std::mutex> lock(udev_thread_mutex_);
@ -186,19 +171,16 @@ auto waybar::modules::Backlight::update() -> void {
devices = devices_; devices = devices_;
} }
const auto best = const auto best = best_device(devices.cbegin(), devices.cend(), preferred_device_);
best_device(devices.cbegin(), devices.cend(), preferred_device_);
if (best != nullptr) { if (best != nullptr) {
if (previous_best_.has_value() && previous_best_.value() == *best && if (previous_best_.has_value() && previous_best_.value() == *best &&
!previous_format_.empty() && previous_format_ == format_) { !previous_format_.empty() && previous_format_ == format_) {
return; return;
} }
const auto percent = const auto percent = best->get_max() == 0 ? 100 : best->get_actual() * 100 / best->get_max();
best->get_max() == 0 ? 100 : best->get_actual() * 100 / best->get_max(); label_.set_markup(fmt::format(
label_.set_markup(fmt::format(format_, format_, fmt::arg("percent", std::to_string(percent)), fmt::arg("icon", getIcon(percent))));
fmt::arg("percent", std::to_string(percent)),
fmt::arg("icon", getIcon(percent))));
} else { } else {
if (!previous_best_.has_value()) { if (!previous_best_.has_value()) {
return; return;
@ -210,28 +192,22 @@ auto waybar::modules::Backlight::update() -> void {
} }
template <class ForwardIt> template <class ForwardIt>
const waybar::modules::Backlight::BacklightDev * const waybar::modules::Backlight::BacklightDev *waybar::modules::Backlight::best_device(
waybar::modules::Backlight::best_device(ForwardIt first, ForwardIt last, ForwardIt first, ForwardIt last, std::string_view preferred_device) {
std::string_view preferred_device) { const auto found = std::find_if(
const auto found = first, last, [preferred_device](const auto &dev) { return dev.name() == preferred_device; });
std::find_if(first, last, [preferred_device](const auto &dev) {
return dev.name() == preferred_device;
});
if (found != last) { if (found != last) {
return &(*found); return &(*found);
} }
const auto max = const auto max = std::max_element(
std::max_element(first, last, [](const auto &l, const auto &r) { first, last, [](const auto &l, const auto &r) { return l.get_max() < r.get_max(); });
return l.get_max() < r.get_max();
});
return max == last ? nullptr : &(*max); return max == last ? nullptr : &(*max);
} }
template <class ForwardIt, class Inserter> template <class ForwardIt, class Inserter>
void waybar::modules::Backlight::upsert_device(ForwardIt first, ForwardIt last, void waybar::modules::Backlight::upsert_device(ForwardIt first, ForwardIt last, Inserter inserter,
Inserter inserter,
udev_device *dev) { udev_device *dev) {
const char *name = udev_device_get_sysname(dev); const char *name = udev_device_get_sysname(dev);
check_nn(name); check_nn(name);
@ -244,9 +220,8 @@ void waybar::modules::Backlight::upsert_device(ForwardIt first, ForwardIt last,
check_nn(max); check_nn(max);
const int max_int = std::stoi(max); const int max_int = std::stoi(max);
auto found = std::find_if(first, last, [name](const auto &device) { auto found =
return device.name() == name; std::find_if(first, last, [name](const auto &device) { return device.name() == name; });
});
if (found != last) { if (found != last) {
found->set_actual(actual_int); found->set_actual(actual_int);
found->set_max(max_int); found->set_max(max_int);
@ -257,21 +232,16 @@ void waybar::modules::Backlight::upsert_device(ForwardIt first, ForwardIt last,
} }
template <class ForwardIt, class Inserter> template <class ForwardIt, class Inserter>
void waybar::modules::Backlight::enumerate_devices(ForwardIt first, void waybar::modules::Backlight::enumerate_devices(ForwardIt first, ForwardIt last,
ForwardIt last, Inserter inserter, udev *udev) {
Inserter inserter, std::unique_ptr<udev_enumerate, UdevEnumerateDeleter> enumerate{udev_enumerate_new(udev)};
udev *udev) {
std::unique_ptr<udev_enumerate, UdevEnumerateDeleter> enumerate{
udev_enumerate_new(udev)};
udev_enumerate_add_match_subsystem(enumerate.get(), "backlight"); udev_enumerate_add_match_subsystem(enumerate.get(), "backlight");
udev_enumerate_scan_devices(enumerate.get()); udev_enumerate_scan_devices(enumerate.get());
udev_list_entry *enum_devices = udev_list_entry *enum_devices = udev_enumerate_get_list_entry(enumerate.get());
udev_enumerate_get_list_entry(enumerate.get());
udev_list_entry *dev_list_entry; udev_list_entry *dev_list_entry;
udev_list_entry_foreach(dev_list_entry, enum_devices) { udev_list_entry_foreach(dev_list_entry, enum_devices) {
const char *path = udev_list_entry_get_name(dev_list_entry); const char * path = udev_list_entry_get_name(dev_list_entry);
std::unique_ptr<udev_device, UdevDeviceDeleter> dev{ std::unique_ptr<udev_device, UdevDeviceDeleter> dev{udev_device_new_from_syspath(udev, path)};
udev_device_new_from_syspath(udev, path)};
check_nn(dev.get(), "dev new failed"); check_nn(dev.get(), "dev new failed");
upsert_device(first, last, inserter, dev.get()); upsert_device(first, last, inserter, dev.get());
} }

View File

@ -1,8 +1,7 @@
#include "modules/battery.hpp" #include "modules/battery.hpp"
waybar::modules::Battery::Battery(const std::string& id, const Json::Value& config) waybar::modules::Battery::Battery(const std::string& id, const Json::Value& config)
: ALabel(config, "{capacity}%", 60) : ALabel(config, "{capacity}%", 60) {
{
label_.set_name("battery"); label_.set_name("battery");
if (!id.empty()) { if (!id.empty()) {
label_.get_style_context()->add_class(id); label_.get_style_context()->add_class(id);
@ -21,23 +20,21 @@ waybar::modules::Battery::Battery(const std::string& id, const Json::Value& conf
worker(); worker();
} }
waybar::modules::Battery::~Battery() waybar::modules::Battery::~Battery() {
{
for (auto wd : wds_) { for (auto wd : wds_) {
inotify_rm_watch(fd_, wd); inotify_rm_watch(fd_, wd);
} }
close(fd_); close(fd_);
} }
void waybar::modules::Battery::worker() void waybar::modules::Battery::worker() {
{
thread_timer_ = [this] { thread_timer_ = [this] {
dp.emit(); dp.emit();
thread_timer_.sleep_for(interval_); thread_timer_.sleep_for(interval_);
}; };
thread_ = [this] { thread_ = [this] {
struct inotify_event event = {0}; struct inotify_event event = {0};
int nbytes = read(fd_, &event, sizeof(event)); int nbytes = read(fd_, &event, sizeof(event));
if (nbytes != sizeof(event) || event.mask & IN_IGNORED) { if (nbytes != sizeof(event) || event.mask & IN_IGNORED) {
thread_.stop(); thread_.stop();
return; return;
@ -48,8 +45,7 @@ void waybar::modules::Battery::worker()
}; };
} }
void waybar::modules::Battery::getBatteries() void waybar::modules::Battery::getBatteries() {
{
try { try {
for (auto const& node : fs::directory_iterator(data_dir_)) { for (auto const& node : fs::directory_iterator(data_dir_)) {
if (!fs::is_directory(node)) { if (!fs::is_directory(node)) {
@ -57,18 +53,18 @@ void waybar::modules::Battery::getBatteries()
} }
auto dir_name = node.path().filename(); auto dir_name = node.path().filename();
auto bat_defined = config_["bat"].isString(); auto bat_defined = config_["bat"].isString();
if (((bat_defined && dir_name == config_["bat"].asString()) if (((bat_defined && dir_name == config_["bat"].asString()) || !bat_defined) &&
|| !bat_defined) && fs::exists(node / "capacity") fs::exists(node / "capacity") && fs::exists(node / "uevent") &&
&& fs::exists(node / "uevent") && fs::exists(node / "status")) { fs::exists(node / "status")) {
batteries_.push_back(node); batteries_.push_back(node);
} }
auto adap_defined = config_["adapter"].isString(); auto adap_defined = config_["adapter"].isString();
if (((adap_defined && dir_name == config_["adapter"].asString()) if (((adap_defined && dir_name == config_["adapter"].asString()) || !adap_defined) &&
|| !adap_defined) && fs::exists(node / "online")) { fs::exists(node / "online")) {
adapter_ = node; adapter_ = node;
} }
} }
} catch (fs::filesystem_error &e) { } catch (fs::filesystem_error& e) {
throw std::runtime_error(e.what()); throw std::runtime_error(e.what());
} }
if (batteries_.empty()) { if (batteries_.empty()) {
@ -79,13 +75,12 @@ void waybar::modules::Battery::getBatteries()
} }
} }
const std::tuple<uint8_t, std::string> waybar::modules::Battery::getInfos() const const std::tuple<uint8_t, std::string> waybar::modules::Battery::getInfos() const {
{
try { try {
uint16_t total = 0; uint16_t total = 0;
std::string status = "Unknown"; std::string status = "Unknown";
for (auto const& bat : batteries_) { for (auto const& bat : batteries_) {
uint16_t capacity; uint16_t capacity;
std::string _status; std::string _status;
std::ifstream(bat / "capacity") >> capacity; std::ifstream(bat / "capacity") >> capacity;
std::ifstream(bat / "status") >> _status; std::ifstream(bat / "status") >> _status;
@ -102,8 +97,7 @@ const std::tuple<uint8_t, std::string> waybar::modules::Battery::getInfos() cons
} }
} }
const std::string waybar::modules::Battery::getAdapterStatus(uint8_t capacity) const const std::string waybar::modules::Battery::getAdapterStatus(uint8_t capacity) const {
{
if (!adapter_.empty()) { if (!adapter_.empty()) {
bool online; bool online;
std::ifstream(adapter_ / "online") >> online; std::ifstream(adapter_ / "online") >> online;
@ -115,21 +109,18 @@ const std::string waybar::modules::Battery::getAdapterStatus(uint8_t capacity) c
return "Unknown"; return "Unknown";
} }
const std::string waybar::modules::Battery::getState(uint8_t capacity) const const std::string waybar::modules::Battery::getState(uint8_t capacity) const {
{
// Get current state // Get current state
std::vector<std::pair<std::string, uint8_t>> states; std::vector<std::pair<std::string, uint8_t>> states;
if (config_["states"].isObject()) { if (config_["states"].isObject()) {
for (auto it = config_["states"].begin(); it != config_["states"].end(); ++it) { for (auto it = config_["states"].begin(); it != config_["states"].end(); ++it) {
if (it->isUInt() && it.key().isString()) { if (it->isUInt() && it.key().isString()) {
states.push_back({it.key().asString(), it->asUInt()}); states.emplace_back(it.key().asString(), it->asUInt());
} }
} }
} }
// Sort states // Sort states
std::sort(states.begin(), states.end(), [](auto &a, auto &b) { std::sort(states.begin(), states.end(), [](auto& a, auto& b) { return a.second < b.second; });
return a.second < b.second;
});
std::string valid_state; std::string valid_state;
for (auto const& state : states) { for (auto const& state : states) {
if (capacity <= state.second && valid_state.empty()) { if (capacity <= state.second && valid_state.empty()) {
@ -142,8 +133,7 @@ const std::string waybar::modules::Battery::getState(uint8_t capacity) const
return valid_state; return valid_state;
} }
auto waybar::modules::Battery::update() -> void auto waybar::modules::Battery::update() -> void {
{
auto [capacity, status] = getInfos(); auto [capacity, status] = getInfos();
if (status == "Unknown") { if (status == "Unknown") {
status = getAdapterStatus(capacity); status = getAdapterStatus(capacity);
@ -168,7 +158,7 @@ auto waybar::modules::Battery::update() -> void
event_box_.hide(); event_box_.hide();
} else { } else {
event_box_.show(); event_box_.show();
label_.set_markup(fmt::format(format, fmt::arg("capacity", capacity), label_.set_markup(
fmt::arg("icon", getIcon(capacity)))); fmt::format(format, fmt::arg("capacity", capacity), fmt::arg("icon", getIcon(capacity))));
} }
} }

View File

@ -1,8 +1,7 @@
#include "modules/clock.hpp" #include "modules/clock.hpp"
waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
: ALabel(config, "{:%H:%M}", 60) : ALabel(config, "{:%H:%M}", 60) {
{
label_.set_name("clock"); label_.set_name("clock");
if (!id.empty()) { if (!id.empty()) {
label_.get_style_context()->add_class(id); label_.get_style_context()->add_class(id);
@ -13,14 +12,17 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
auto timeout = std::chrono::floor<std::chrono::seconds>(now + interval_); auto timeout = std::chrono::floor<std::chrono::seconds>(now + interval_);
auto time_s = std::chrono::time_point_cast<std::chrono::seconds>(timeout); auto time_s = std::chrono::time_point_cast<std::chrono::seconds>(timeout);
auto sub_m = auto sub_m =
std::chrono::duration_cast<std::chrono::seconds>(time_s.time_since_epoch()).count() % interval_.count(); std::chrono::duration_cast<std::chrono::seconds>(time_s.time_since_epoch()).count() %
if (sub_m > 0) thread_.sleep_until(timeout - std::chrono::seconds(sub_m - 1)); interval_.count();
else thread_.sleep_until(timeout - std::chrono::seconds(sub_m)); if (sub_m > 0) {
thread_.sleep_until(timeout - std::chrono::seconds(sub_m - 1));
} else {
thread_.sleep_until(timeout - std::chrono::seconds(sub_m));
}
}; };
} }
auto waybar::modules::Clock::update() -> void auto waybar::modules::Clock::update() -> void {
{
auto localtime = fmt::localtime(std::time(nullptr)); auto localtime = fmt::localtime(std::time(nullptr));
auto text = fmt::format(format_, localtime); auto text = fmt::format(format_, localtime);
label_.set_markup(text); label_.set_markup(text);

View File

@ -1,8 +1,7 @@
#include "modules/cpu.hpp" #include "modules/cpu.hpp"
waybar::modules::Cpu::Cpu(const std::string& id, const Json::Value& config) waybar::modules::Cpu::Cpu(const std::string& id, const Json::Value& config)
: ALabel(config, "{usage}%", 10) : ALabel(config, "{usage}%", 10) {
{
label_.set_name("cpu"); label_.set_name("cpu");
if (!id.empty()) { if (!id.empty()) {
label_.get_style_context()->add_class(id); label_.get_style_context()->add_class(id);
@ -13,44 +12,40 @@ waybar::modules::Cpu::Cpu(const std::string& id, const Json::Value& config)
}; };
} }
auto waybar::modules::Cpu::update() -> void auto waybar::modules::Cpu::update() -> void {
{
// TODO: as creating dynamic fmt::arg arrays is buggy we have to calc both // TODO: as creating dynamic fmt::arg arrays is buggy we have to calc both
auto cpu_load = getCpuLoad(); auto cpu_load = getCpuLoad();
auto [cpu_usage, tooltip] = getCpuUsage(); auto [cpu_usage, tooltip] = getCpuUsage();
if (tooltipEnabled()) { if (tooltipEnabled()) {
label_.set_tooltip_text(tooltip); label_.set_tooltip_text(tooltip);
} }
label_.set_markup(fmt::format(format_, label_.set_markup(fmt::format(format_, fmt::arg("load", cpu_load), fmt::arg("usage", cpu_usage)));
fmt::arg("load", cpu_load), fmt::arg("usage", cpu_usage)));
} }
uint16_t waybar::modules::Cpu::getCpuLoad() uint16_t waybar::modules::Cpu::getCpuLoad() {
{
struct sysinfo info = {0}; struct sysinfo info = {0};
if (sysinfo(&info) == 0) { if (sysinfo(&info) == 0) {
float f_load = 1.f / (1u << SI_LOAD_SHIFT); float f_load = 1.F / (1U << SI_LOAD_SHIFT);
uint16_t load = info.loads[0] * f_load * 100 / get_nprocs(); uint16_t load = info.loads[0] * f_load * 100 / get_nprocs();
return load; return load;
} }
throw std::runtime_error("Can't get Cpu load"); throw std::runtime_error("Can't get Cpu load");
} }
std::tuple<uint16_t, std::string> waybar::modules::Cpu::getCpuUsage() std::tuple<uint16_t, std::string> waybar::modules::Cpu::getCpuUsage() {
{
if (prev_times_.empty()) { if (prev_times_.empty()) {
prev_times_ = parseCpuinfo(); prev_times_ = parseCpuinfo();
std::this_thread::sleep_for(std::chrono::milliseconds(100)); std::this_thread::sleep_for(std::chrono::milliseconds(100));
} }
std::vector<std::tuple<size_t, size_t>> curr_times = parseCpuinfo(); std::vector<std::tuple<size_t, size_t>> curr_times = parseCpuinfo();
std::string tooltip; std::string tooltip;
uint16_t usage = 0; uint16_t usage = 0;
for (size_t i = 0; i < curr_times.size(); ++i) { for (size_t i = 0; i < curr_times.size(); ++i) {
auto [curr_idle, curr_total] = curr_times[i]; auto [curr_idle, curr_total] = curr_times[i];
auto [prev_idle, prev_total] = prev_times_[i]; auto [prev_idle, prev_total] = prev_times_[i];
const float delta_idle = curr_idle - prev_idle; const float delta_idle = curr_idle - prev_idle;
const float delta_total = curr_total - prev_total; const float delta_total = curr_total - prev_total;
uint16_t tmp = 100 * (1 - delta_idle / delta_total); uint16_t tmp = 100 * (1 - delta_idle / delta_total);
if (i == 0) { if (i == 0) {
usage = tmp; usage = tmp;
tooltip = fmt::format("Total: {}%", tmp); tooltip = fmt::format("Total: {}%", tmp);
@ -62,21 +57,21 @@ std::tuple<uint16_t, std::string> waybar::modules::Cpu::getCpuUsage()
return {usage, tooltip}; return {usage, tooltip};
} }
std::vector<std::tuple<size_t, size_t>> waybar::modules::Cpu::parseCpuinfo() std::vector<std::tuple<size_t, size_t>> waybar::modules::Cpu::parseCpuinfo() {
{
std::ifstream info(data_dir_); std::ifstream info(data_dir_);
if (!info.is_open()) { if (!info.is_open()) {
throw std::runtime_error("Can't open " + data_dir_); throw std::runtime_error("Can't open " + data_dir_);
} }
std::vector< std::tuple<size_t, size_t> > cpuinfo; std::vector<std::tuple<size_t, size_t>> cpuinfo;
std::string line; std::string line;
while (getline(info, line)) { while (getline(info, line)) {
if (line.substr(0,3).compare("cpu") != 0) { if (line.substr(0, 3).compare("cpu") != 0) {
break; break;
} }
std::stringstream sline(line.substr(5)); std::stringstream sline(line.substr(5));
std::vector<size_t> times; std::vector<size_t> times;
for (size_t time; sline >> time; times.push_back(time)); for (size_t time = 0; sline >> time; times.push_back(time))
;
size_t idle_time = 0; size_t idle_time = 0;
size_t total_time = 0; size_t total_time = 0;
@ -84,7 +79,7 @@ std::vector<std::tuple<size_t, size_t>> waybar::modules::Cpu::parseCpuinfo()
idle_time = times[3]; idle_time = times[3];
total_time = std::accumulate(times.begin(), times.end(), 0); total_time = std::accumulate(times.begin(), times.end(), 0);
} }
cpuinfo.push_back({idle_time, total_time}); cpuinfo.emplace_back(idle_time, total_time);
} }
return cpuinfo; return cpuinfo;
} }

View File

@ -1,9 +1,7 @@
#include "modules/custom.hpp" #include "modules/custom.hpp"
waybar::modules::Custom::Custom(const std::string& name, waybar::modules::Custom::Custom(const std::string& name, const Json::Value& config)
const Json::Value& config) : ALabel(config, "{}"), name_(name), fp_(nullptr), pid_(-1) {
: ALabel(config, "{}"), name_(name), fp_(nullptr)
{
label_.set_name("custom-" + name_); label_.set_name("custom-" + name_);
if (config_["exec"].isString()) { if (config_["exec"].isString()) {
if (interval_.count() > 0) { if (interval_.count() > 0) {
@ -15,16 +13,14 @@ waybar::modules::Custom::Custom(const std::string& name,
dp.emit(); dp.emit();
} }
waybar::modules::Custom::~Custom() waybar::modules::Custom::~Custom() {
{ if (pid_ != -1) {
if (fp_) { kill(-pid_, 9);
pclose(fp_); pid_ = -1;
fp_ = nullptr;
} }
} }
void waybar::modules::Custom::delayWorker() void waybar::modules::Custom::delayWorker() {
{
thread_ = [this] { thread_ = [this] {
bool can_update = true; bool can_update = true;
if (config_["exec-if"].isString()) { if (config_["exec-if"].isString()) {
@ -42,25 +38,25 @@ void waybar::modules::Custom::delayWorker()
}; };
} }
void waybar::modules::Custom::continuousWorker() void waybar::modules::Custom::continuousWorker() {
{
auto cmd = config_["exec"].asString(); auto cmd = config_["exec"].asString();
fp_ = popen(cmd.c_str(), "r"); pid_ = -1;
fp_ = util::command::open(cmd, pid_);
if (!fp_) { if (!fp_) {
throw std::runtime_error("Unable to open " + cmd); throw std::runtime_error("Unable to open " + cmd);
} }
thread_ = [this] { thread_ = [&] {
char* buff = nullptr; char* buff = nullptr;
size_t len = 0; size_t len = 0;
if (getline(&buff, &len, fp_) == -1) { if (getline(&buff, &len, fp_) == -1) {
int exit_code = 1; int exit_code = 1;
if (fp_) { if (fp_) {
exit_code = WEXITSTATUS(pclose(fp_)); exit_code = WEXITSTATUS(util::command::close(fp_, pid_));
fp_ = nullptr; fp_ = nullptr;
} }
thread_.stop(); thread_.stop();
if (exit_code != 0) { if (exit_code != 0) {
output_ = { exit_code, "" }; output_ = {exit_code, ""};
dp.emit(); dp.emit();
std::cerr << name_ + " just stopped unexpectedly, is it endless?" << std::endl; std::cerr << name_ + " just stopped unexpectedly, is it endless?" << std::endl;
} }
@ -69,23 +65,21 @@ void waybar::modules::Custom::continuousWorker()
std::string output = buff; std::string output = buff;
// Remove last newline // Remove last newline
if (!output.empty() && output[output.length()-1] == '\n') { if (!output.empty() && output[output.length() - 1] == '\n') {
output.erase(output.length()-1); output.erase(output.length() - 1);
} }
output_ = { 0, output }; output_ = {0, output};
dp.emit(); dp.emit();
}; };
} }
void waybar::modules::Custom::refresh(int sig /*signal*/) void waybar::modules::Custom::refresh(int sig /*signal*/) {
{ if (sig == SIGRTMIN + config_["signal"].asInt()) {
if(sig == SIGRTMIN + config_["signal"].asInt()) {
thread_.wake_up(); thread_.wake_up();
} }
} }
auto waybar::modules::Custom::update() -> void auto waybar::modules::Custom::update() -> void {
{
// Hide label if output is empty // Hide label if output is empty
if (config_["exec"].isString() && (output_.out.empty() || output_.exit_code != 0)) { if (config_["exec"].isString() && (output_.out.empty() || output_.exit_code != 0)) {
event_box_.hide(); event_box_.hide();
@ -96,10 +90,11 @@ auto waybar::modules::Custom::update() -> void
parseOutputRaw(); parseOutputRaw();
} }
auto str = fmt::format(format_, text_, auto str = fmt::format(format_,
fmt::arg("alt", alt_), text_,
fmt::arg("icon", getIcon(percentage_, alt_)), fmt::arg("alt", alt_),
fmt::arg("percentage", percentage_)); fmt::arg("icon", getIcon(percentage_, alt_)),
fmt::arg("percentage", percentage_));
label_.set_markup(str); label_.set_markup(str);
if (tooltipEnabled()) { if (tooltipEnabled()) {
if (text_ == tooltip_) { if (text_ == tooltip_) {
@ -108,26 +103,22 @@ auto waybar::modules::Custom::update() -> void
label_.set_tooltip_text(tooltip_); label_.set_tooltip_text(tooltip_);
} }
} }
if (class_ != "") { auto classes = label_.get_style_context()->list_classes();
if (prevclass_ != "") { for (auto const& c : classes) {
label_.get_style_context()->remove_class(prevclass_); label_.get_style_context()->remove_class(c);
} }
label_.get_style_context()->add_class(class_); for (auto const& c : class_) {
prevclass_ = class_; label_.get_style_context()->add_class(c);
} else {
label_.get_style_context()->remove_class(prevclass_);
prevclass_ = "";
} }
event_box_.show(); event_box_.show();
} }
} }
void waybar::modules::Custom::parseOutputRaw() void waybar::modules::Custom::parseOutputRaw() {
{
std::istringstream output(output_.out); std::istringstream output(output_.out);
std::string line; std::string line;
int i = 0; int i = 0;
while (getline(output, line)) { while (getline(output, line)) {
if (i == 0) { if (i == 0) {
if (config_["escape"].isBool() && config_["escape"].asBool()) { if (config_["escape"].isBool() && config_["escape"].asBool()) {
@ -136,11 +127,11 @@ void waybar::modules::Custom::parseOutputRaw()
text_ = line; text_ = line;
} }
tooltip_ = line; tooltip_ = line;
class_ = ""; class_.clear();
} else if (i == 1) { } else if (i == 1) {
tooltip_ = line; tooltip_ = line;
} else if (i == 2) { } else if (i == 2) {
class_ = line; class_.push_back(line);
} else { } else {
break; break;
} }
@ -148,10 +139,10 @@ void waybar::modules::Custom::parseOutputRaw()
} }
} }
void waybar::modules::Custom::parseOutputJson() void waybar::modules::Custom::parseOutputJson() {
{
std::istringstream output(output_.out); std::istringstream output(output_.out);
std::string line; std::string line;
class_.clear();
while (getline(output, line)) { while (getline(output, line)) {
auto parsed = parser_.parse(line); auto parsed = parser_.parse(line);
if (config_["escape"].isBool() && config_["escape"].asBool()) { if (config_["escape"].isBool() && config_["escape"].asBool()) {
@ -165,7 +156,13 @@ void waybar::modules::Custom::parseOutputJson()
alt_ = parsed["alt"].asString(); alt_ = parsed["alt"].asString();
} }
tooltip_ = parsed["tooltip"].asString(); tooltip_ = parsed["tooltip"].asString();
class_ = parsed["class"].asString(); if (parsed["class"].isString()) {
class_.push_back(parsed["class"].asString());
} else if (parsed["class"].isArray()) {
for (auto const& c : parsed["class"]) {
class_.push_back(c.asString());
}
}
if (!parsed["percentage"].asString().empty() && parsed["percentage"].isUInt()) { if (!parsed["percentage"].asString().empty() && parsed["percentage"].isUInt()) {
percentage_ = parsed["percentage"].asUInt(); percentage_ = parsed["percentage"].asUInt();
} else { } else {

View File

@ -1,9 +1,13 @@
#include "modules/idle_inhibitor.hpp" #include "modules/idle_inhibitor.hpp"
#include "util/command.hpp" #include "util/command.hpp"
waybar::modules::IdleInhibitor::IdleInhibitor(const std::string& id, const Bar& bar, const Json::Value& config) waybar::modules::IdleInhibitor::IdleInhibitor(const std::string& id, const Bar& bar,
: ALabel(config, "{status}"), bar_(bar), status_("deactivated"), idle_inhibitor_(nullptr) const Json::Value& config)
{ : ALabel(config, "{status}"),
bar_(bar),
status_("deactivated"),
idle_inhibitor_(nullptr),
pid_(-1) {
label_.set_name("idle_inhibitor"); label_.set_name("idle_inhibitor");
if (!id.empty()) { if (!id.empty()) {
label_.get_style_context()->add_class(id); label_.get_style_context()->add_class(id);
@ -14,21 +18,22 @@ waybar::modules::IdleInhibitor::IdleInhibitor(const std::string& id, const Bar&
dp.emit(); dp.emit();
} }
waybar::modules::IdleInhibitor::~IdleInhibitor() waybar::modules::IdleInhibitor::~IdleInhibitor() {
{ if (idle_inhibitor_ != nullptr) {
if(idle_inhibitor_) {
zwp_idle_inhibitor_v1_destroy(idle_inhibitor_); zwp_idle_inhibitor_v1_destroy(idle_inhibitor_);
idle_inhibitor_ = nullptr; idle_inhibitor_ = nullptr;
} }
if (pid_ != -1) {
kill(-pid_, 9);
pid_ = -1;
}
} }
auto waybar::modules::IdleInhibitor::update() -> void auto waybar::modules::IdleInhibitor::update() -> void {
{
label_.set_markup( label_.set_markup(
fmt::format(format_, fmt::arg("status", status_), fmt::format(format_, fmt::arg("status", status_), fmt::arg("icon", getIcon(0, status_))));
fmt::arg("icon", getIcon(0, status_))));
label_.get_style_context()->add_class(status_); label_.get_style_context()->add_class(status_);
if(tooltipEnabled()) { if (tooltipEnabled()) {
label_.set_tooltip_text(status_); label_.set_tooltip_text(status_);
} }
} }
@ -36,17 +41,17 @@ auto waybar::modules::IdleInhibitor::update() -> void
bool waybar::modules::IdleInhibitor::handleToggle(GdkEventButton* const& e) { bool waybar::modules::IdleInhibitor::handleToggle(GdkEventButton* const& e) {
if (e->button == 1) { if (e->button == 1) {
label_.get_style_context()->remove_class(status_); label_.get_style_context()->remove_class(status_);
if (idle_inhibitor_) { if (idle_inhibitor_ != nullptr) {
zwp_idle_inhibitor_v1_destroy(idle_inhibitor_); zwp_idle_inhibitor_v1_destroy(idle_inhibitor_);
idle_inhibitor_ = nullptr; idle_inhibitor_ = nullptr;
status_ = "deactivated"; status_ = "deactivated";
} else { } else {
idle_inhibitor_ = zwp_idle_inhibit_manager_v1_create_inhibitor( idle_inhibitor_ = zwp_idle_inhibit_manager_v1_create_inhibitor(
bar_.client.idle_inhibit_manager, bar_.surface); waybar::Client::inst()->idle_inhibit_manager, bar_.surface);
status_ = "activated"; status_ = "activated";
} }
if (config_["on-click"].isString() && e->button == 1) { if (config_["on-click"].isString() && e->button == 1) {
waybar::util::command::forkExec(config_["on-click"].asString()); pid_ = waybar::util::command::forkExec(config_["on-click"].asString());
} }
} else { } else {
ALabel::handleToggle(e); ALabel::handleToggle(e);

View File

@ -1,8 +1,7 @@
#include "modules/memory.hpp" #include "modules/memory.hpp"
waybar::modules::Memory::Memory(const std::string& id, const Json::Value& config) waybar::modules::Memory::Memory(const std::string& id, const Json::Value& config)
: ALabel(config, "{}%", 30) : ALabel(config, "{}%", 30) {
{
label_.set_name("memory"); label_.set_name("memory");
if (!id.empty()) { if (!id.empty()) {
label_.get_style_context()->add_class(id); label_.get_style_context()->add_class(id);
@ -13,8 +12,7 @@ waybar::modules::Memory::Memory(const std::string& id, const Json::Value& config
}; };
} }
auto waybar::modules::Memory::update() -> void auto waybar::modules::Memory::update() -> void {
{
parseMeminfo(); parseMeminfo();
if (memtotal_ > 0 && memfree_ >= 0) { if (memtotal_ > 0 && memfree_ >= 0) {
int used_ram_percentage = 100 * (memtotal_ - memfree_) / memtotal_; int used_ram_percentage = 100 * (memtotal_ - memfree_) / memtotal_;
@ -29,21 +27,20 @@ auto waybar::modules::Memory::update() -> void
} }
} }
void waybar::modules::Memory::parseMeminfo() void waybar::modules::Memory::parseMeminfo() {
{ int64_t memfree = -1, membuffer = -1, memcache = -1, memavail = -1;
long memfree = -1, membuffer = -1, memcache = -1, memavail = -1;
std::ifstream info(data_dir_); std::ifstream info(data_dir_);
if (!info.is_open()) { if (!info.is_open()) {
throw std::runtime_error("Can't open " + data_dir_); throw std::runtime_error("Can't open " + data_dir_);
} }
std::string line; std::string line;
while (getline(info, line)) { while (getline(info, line)) {
auto posDelim = line.find(":"); auto posDelim = line.find(':');
if (posDelim == std::string::npos) { if (posDelim == std::string::npos) {
continue; continue;
} }
std::string name = line.substr(0, posDelim); std::string name = line.substr(0, posDelim);
long value = std::stol(line.substr(posDelim + 1)); int64_t value = std::stol(line.substr(posDelim + 1));
if (name.compare("MemTotal") == 0) { if (name.compare("MemTotal") == 0) {
memtotal_ = value; memtotal_ = value;
@ -56,8 +53,7 @@ void waybar::modules::Memory::parseMeminfo()
} else if (name.compare("Cached") == 0) { } else if (name.compare("Cached") == 0) {
memcache = value; memcache = value;
} }
if (memtotal_ > 0 && if (memtotal_ > 0 && (memavail >= 0 || (memfree > -1 && membuffer > -1 && memcache > -1))) {
(memavail >= 0 || (memfree > -1 && membuffer > -1 && memcache > -1))) {
break; break;
} }
} }

344
src/modules/mpd.cpp Normal file
View File

@ -0,0 +1,344 @@
#include "modules/mpd.hpp"
#include <fmt/chrono.h>
#include <iostream>
waybar::modules::MPD::MPD(const std::string& id, const Json::Value& config)
: ALabel(config, "{album} - {artist} - {title}", 5),
module_name_(id.empty() ? "mpd" : "mpd#" + id),
server_(nullptr),
port_(config_["port"].isUInt() ? config["port"].asUInt() : 0),
timeout_(config_["timeout"].isUInt() ? config_["timeout"].asUInt() * 1'000 : 30'000),
connection_(nullptr, &mpd_connection_free),
alternate_connection_(nullptr, &mpd_connection_free),
status_(nullptr, &mpd_status_free),
song_(nullptr, &mpd_song_free) {
if (!config_["port"].isNull() && !config_["port"].isUInt()) {
std::cerr << module_name_ << ": `port` configuration should be an unsigned int" << std::endl;
}
if (!config_["timeout"].isNull() && !config_["timeout"].isUInt()) {
std::cerr << module_name_ << ": `timeout` configuration should be an unsigned int" << std::endl;
}
label_.set_name("mpd");
if (!id.empty()) {
label_.get_style_context()->add_class(id);
}
if (!config["server"].isNull()) {
if (!config_["server"].isString()) {
std::cerr << module_name_ << "`server` configuration should be a string" << std::endl;
}
server_ = config["server"].asCString();
}
event_listener().detach();
event_box_.add_events(Gdk::BUTTON_PRESS_MASK);
event_box_.signal_button_press_event().connect(sigc::mem_fun(*this, &MPD::handlePlayPause));
}
auto waybar::modules::MPD::update() -> void {
std::lock_guard guard(connection_lock_);
tryConnect();
if (connection_ != nullptr) {
try {
bool wasPlaying = playing();
fetchState();
if (!wasPlaying && playing()) {
periodic_updater().detach();
}
} catch (const std::exception& e) {
std::cerr << module_name_ + ": " + e.what() << std::endl;
state_ = MPD_STATE_UNKNOWN;
}
}
setLabel();
}
std::thread waybar::modules::MPD::event_listener() {
return std::thread([this] {
while (true) {
try {
if (connection_ == nullptr) {
// Retry periodically if no connection
update();
std::this_thread::sleep_for(interval_);
} else {
waitForEvent();
dp.emit();
}
} catch (const std::exception& e) {
std::cerr << module_name_ + ": " + e.what() << std::endl;
}
}
});
}
std::thread waybar::modules::MPD::periodic_updater() {
return std::thread([this] {
while (connection_ != nullptr && playing()) {
dp.emit();
std::this_thread::sleep_for(std::chrono::seconds(1));
}
});
}
std::string waybar::modules::MPD::getTag(mpd_tag_type type, unsigned idx) {
std::string result =
config_["unknown-tag"].isString() ? config_["unknown-tag"].asString() : "N/A";
const char* tag = mpd_song_get_tag(song_.get(), type, idx);
// mpd_song_get_tag can return NULL, so make sure it's valid before setting
if (tag) result = tag;
return result;
}
void waybar::modules::MPD::setLabel() {
if (connection_ == nullptr) {
label_.get_style_context()->add_class("disconnected");
label_.get_style_context()->remove_class("stopped");
label_.get_style_context()->remove_class("playing");
label_.get_style_context()->remove_class("paused");
auto format = config_["format-disconnected"].isString()
? config_["format-disconnected"].asString()
: "disconnected";
label_.set_markup(format);
if (tooltipEnabled()) {
std::string tooltip_format;
tooltip_format = config_["tooltip-format-disconnected"].isString()
? config_["tooltip-format-disconnected"].asString()
: "MPD (disconnected)";
// Nothing to format
label_.set_tooltip_text(tooltip_format);
}
return;
} else {
label_.get_style_context()->remove_class("disconnected");
}
auto format = format_;
std::string artist, album_artist, album, title, date;
std::chrono::seconds elapsedTime, totalTime;
std::string stateIcon = "";
if (stopped()) {
format =
config_["format-stopped"].isString() ? config_["format-stopped"].asString() : "stopped";
label_.get_style_context()->add_class("stopped");
label_.get_style_context()->remove_class("playing");
label_.get_style_context()->remove_class("paused");
} else {
label_.get_style_context()->remove_class("stopped");
if (playing()) {
label_.get_style_context()->add_class("playing");
label_.get_style_context()->remove_class("paused");
} else {
label_.get_style_context()->add_class("paused");
label_.get_style_context()->remove_class("playing");
}
stateIcon = getStateIcon();
artist = getTag(MPD_TAG_ARTIST);
album_artist = getTag(MPD_TAG_ALBUM_ARTIST);
album = getTag(MPD_TAG_ALBUM);
title = getTag(MPD_TAG_TITLE);
date = getTag(MPD_TAG_DATE);
elapsedTime = std::chrono::seconds(mpd_status_get_elapsed_time(status_.get()));
totalTime = std::chrono::seconds(mpd_status_get_total_time(status_.get()));
}
bool consumeActivated = mpd_status_get_consume(status_.get());
std::string consumeIcon = getOptionIcon("consume", consumeActivated);
bool randomActivated = mpd_status_get_random(status_.get());
std::string randomIcon = getOptionIcon("random", randomActivated);
bool repeatActivated = mpd_status_get_repeat(status_.get());
std::string repeatIcon = getOptionIcon("repeat", repeatActivated);
bool singleActivated = mpd_status_get_single(status_.get());
std::string singleIcon = getOptionIcon("single", singleActivated);
// TODO: format can fail
label_.set_markup(
fmt::format(format,
fmt::arg("artist", Glib::Markup::escape_text(artist).raw()),
fmt::arg("albumArtist", Glib::Markup::escape_text(album_artist).raw()),
fmt::arg("album", Glib::Markup::escape_text(album).raw()),
fmt::arg("title", Glib::Markup::escape_text(title).raw()),
fmt::arg("date", Glib::Markup::escape_text(date).raw()),
fmt::arg("elapsedTime", elapsedTime),
fmt::arg("totalTime", totalTime),
fmt::arg("stateIcon", stateIcon),
fmt::arg("consumeIcon", consumeIcon),
fmt::arg("randomIcon", randomIcon),
fmt::arg("repeatIcon", repeatIcon),
fmt::arg("singleIcon", singleIcon)));
if (tooltipEnabled()) {
std::string tooltip_format;
tooltip_format = config_["tooltip-format"].isString() ? config_["tooltip-format"].asString()
: "MPD (connected)";
auto tooltip_text = fmt::format(tooltip_format,
fmt::arg("artist", artist),
fmt::arg("albumArtist", album_artist),
fmt::arg("album", album),
fmt::arg("title", title),
fmt::arg("date", date),
fmt::arg("stateIcon", stateIcon),
fmt::arg("consumeIcon", consumeIcon),
fmt::arg("randomIcon", randomIcon),
fmt::arg("repeatIcon", repeatIcon),
fmt::arg("singleIcon", singleIcon));
label_.set_tooltip_text(tooltip_text);
}
}
std::string waybar::modules::MPD::getStateIcon() {
if (!config_["state-icons"].isObject()) {
return "";
}
if (connection_ == nullptr) {
std::cerr << module_name_ << ": Trying to fetch state icon while disconnected" << std::endl;
return "";
}
if (stopped()) {
std::cerr << module_name_ << ": Trying to fetch state icon while stopped" << std::endl;
return "";
}
if (playing()) {
return config_["state-icons"]["playing"].asString();
} else {
return config_["state-icons"]["paused"].asString();
}
}
std::string waybar::modules::MPD::getOptionIcon(std::string optionName, bool activated) {
if (!config_[optionName + "-icons"].isObject()) {
return "";
}
if (connection_ == nullptr) {
std::cerr << module_name_ << ": Trying to fetch option icon while disconnected" << std::endl;
return "";
}
if (activated) {
return config_[optionName + "-icons"]["on"].asString();
} else {
return config_[optionName + "-icons"]["off"].asString();
}
}
void waybar::modules::MPD::tryConnect() {
if (connection_ != nullptr) {
return;
}
connection_ =
unique_connection(mpd_connection_new(server_, port_, timeout_), &mpd_connection_free);
alternate_connection_ =
unique_connection(mpd_connection_new(server_, port_, timeout_), &mpd_connection_free);
if (connection_ == nullptr || alternate_connection_ == nullptr) {
std::cerr << module_name_ << ": Failed to connect to MPD" << std::endl;
connection_.reset();
alternate_connection_.reset();
return;
}
try {
checkErrors(connection_.get());
std::cerr << module_name_ << ": Connected to MPD" << std::endl;
} catch (std::runtime_error& e) {
std::cerr << module_name_ << ": Failed to connect to MPD: " << e.what() << std::endl;
connection_.reset();
alternate_connection_.reset();
}
}
void waybar::modules::MPD::checkErrors(mpd_connection* conn) {
switch (mpd_connection_get_error(conn)) {
case MPD_ERROR_SUCCESS:
mpd_connection_clear_error(conn);
return;
case MPD_ERROR_TIMEOUT:
case MPD_ERROR_CLOSED:
mpd_connection_clear_error(conn);
connection_.reset();
alternate_connection_.reset();
state_ = MPD_STATE_UNKNOWN;
throw std::runtime_error("Connection to MPD closed");
default:
auto error_message = mpd_connection_get_error_message(conn);
mpd_connection_clear_error(conn);
throw std::runtime_error(std::string(error_message));
}
}
void waybar::modules::MPD::fetchState() {
auto conn = connection_.get();
status_ = unique_status(mpd_run_status(conn), &mpd_status_free);
checkErrors(conn);
state_ = mpd_status_get_state(status_.get());
checkErrors(conn);
song_ = unique_song(mpd_run_current_song(conn), &mpd_song_free);
checkErrors(conn);
}
void waybar::modules::MPD::waitForEvent() {
auto conn = alternate_connection_.get();
// Wait for a player (play/pause), option (random, shuffle, etc.), or playlist
// change
if (!mpd_send_idle_mask(
conn, static_cast<mpd_idle>(MPD_IDLE_PLAYER | MPD_IDLE_OPTIONS | MPD_IDLE_PLAYLIST))) {
checkErrors(conn);
return;
}
// alternate_idle_ = true;
// See issue #277:
// https://github.com/Alexays/Waybar/issues/277
mpd_recv_idle(conn, /* disable_timeout = */ false);
checkErrors(conn);
mpd_response_finish(conn);
checkErrors(conn);
}
bool waybar::modules::MPD::handlePlayPause(GdkEventButton* const& e) {
if (e->type == GDK_2BUTTON_PRESS || e->type == GDK_3BUTTON_PRESS || connection_ == nullptr) {
return false;
}
if (e->button == 1) {
std::lock_guard guard(connection_lock_);
if (stopped()) {
mpd_run_play(connection_.get());
} else {
mpd_run_toggle_pause(connection_.get());
}
} else if (e->button == 3) {
std::lock_guard guard(connection_lock_);
mpd_run_stop(connection_.get());
}
return true;
}
bool waybar::modules::MPD::stopped() {
return connection_ == nullptr || state_ == MPD_STATE_UNKNOWN || state_ == MPD_STATE_STOP;
}
bool waybar::modules::MPD::playing() { return connection_ != nullptr && state_ == MPD_STATE_PLAY; }

View File

@ -1,10 +1,14 @@
#include <sys/eventfd.h>
#include "modules/network.hpp" #include "modules/network.hpp"
#include <sys/eventfd.h>
waybar::modules::Network::Network(const std::string& id, const Json::Value& config) waybar::modules::Network::Network(const std::string &id, const Json::Value &config)
: ALabel(config, "{ifname}", 60), family_(AF_INET), efd_(-1), ev_fd_(-1), : ALabel(config, "{ifname}", 60),
cidr_(-1), signal_strength_dbm_(0), signal_strength_(0) family_(AF_INET),
{ efd_(-1),
ev_fd_(-1),
cidr_(-1),
signal_strength_dbm_(0),
signal_strength_(0) {
label_.set_name("network"); label_.set_name("network");
if (!id.empty()) { if (!id.empty()) {
label_.get_style_context()->add_class(id); label_.get_style_context()->add_class(id);
@ -29,8 +33,7 @@ waybar::modules::Network::Network(const std::string& id, const Json::Value& conf
worker(); worker();
} }
waybar::modules::Network::~Network() waybar::modules::Network::~Network() {
{
if (ev_fd_ > -1) { if (ev_fd_ > -1) {
eventfd_write(ev_fd_, 1); eventfd_write(ev_fd_, 1);
std::this_thread::sleep_for(std::chrono::milliseconds(150)); std::this_thread::sleep_for(std::chrono::milliseconds(150));
@ -51,8 +54,7 @@ waybar::modules::Network::~Network()
} }
} }
void waybar::modules::Network::createInfoSocket() void waybar::modules::Network::createInfoSocket() {
{
info_sock_ = nl_socket_alloc(); info_sock_ = nl_socket_alloc();
if (nl_connect(info_sock_, NETLINK_ROUTE) != 0) { if (nl_connect(info_sock_, NETLINK_ROUTE) != 0) {
throw std::runtime_error("Can't connect network socket"); throw std::runtime_error("Can't connect network socket");
@ -66,13 +68,13 @@ void waybar::modules::Network::createInfoSocket()
nl_socket_disable_seq_check(info_sock_); nl_socket_disable_seq_check(info_sock_);
nl_socket_set_nonblocking(info_sock_); nl_socket_set_nonblocking(info_sock_);
nl_socket_modify_cb(info_sock_, NL_CB_VALID, NL_CB_CUSTOM, handleEvents, this); nl_socket_modify_cb(info_sock_, NL_CB_VALID, NL_CB_CUSTOM, handleEvents, this);
efd_ = epoll_create1(0); efd_ = epoll_create1(EPOLL_CLOEXEC);
if (efd_ < 0) { if (efd_ < 0) {
throw std::runtime_error("Can't create epoll"); throw std::runtime_error("Can't create epoll");
} }
{ {
ev_fd_ = eventfd(0, EFD_NONBLOCK); ev_fd_ = eventfd(0, EFD_NONBLOCK);
struct epoll_event event; struct epoll_event event = {0};
event.events = EPOLLIN | EPOLLET; event.events = EPOLLIN | EPOLLET;
event.data.fd = ev_fd_; event.data.fd = ev_fd_;
if (epoll_ctl(efd_, EPOLL_CTL_ADD, ev_fd_, &event) == -1) { if (epoll_ctl(efd_, EPOLL_CTL_ADD, ev_fd_, &event) == -1) {
@ -80,8 +82,8 @@ void waybar::modules::Network::createInfoSocket()
} }
} }
{ {
auto fd = nl_socket_get_fd(info_sock_); auto fd = nl_socket_get_fd(info_sock_);
struct epoll_event event; struct epoll_event event = {0};
event.events = EPOLLIN | EPOLLET | EPOLLRDHUP; event.events = EPOLLIN | EPOLLET | EPOLLRDHUP;
event.data.fd = fd; event.data.fd = fd;
if (epoll_ctl(efd_, EPOLL_CTL_ADD, fd, &event) == -1) { if (epoll_ctl(efd_, EPOLL_CTL_ADD, fd, &event) == -1) {
@ -90,8 +92,7 @@ void waybar::modules::Network::createInfoSocket()
} }
} }
void waybar::modules::Network::createEventSocket() void waybar::modules::Network::createEventSocket() {
{
sk_ = nl_socket_alloc(); sk_ = nl_socket_alloc();
if (genl_connect(sk_) != 0) { if (genl_connect(sk_) != 0) {
throw std::runtime_error("Can't connect to netlink socket"); throw std::runtime_error("Can't connect to netlink socket");
@ -105,8 +106,7 @@ void waybar::modules::Network::createEventSocket()
} }
} }
void waybar::modules::Network::worker() void waybar::modules::Network::worker() {
{
thread_timer_ = [this] { thread_timer_ = [this] {
if (ifid_ > 0) { if (ifid_ > 0) {
getInfo(); getInfo();
@ -114,9 +114,9 @@ void waybar::modules::Network::worker()
} }
thread_timer_.sleep_for(interval_); thread_timer_.sleep_for(interval_);
}; };
struct epoll_event events[EPOLL_MAX]; std::array<struct epoll_event, EPOLL_MAX> events{};
thread_ = [this, &events] { thread_ = [this, &events] {
int ec = epoll_wait(efd_, events, EPOLL_MAX, -1); int ec = epoll_wait(efd_, events.data(), EPOLL_MAX, -1);
if (ec > 0) { if (ec > 0) {
for (auto i = 0; i < ec; i++) { for (auto i = 0; i < ec; i++) {
if (events[i].data.fd == nl_socket_get_fd(info_sock_)) { if (events[i].data.fd == nl_socket_get_fd(info_sock_)) {
@ -132,8 +132,7 @@ void waybar::modules::Network::worker()
}; };
} }
auto waybar::modules::Network::update() -> void auto waybar::modules::Network::update() -> void {
{
std::string connectiontype; std::string connectiontype;
std::string tooltip_format = ""; std::string tooltip_format = "";
if (config_["tooltip-format"].isString()) { if (config_["tooltip-format"].isString()) {
@ -172,28 +171,26 @@ auto waybar::modules::Network::update() -> void
format_ = default_format_; format_ = default_format_;
} }
auto text = fmt::format(format_, auto text = fmt::format(format_,
fmt::arg("essid", essid_), fmt::arg("essid", essid_),
fmt::arg("signaldBm", signal_strength_dbm_), fmt::arg("signaldBm", signal_strength_dbm_),
fmt::arg("signalStrength", signal_strength_), fmt::arg("signalStrength", signal_strength_),
fmt::arg("ifname", ifname_), fmt::arg("ifname", ifname_),
fmt::arg("netmask", netmask_), fmt::arg("netmask", netmask_),
fmt::arg("ipaddr", ipaddr_), fmt::arg("ipaddr", ipaddr_),
fmt::arg("cidr", cidr_), fmt::arg("cidr", cidr_),
fmt::arg("icon", getIcon(signal_strength_, connectiontype)) fmt::arg("icon", getIcon(signal_strength_, connectiontype)));
);
label_.set_markup(text); label_.set_markup(text);
if (tooltipEnabled()) { if (tooltipEnabled()) {
if (!tooltip_format.empty()) { if (!tooltip_format.empty()) {
auto tooltip_text = fmt::format(tooltip_format, auto tooltip_text = fmt::format(tooltip_format,
fmt::arg("essid", essid_), fmt::arg("essid", essid_),
fmt::arg("signaldBm", signal_strength_dbm_), fmt::arg("signaldBm", signal_strength_dbm_),
fmt::arg("signalStrength", signal_strength_), fmt::arg("signalStrength", signal_strength_),
fmt::arg("ifname", ifname_), fmt::arg("ifname", ifname_),
fmt::arg("netmask", netmask_), fmt::arg("netmask", netmask_),
fmt::arg("ipaddr", ipaddr_), fmt::arg("ipaddr", ipaddr_),
fmt::arg("cidr", cidr_), fmt::arg("cidr", cidr_),
fmt::arg("icon", getIcon(signal_strength_, connectiontype)) fmt::arg("icon", getIcon(signal_strength_, connectiontype)));
);
label_.set_tooltip_text(tooltip_text); label_.set_tooltip_text(tooltip_text);
} else { } else {
label_.set_tooltip_text(text); label_.set_tooltip_text(text);
@ -201,8 +198,7 @@ auto waybar::modules::Network::update() -> void
} }
} }
void waybar::modules::Network::disconnected() void waybar::modules::Network::disconnected() {
{
essid_.clear(); essid_.clear();
signal_strength_dbm_ = 0; signal_strength_dbm_ = 0;
signal_strength_ = 0; signal_strength_ = 0;
@ -218,17 +214,16 @@ void waybar::modules::Network::disconnected()
} }
// Based on https://gist.github.com/Yawning/c70d804d4b8ae78cc698 // Based on https://gist.github.com/Yawning/c70d804d4b8ae78cc698
int waybar::modules::Network::getExternalInterface() int waybar::modules::Network::getExternalInterface() {
{
static const uint32_t route_buffer_size = 8192; static const uint32_t route_buffer_size = 8192;
struct nlmsghdr *hdr = nullptr; struct nlmsghdr * hdr = nullptr;
struct rtmsg *rt = nullptr; struct rtmsg * rt = nullptr;
char resp[route_buffer_size] = {0}; char resp[route_buffer_size] = {0};
int ifidx = -1; int ifidx = -1;
/* Prepare request. */ /* Prepare request. */
constexpr uint32_t reqlen = NLMSG_SPACE(sizeof(*rt)); constexpr uint32_t reqlen = NLMSG_SPACE(sizeof(*rt));
char req[reqlen] = {0}; char req[reqlen] = {0};
/* Build the RTM_GETROUTE request. */ /* Build the RTM_GETROUTE request. */
hdr = reinterpret_cast<struct nlmsghdr *>(req); hdr = reinterpret_cast<struct nlmsghdr *>(req);
@ -257,7 +252,7 @@ int waybar::modules::Network::getExternalInterface()
/* Parse the response payload into netlink messages. */ /* Parse the response payload into netlink messages. */
for (hdr = reinterpret_cast<struct nlmsghdr *>(resp); NLMSG_OK(hdr, len); for (hdr = reinterpret_cast<struct nlmsghdr *>(resp); NLMSG_OK(hdr, len);
hdr = NLMSG_NEXT(hdr, len)) { hdr = NLMSG_NEXT(hdr, len)) {
if (hdr->nlmsg_type == NLMSG_DONE) { if (hdr->nlmsg_type == NLMSG_DONE) {
goto out; goto out;
} }
@ -284,10 +279,10 @@ int waybar::modules::Network::getExternalInterface()
/* Parse all the attributes for a single routing table entry. */ /* Parse all the attributes for a single routing table entry. */
struct rtattr *attr = RTM_RTA(rt); struct rtattr *attr = RTM_RTA(rt);
uint64_t attrlen = RTM_PAYLOAD(hdr); uint64_t attrlen = RTM_PAYLOAD(hdr);
bool has_gateway = false; bool has_gateway = false;
bool has_destination = false; bool has_destination = false;
int temp_idx = -1; int temp_idx = -1;
for (; RTA_OK(attr, attrlen); attr = RTA_NEXT(attr, attrlen)) { for (; RTA_OK(attr, attrlen); attr = RTA_NEXT(attr, attrlen)) {
/* Determine if this routing table entry corresponds to the default /* Determine if this routing table entry corresponds to the default
* route by seeing if it has a gateway, and if a destination addr is * route by seeing if it has a gateway, and if a destination addr is
@ -307,8 +302,8 @@ int waybar::modules::Network::getExternalInterface()
* Should be either missing, or maybe all 0s. Accept both. * Should be either missing, or maybe all 0s. Accept both.
*/ */
const uint32_t nr_zeroes = (family_ == AF_INET) ? 4 : 16; const uint32_t nr_zeroes = (family_ == AF_INET) ? 4 : 16;
unsigned char c = 0; unsigned char c = 0;
size_t dstlen = RTA_PAYLOAD(attr); size_t dstlen = RTA_PAYLOAD(attr);
if (dstlen != nr_zeroes) { if (dstlen != nr_zeroes) {
break; break;
} }
@ -320,7 +315,7 @@ int waybar::modules::Network::getExternalInterface()
} }
case RTA_OIF: case RTA_OIF:
/* The output interface index. */ /* The output interface index. */
temp_idx = *static_cast<int*>(RTA_DATA(attr)); temp_idx = *static_cast<int *>(RTA_DATA(attr));
break; break;
default: default:
break; break;
@ -341,7 +336,7 @@ out:
} }
void waybar::modules::Network::getInterfaceAddress() { void waybar::modules::Network::getInterfaceAddress() {
unsigned int cidrRaw; unsigned int cidrRaw;
struct ifaddrs *ifaddr, *ifa; struct ifaddrs *ifaddr, *ifa;
ipaddr_.clear(); ipaddr_.clear();
netmask_.clear(); netmask_.clear();
@ -352,13 +347,13 @@ void waybar::modules::Network::getInterfaceAddress() {
while (ifa != nullptr && ipaddr_.empty() && netmask_.empty()) { while (ifa != nullptr && ipaddr_.empty() && netmask_.empty()) {
if (ifa->ifa_addr != nullptr && ifa->ifa_addr->sa_family == family_) { if (ifa->ifa_addr != nullptr && ifa->ifa_addr->sa_family == family_) {
if (strcmp(ifa->ifa_name, ifname_.c_str()) == 0) { if (strcmp(ifa->ifa_name, ifname_.c_str()) == 0) {
ipaddr_ = inet_ntoa(((struct sockaddr_in*)ifa->ifa_addr)->sin_addr); ipaddr_ = inet_ntoa(((struct sockaddr_in *)ifa->ifa_addr)->sin_addr);
netmask_ = inet_ntoa(((struct sockaddr_in*)ifa->ifa_netmask)->sin_addr); netmask_ = inet_ntoa(((struct sockaddr_in *)ifa->ifa_netmask)->sin_addr);
cidrRaw = ((struct sockaddr_in *)(ifa->ifa_netmask))->sin_addr.s_addr; cidrRaw = ((struct sockaddr_in *)(ifa->ifa_netmask))->sin_addr.s_addr;
unsigned int cidr = 0; unsigned int cidr = 0;
while (cidrRaw) { while (cidrRaw) {
cidr += cidrRaw & 1; cidr += cidrRaw & 1;
cidrRaw >>= 1; cidrRaw >>= 1;
} }
cidr_ = cidr; cidr_ = cidr;
} }
@ -369,26 +364,22 @@ void waybar::modules::Network::getInterfaceAddress() {
} }
} }
int waybar::modules::Network::netlinkRequest(void *req, int waybar::modules::Network::netlinkRequest(void *req, uint32_t reqlen, uint32_t groups) {
uint32_t reqlen, uint32_t groups)
{
struct sockaddr_nl sa = {}; struct sockaddr_nl sa = {};
sa.nl_family = AF_NETLINK; sa.nl_family = AF_NETLINK;
sa.nl_groups = groups; sa.nl_groups = groups;
struct iovec iov = { req, reqlen }; struct iovec iov = {req, reqlen};
struct msghdr msg = { &sa, sizeof(sa), &iov, 1, nullptr, 0, 0 }; struct msghdr msg = {&sa, sizeof(sa), &iov, 1, nullptr, 0, 0};
return sendmsg(nl_socket_get_fd(info_sock_), &msg, 0); return sendmsg(nl_socket_get_fd(info_sock_), &msg, 0);
} }
int waybar::modules::Network::netlinkResponse(void *resp, int waybar::modules::Network::netlinkResponse(void *resp, uint32_t resplen, uint32_t groups) {
uint32_t resplen, uint32_t groups)
{
struct sockaddr_nl sa = {}; struct sockaddr_nl sa = {};
sa.nl_family = AF_NETLINK; sa.nl_family = AF_NETLINK;
sa.nl_groups = groups; sa.nl_groups = groups;
struct iovec iov = { resp, resplen }; struct iovec iov = {resp, resplen};
struct msghdr msg = { &sa, sizeof(sa), &iov, 1, nullptr, 0, 0 }; struct msghdr msg = {&sa, sizeof(sa), &iov, 1, nullptr, 0, 0};
auto ret = recvmsg(nl_socket_get_fd(info_sock_), &msg, 0); auto ret = recvmsg(nl_socket_get_fd(info_sock_), &msg, 0);
if (msg.msg_flags & MSG_TRUNC) { if (msg.msg_flags & MSG_TRUNC) {
return -1; return -1;
} }
@ -396,11 +387,10 @@ int waybar::modules::Network::netlinkResponse(void *resp,
} }
int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) {
int ret = 0; int ret = 0;
auto net = static_cast<waybar::modules::Network *>(data); auto net = static_cast<waybar::modules::Network *>(data);
bool need_update = false; bool need_update = false;
for (nlmsghdr *nh = nlmsg_hdr(msg); NLMSG_OK(nh, ret); for (nlmsghdr *nh = nlmsg_hdr(msg); NLMSG_OK(nh, ret); nh = NLMSG_NEXT(nh, ret)) {
nh = NLMSG_NEXT(nh, ret)) {
if (nh->nlmsg_type == RTM_NEWADDR) { if (nh->nlmsg_type == RTM_NEWADDR) {
need_update = true; need_update = true;
} }
@ -443,10 +433,10 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) {
} }
int waybar::modules::Network::handleScan(struct nl_msg *msg, void *data) { int waybar::modules::Network::handleScan(struct nl_msg *msg, void *data) {
auto net = static_cast<waybar::modules::Network *>(data); auto net = static_cast<waybar::modules::Network *>(data);
auto gnlh = static_cast<genlmsghdr *>(nlmsg_data(nlmsg_hdr(msg))); auto gnlh = static_cast<genlmsghdr *>(nlmsg_data(nlmsg_hdr(msg)));
struct nlattr* tb[NL80211_ATTR_MAX + 1]; struct nlattr * tb[NL80211_ATTR_MAX + 1];
struct nlattr* bss[NL80211_BSS_MAX + 1]; struct nlattr * bss[NL80211_BSS_MAX + 1];
struct nla_policy bss_policy[NL80211_BSS_MAX + 1]{}; struct nla_policy bss_policy[NL80211_BSS_MAX + 1]{};
bss_policy[NL80211_BSS_TSF].type = NLA_U64; bss_policy[NL80211_BSS_TSF].type = NLA_U64;
bss_policy[NL80211_BSS_FREQUENCY].type = NLA_U32; bss_policy[NL80211_BSS_FREQUENCY].type = NLA_U32;
@ -458,7 +448,8 @@ int waybar::modules::Network::handleScan(struct nl_msg *msg, void *data) {
bss_policy[NL80211_BSS_SIGNAL_UNSPEC].type = NLA_U8; bss_policy[NL80211_BSS_SIGNAL_UNSPEC].type = NLA_U8;
bss_policy[NL80211_BSS_STATUS].type = NLA_U32; 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; return NL_SKIP;
} }
if (tb[NL80211_ATTR_BSS] == nullptr) { if (tb[NL80211_ATTR_BSS] == nullptr) {
@ -476,21 +467,19 @@ int waybar::modules::Network::handleScan(struct nl_msg *msg, void *data) {
return NL_SKIP; 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) { if (bss[NL80211_BSS_INFORMATION_ELEMENTS] != nullptr) {
auto ies = auto ies = static_cast<char *>(nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]));
static_cast<char*>(nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS])); auto ies_len = nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
auto ies_len = nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
const auto hdr_len = 2; const auto hdr_len = 2;
while (ies_len > hdr_len && ies[0] != 0) { while (ies_len > hdr_len && ies[0] != 0) {
ies_len -= ies[1] + hdr_len; ies_len -= ies[1] + hdr_len;
ies += ies[1] + hdr_len; ies += ies[1] + hdr_len;
} }
if (ies_len > hdr_len && ies_len > ies[1] + hdr_len) { if (ies_len > hdr_len && ies_len > ies[1] + hdr_len) {
auto essid_begin = ies + hdr_len; auto essid_begin = ies + hdr_len;
auto essid_end = essid_begin + ies[1]; auto essid_end = essid_begin + ies[1];
std::string essid_raw; std::string essid_raw;
std::copy(essid_begin, essid_end, std::back_inserter(essid_raw)); std::copy(essid_begin, essid_end, std::back_inserter(essid_raw));
essid_ = Glib::Markup::escape_text(essid_raw); essid_ = Glib::Markup::escape_text(essid_raw);
@ -506,16 +495,15 @@ void waybar::modules::Network::parseSignal(struct nlattr **bss) {
// WiFi-hardware usually operates in the range -90 to -20dBm. // WiFi-hardware usually operates in the range -90 to -20dBm.
const int hardwareMax = -20; const int hardwareMax = -20;
const int hardwareMin = -90; const int hardwareMin = -90;
signal_strength_ = ((signal_strength_dbm_ - hardwareMin) signal_strength_ =
/ double{hardwareMax - hardwareMin}) * 100; ((signal_strength_dbm_ - hardwareMin) / double{hardwareMax - hardwareMin}) * 100;
} }
if (bss[NL80211_BSS_SIGNAL_UNSPEC] != nullptr) { if (bss[NL80211_BSS_SIGNAL_UNSPEC] != nullptr) {
signal_strength_ = nla_get_u8(bss[NL80211_BSS_SIGNAL_UNSPEC]); signal_strength_ = nla_get_u8(bss[NL80211_BSS_SIGNAL_UNSPEC]);
} }
} }
bool waybar::modules::Network::associatedOrJoined(struct nlattr** bss) bool waybar::modules::Network::associatedOrJoined(struct nlattr **bss) {
{
if (bss[NL80211_BSS_STATUS] == nullptr) { if (bss[NL80211_BSS_STATUS] == nullptr) {
return false; return false;
} }
@ -530,16 +518,16 @@ bool waybar::modules::Network::associatedOrJoined(struct nlattr** bss)
} }
} }
auto waybar::modules::Network::getInfo() -> void auto waybar::modules::Network::getInfo() -> void {
{
getInterfaceAddress(); getInterfaceAddress();
struct nl_msg* nl_msg = nlmsg_alloc(); struct nl_msg *nl_msg = nlmsg_alloc();
if (nl_msg == nullptr) { if (nl_msg == nullptr) {
return; return;
} }
if (genlmsg_put(nl_msg, NL_AUTO_PORT, NL_AUTO_SEQ, nl80211_id_, 0, NLM_F_DUMP, if (genlmsg_put(
NL80211_CMD_GET_SCAN, 0) == nullptr nl_msg, NL_AUTO_PORT, NL_AUTO_SEQ, nl80211_id_, 0, NLM_F_DUMP, NL80211_CMD_GET_SCAN, 0) ==
|| nla_put_u32(nl_msg, NL80211_ATTR_IFINDEX, ifid_) < 0) { nullptr ||
nla_put_u32(nl_msg, NL80211_ATTR_IFINDEX, ifid_) < 0) {
nlmsg_free(nl_msg); nlmsg_free(nl_msg);
return; return;
} }

View File

@ -1,7 +1,7 @@
#include "modules/pulseaudio.hpp" #include "modules/pulseaudio.hpp"
#include <array> #include <array>
waybar::modules::Pulseaudio::Pulseaudio(const std::string& id, const Json::Value &config) waybar::modules::Pulseaudio::Pulseaudio(const std::string &id, const Json::Value &config)
: ALabel(config, "{volume}%"), : ALabel(config, "{volume}%"),
mainloop_(nullptr), mainloop_(nullptr),
mainloop_api_(nullptr), mainloop_api_(nullptr),
@ -24,10 +24,9 @@ waybar::modules::Pulseaudio::Pulseaudio(const std::string& id, const Json::Value
if (context_ == nullptr) { if (context_ == nullptr) {
throw std::runtime_error("pa_context_new() failed."); throw std::runtime_error("pa_context_new() failed.");
} }
if (pa_context_connect(context_, nullptr, PA_CONTEXT_NOAUTOSPAWN, if (pa_context_connect(context_, nullptr, PA_CONTEXT_NOAUTOSPAWN, nullptr) < 0) {
nullptr) < 0) { auto err =
auto err = fmt::format("pa_context_connect() failed: {}", fmt::format("pa_context_connect() failed: {}", pa_strerror(pa_context_errno(context_)));
pa_strerror(pa_context_errno(context_)));
throw std::runtime_error(err); throw std::runtime_error(err);
} }
pa_context_set_state_callback(context_, contextStateCb, this); pa_context_set_state_callback(context_, contextStateCb, this);
@ -38,11 +37,9 @@ waybar::modules::Pulseaudio::Pulseaudio(const std::string& id, const Json::Value
// define the pulse scroll events only when no user provided // define the pulse scroll events only when no user provided
// events are configured // events are configured
if (!config["on-scroll-up"].isString() && if (!config["on-scroll-up"].isString() && !config["on-scroll-down"].isString()) {
!config["on-scroll-down"].isString()) {
event_box_.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK); event_box_.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK);
event_box_.signal_scroll_event().connect( event_box_.signal_scroll_event().connect(sigc::mem_fun(*this, &Pulseaudio::handleScroll));
sigc::mem_fun(*this, &Pulseaudio::handleScroll));
} }
} }
@ -52,8 +49,7 @@ waybar::modules::Pulseaudio::~Pulseaudio() {
pa_threaded_mainloop_free(mainloop_); pa_threaded_mainloop_free(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); auto pa = static_cast<waybar::modules::Pulseaudio *>(data);
switch (pa_context_get_state(c)) { switch (pa_context_get_state(c)) {
case PA_CONTEXT_TERMINATED: case PA_CONTEXT_TERMINATED:
@ -77,8 +73,8 @@ void waybar::modules::Pulseaudio::contextStateCb(pa_context *c, void *data)
bool waybar::modules::Pulseaudio::handleScroll(GdkEventScroll *e) { bool waybar::modules::Pulseaudio::handleScroll(GdkEventScroll *e) {
// Avoid concurrent scroll event // Avoid concurrent scroll event
bool direction_up = false; bool direction_up = false;
uint16_t change = config_["scroll-step"].isUInt() ? config_["scroll-step"].asUInt() * 100 : 100; uint16_t change = config_["scroll-step"].isUInt() ? config_["scroll-step"].asUInt() * 100 : 100;
pa_cvolume pa_volume = pa_volume_; pa_cvolume pa_volume = pa_volume_;
if (scrolling_) { if (scrolling_) {
@ -94,8 +90,7 @@ bool waybar::modules::Pulseaudio::handleScroll(GdkEventScroll *e) {
if (e->direction == GDK_SCROLL_SMOOTH) { if (e->direction == GDK_SCROLL_SMOOTH) {
gdouble delta_x, delta_y; gdouble delta_x, delta_y;
gdk_event_get_scroll_deltas(reinterpret_cast<const GdkEvent *>(e), &delta_x, gdk_event_get_scroll_deltas(reinterpret_cast<const GdkEvent *>(e), &delta_x, &delta_y);
&delta_y);
if (delta_y < 0) { if (delta_y < 0) {
direction_up = true; direction_up = true;
} else if (delta_y > 0) { } else if (delta_y > 0) {
@ -104,13 +99,16 @@ bool waybar::modules::Pulseaudio::handleScroll(GdkEventScroll *e) {
} }
if (direction_up) { if (direction_up) {
if (volume_ + 1 < 100) pa_cvolume_inc(&pa_volume, change); if (volume_ + 1 < 100) {
pa_cvolume_inc(&pa_volume, change);
}
} else { } else {
if (volume_ - 1 > 0) pa_cvolume_dec(&pa_volume, change); if (volume_ - 1 > 0) {
pa_cvolume_dec(&pa_volume, change);
}
} }
pa_context_set_sink_volume_by_index(context_, sink_idx_, &pa_volume, pa_context_set_sink_volume_by_index(context_, sink_idx_, &pa_volume, volumeModifyCb, this);
volumeModifyCb, this);
return true; return true;
} }
@ -118,48 +116,39 @@ bool waybar::modules::Pulseaudio::handleScroll(GdkEventScroll *e) {
/* /*
* Called when an event we subscribed to occurs. * Called when an event we subscribed to occurs.
*/ */
void waybar::modules::Pulseaudio::subscribeCb(pa_context* context, void waybar::modules::Pulseaudio::subscribeCb(pa_context * context,
pa_subscription_event_type_t type, uint32_t idx, void* data) pa_subscription_event_type_t type, uint32_t idx,
{ void *data) {
unsigned facility = type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK; unsigned facility = type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK;
if (facility == PA_SUBSCRIPTION_EVENT_SINK) {
switch (facility) { pa_context_get_sink_info_by_index(context, idx, sinkInfoCb, data);
case PA_SUBSCRIPTION_EVENT_SINK:
pa_context_get_sink_info_by_index(context, idx, sinkInfoCb, data);
break;
default:
break;
} }
} }
/* /*
* Called in response to a volume change request * Called in response to a volume change request
*/ */
void waybar::modules::Pulseaudio::volumeModifyCb(pa_context *c, int success, void waybar::modules::Pulseaudio::volumeModifyCb(pa_context *c, int success, void *data) {
void *data) {
auto pa = static_cast<waybar::modules::Pulseaudio *>(data); auto pa = static_cast<waybar::modules::Pulseaudio *>(data);
if (success) { if (success != 0) {
pa_context_get_sink_info_by_index(pa->context_, pa->sink_idx_, sinkInfoCb, pa_context_get_sink_info_by_index(pa->context_, pa->sink_idx_, sinkInfoCb, data);
data);
} }
} }
/* /*
* Called when the requested sink information is ready. * Called when the requested sink information is ready.
*/ */
void waybar::modules::Pulseaudio::sinkInfoCb(pa_context * /*context*/, void waybar::modules::Pulseaudio::sinkInfoCb(pa_context * /*context*/, const pa_sink_info *i,
const pa_sink_info *i, int /*eol*/, int /*eol*/, void * data) {
void *data) {
if (i != nullptr) { if (i != nullptr) {
auto pa = static_cast<waybar::modules::Pulseaudio *>(data); auto pa = static_cast<waybar::modules::Pulseaudio *>(data);
pa->pa_volume_ = i->volume; pa->pa_volume_ = i->volume;
float volume = static_cast<float>(pa_cvolume_avg(&(pa->pa_volume_))) / float volume = static_cast<float>(pa_cvolume_avg(&(pa->pa_volume_))) / float{PA_VOLUME_NORM};
float{PA_VOLUME_NORM};
pa->sink_idx_ = i->index; pa->sink_idx_ = i->index;
pa->volume_ = std::round(volume * 100.0f); pa->volume_ = std::round(volume * 100.0F);
pa->muted_ = i->mute != 0; pa->muted_ = i->mute != 0;
pa->desc_ = i->description; pa->desc_ = i->description;
pa->port_name_ = i->active_port ? i->active_port->name : "Unknown"; pa->port_name_ = i->active_port != nullptr ? i->active_port->name : "Unknown";
pa->dp.emit(); pa->dp.emit();
} }
} }
@ -168,30 +157,27 @@ void waybar::modules::Pulseaudio::sinkInfoCb(pa_context * /*context*/,
* Called when the requested information on the server is ready. This is * Called when the requested information on the server is ready. This is
* used to find the default PulseAudio sink. * 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,
const pa_server_info *i, void *data) 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);
} }
static const std::array<std::string, 9> ports = { static const std::array<std::string, 9> ports = {
"headphones", "headphones",
"speaker", "speaker",
"hdmi", "hdmi",
"headset", "headset",
"handsfree", "handsfree",
"portable", "portable",
"car", "car",
"hifi", "hifi",
"phone", "phone",
}; };
const std::string waybar::modules::Pulseaudio::getPortIcon() const const std::string waybar::modules::Pulseaudio::getPortIcon() const {
{
std::string nameLC = port_name_; std::string nameLC = port_name_;
std::transform(nameLC.begin(), nameLC.end(), nameLC.begin(), ::tolower); std::transform(nameLC.begin(), nameLC.end(), nameLC.begin(), ::tolower);
for (auto const& port : ports) { for (auto const &port : ports) {
if (nameLC.find(port) != std::string::npos) { if (nameLC.find(port) != std::string::npos) {
return port; return port;
} }
@ -199,26 +185,23 @@ const std::string waybar::modules::Pulseaudio::getPortIcon() const
return port_name_; return port_name_;
} }
auto waybar::modules::Pulseaudio::update() -> void auto waybar::modules::Pulseaudio::update() -> void {
{
auto format = format_; auto format = format_;
if (muted_) { if (muted_) {
format = format = config_["format-muted"].isString() ? config_["format-muted"].asString() : format;
config_["format-muted"].isString() ? config_["format-muted"].asString() : format;
label_.get_style_context()->add_class("muted"); label_.get_style_context()->add_class("muted");
} else { } else {
label_.get_style_context()->remove_class("muted"); label_.get_style_context()->remove_class("muted");
if (port_name_.find("a2dp_sink") != std::string::npos) { if (port_name_.find("a2dp_sink") != std::string::npos) {
format = config_["format-bluetooth"].isString() format =
? config_["format-bluetooth"].asString() : format; config_["format-bluetooth"].isString() ? config_["format-bluetooth"].asString() : format;
label_.get_style_context()->add_class("bluetooth"); label_.get_style_context()->add_class("bluetooth");
} else { } else {
label_.get_style_context()->remove_class("bluetooth"); label_.get_style_context()->remove_class("bluetooth");
} }
} }
label_.set_markup( label_.set_markup(fmt::format(
fmt::format(format, fmt::arg("volume", volume_), format, fmt::arg("volume", volume_), fmt::arg("icon", getIcon(volume_, getPortIcon()))));
fmt::arg("icon", getIcon(volume_, getPortIcon()))));
if (tooltipEnabled()) { if (tooltipEnabled()) {
label_.set_tooltip_text(desc_); label_.set_tooltip_text(desc_);
} }

View File

@ -1,66 +1,73 @@
#include "modules/sni/host.hpp" #include "modules/sni/host.hpp"
#include <iostream> #include <iostream>
using namespace waybar::modules::SNI; namespace waybar::modules::SNI {
Host::Host(const std::size_t id, const Json::Value &config, Host::Host(const std::size_t id, const Json::Value& config,
const std::function<void(std::unique_ptr<Item>&)>& on_add, const std::function<void(std::unique_ptr<Item>&)>& on_add,
const std::function<void(std::unique_ptr<Item>&)>& on_remove) const std::function<void(std::unique_ptr<Item>&)>& on_remove)
: bus_name_("org.kde.StatusNotifierHost-" + std::to_string(getpid()) + "-" + std::to_string(id)), : bus_name_("org.kde.StatusNotifierHost-" + std::to_string(getpid()) + "-" +
object_path_("/StatusNotifierHost/" + std::to_string(id)), std::to_string(id)),
bus_name_id_(Gio::DBus::own_name(Gio::DBus::BusType::BUS_TYPE_SESSION, bus_name_, object_path_("/StatusNotifierHost/" + std::to_string(id)),
sigc::mem_fun(*this, &Host::busAcquired))), bus_name_id_(Gio::DBus::own_name(Gio::DBus::BusType::BUS_TYPE_SESSION, bus_name_,
config_(config), on_add_(on_add), on_remove_(on_remove) sigc::mem_fun(*this, &Host::busAcquired))),
{ config_(config),
on_add_(on_add),
on_remove_(on_remove) {}
Host::~Host() {
if (bus_name_id_ > 0) {
Gio::DBus::unwatch_name(bus_name_id_);
bus_name_id_ = 0;
}
if (watcher_id_ > 0) {
Gio::DBus::unwatch_name(watcher_id_);
watcher_id_ = 0;
}
g_cancellable_cancel(cancellable_);
g_clear_object(&cancellable_);
g_clear_object(&watcher_);
} }
Host::~Host() void Host::busAcquired(const Glib::RefPtr<Gio::DBus::Connection>& conn, Glib::ustring name) {
{ watcher_id_ = Gio::DBus::watch_name(conn,
Gio::DBus::unwatch_name(bus_name_id_); "org.kde.StatusNotifierWatcher",
} sigc::mem_fun(*this, &Host::nameAppeared),
sigc::mem_fun(*this, &Host::nameVanished));
void Host::busAcquired(const Glib::RefPtr<Gio::DBus::Connection>& conn, Glib::ustring name)
{
watcher_id_ = Gio::DBus::watch_name(conn, "org.kde.StatusNotifierWatcher",
sigc::mem_fun(*this, &Host::nameAppeared), sigc::mem_fun(*this, &Host::nameVanished));
} }
void Host::nameAppeared(const Glib::RefPtr<Gio::DBus::Connection>& conn, const Glib::ustring name, void Host::nameAppeared(const Glib::RefPtr<Gio::DBus::Connection>& conn, const Glib::ustring name,
const Glib::ustring& name_owner) const Glib::ustring& name_owner) {
{
if (cancellable_ != nullptr) { if (cancellable_ != nullptr) {
// TODO // TODO
return; return;
} }
cancellable_ = g_cancellable_new(); cancellable_ = g_cancellable_new();
sn_watcher_proxy_new( sn_watcher_proxy_new(conn->gobj(),
conn->gobj(), G_DBUS_PROXY_FLAGS_NONE,
G_DBUS_PROXY_FLAGS_NONE, "org.kde.StatusNotifierWatcher",
"org.kde.StatusNotifierWatcher", "/StatusNotifierWatcher",
"/StatusNotifierWatcher", cancellable_,
cancellable_, &Host::proxyReady, this); &Host::proxyReady,
this);
} }
void Host::nameVanished(const Glib::RefPtr<Gio::DBus::Connection>& conn, const Glib::ustring name) void Host::nameVanished(const Glib::RefPtr<Gio::DBus::Connection>& conn, const Glib::ustring name) {
{
g_cancellable_cancel(cancellable_); g_cancellable_cancel(cancellable_);
g_clear_object(&cancellable_); g_clear_object(&cancellable_);
g_clear_object(&watcher_); g_clear_object(&watcher_);
items_.clear(); items_.clear();
} }
void Host::proxyReady(GObject* src, GAsyncResult* res, void Host::proxyReady(GObject* src, GAsyncResult* res, gpointer data) {
gpointer data) GError* error = nullptr;
{
GError* error = nullptr;
SnWatcher* watcher = sn_watcher_proxy_new_finish(res, &error); SnWatcher* watcher = sn_watcher_proxy_new_finish(res, &error);
if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
std::cerr << error->message << std::endl; std::cerr << error->message << std::endl;
g_error_free(error); g_error_free(error);
return; return;
} }
auto host = static_cast<SNI::Host *>(data); auto host = static_cast<SNI::Host*>(data);
host->watcher_ = watcher; host->watcher_ = watcher;
if (error != nullptr) { if (error != nullptr) {
std::cerr << error->message << std::endl; std::cerr << error->message << std::endl;
@ -68,13 +75,10 @@ void Host::proxyReady(GObject* src, GAsyncResult* res,
return; return;
} }
sn_watcher_call_register_host( sn_watcher_call_register_host(
host->watcher_, host->object_path_.c_str(), host->cancellable_, host->watcher_, host->object_path_.c_str(), host->cancellable_, &Host::registerHost, data);
&Host::registerHost, data);
} }
void Host::registerHost(GObject* src, GAsyncResult* res, void Host::registerHost(GObject* src, GAsyncResult* res, gpointer data) {
gpointer data)
{
GError* error = nullptr; GError* error = nullptr;
sn_watcher_call_register_host_finish(SN_WATCHER(src), res, &error); sn_watcher_call_register_host_finish(SN_WATCHER(src), res, &error);
if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
@ -82,18 +86,16 @@ void Host::registerHost(GObject* src, GAsyncResult* res,
g_error_free(error); g_error_free(error);
return; return;
} }
auto host = static_cast<SNI::Host *>(data); auto host = static_cast<SNI::Host*>(data);
if (error != nullptr) { if (error != nullptr) {
std::cerr << error->message << std::endl; std::cerr << error->message << std::endl;
g_error_free(error); g_error_free(error);
return; return;
} }
g_signal_connect(host->watcher_, "item-registered", g_signal_connect(host->watcher_, "item-registered", G_CALLBACK(&Host::itemRegistered), data);
G_CALLBACK(&Host::itemRegistered), data); g_signal_connect(host->watcher_, "item-unregistered", G_CALLBACK(&Host::itemUnregistered), data);
g_signal_connect(host->watcher_, "item-unregistered",
G_CALLBACK(&Host::itemUnregistered), data);
auto items = sn_watcher_dup_registered_items(host->watcher_); auto items = sn_watcher_dup_registered_items(host->watcher_);
if (items) { if (items != nullptr) {
for (uint32_t i = 0; items[i] != nullptr; i += 1) { for (uint32_t i = 0; items[i] != nullptr; i += 1) {
host->addRegisteredItem(items[i]); host->addRegisteredItem(items[i]);
} }
@ -101,16 +103,13 @@ void Host::registerHost(GObject* src, GAsyncResult* res,
g_strfreev(items); g_strfreev(items);
} }
void Host::itemRegistered(SnWatcher* watcher, const gchar* service, gpointer data) void Host::itemRegistered(SnWatcher* watcher, const gchar* service, gpointer data) {
{ auto host = static_cast<SNI::Host*>(data);
auto host = static_cast<SNI::Host *>(data);
host->addRegisteredItem(service); host->addRegisteredItem(service);
} }
void Host::itemUnregistered( void Host::itemUnregistered(SnWatcher* watcher, const gchar* service, gpointer data) {
SnWatcher* watcher, const gchar* service, gpointer data) auto host = static_cast<SNI::Host*>(data);
{
auto host = static_cast<SNI::Host *>(data);
auto [bus_name, object_path] = host->getBusNameAndObjectPath(service); auto [bus_name, object_path] = host->getBusNameAndObjectPath(service);
for (auto it = host->items_.begin(); it != host->items_.end(); ++it) { for (auto it = host->items_.begin(); it != host->items_.end(); ++it) {
if ((*it)->bus_name == bus_name && (*it)->object_path == object_path) { if ((*it)->bus_name == bus_name && (*it)->object_path == object_path) {
@ -121,19 +120,18 @@ void Host::itemUnregistered(
} }
} }
std::tuple<std::string, std::string> Host::getBusNameAndObjectPath( std::tuple<std::string, std::string> Host::getBusNameAndObjectPath(const std::string service) {
const std::string service) auto it = service.find('/');
{
auto it = service.find("/");
if (it != std::string::npos) { if (it != std::string::npos) {
return {service.substr(0, it), service.substr(it)}; return {service.substr(0, it), service.substr(it)};
} }
return {service, "/StatusNotifierItem"}; return {service, "/StatusNotifierItem"};
} }
void Host::addRegisteredItem(std::string service) void Host::addRegisteredItem(std::string service) {
{
auto [bus_name, object_path] = getBusNameAndObjectPath(service); auto [bus_name, object_path] = getBusNameAndObjectPath(service);
items_.emplace_back(new Item(bus_name, object_path, config_)); items_.emplace_back(new Item(bus_name, object_path, config_));
on_add_(items_.back()); on_add_(items_.back());
} }
}

View File

@ -1,38 +1,44 @@
#include "modules/sni/item.hpp" #include "modules/sni/item.hpp"
#include <iostream>
#include <glibmm/main.h> #include <glibmm/main.h>
#include <iostream>
using namespace Glib; namespace waybar::modules::SNI {
static const ustring SNI_INTERFACE_NAME = sn_item_interface_info()->name; static const Glib::ustring SNI_INTERFACE_NAME = sn_item_interface_info()->name;
static const unsigned UPDATE_DEBOUNCE_TIME = 10; static const unsigned UPDATE_DEBOUNCE_TIME = 10;
waybar::modules::SNI::Item::Item(std::string bn, std::string op, const Json::Value& config) Item::Item(const std::string& bn, const std::string& op, const Json::Value& config)
: bus_name(bn), object_path(op), icon_size(16), effective_icon_size(0), : bus_name(bn),
update_pending_(false) { object_path(op),
icon_size(16),
effective_icon_size(0),
icon_theme(Gtk::IconTheme::create()),
update_pending_(false) {
if (config["icon-size"].isUInt()) { if (config["icon-size"].isUInt()) {
icon_size = config["icon-size"].asUInt(); icon_size = config["icon-size"].asUInt();
} }
event_box.add(image); event_box.add(image);
event_box.add_events(Gdk::BUTTON_PRESS_MASK); event_box.add_events(Gdk::BUTTON_PRESS_MASK);
event_box.signal_button_press_event().connect( event_box.signal_button_press_event().connect(sigc::mem_fun(*this, &Item::handleClick));
sigc::mem_fun(*this, &Item::handleClick));
cancellable_ = Gio::Cancellable::create(); cancellable_ = Gio::Cancellable::create();
auto interface = Glib::wrap(sn_item_interface_info(), true); auto interface = Glib::wrap(sn_item_interface_info(), true);
Gio::DBus::Proxy::create_for_bus(Gio::DBus::BusType::BUS_TYPE_SESSION, bus_name, Gio::DBus::Proxy::create_for_bus(Gio::DBus::BusType::BUS_TYPE_SESSION,
object_path, SNI_INTERFACE_NAME, sigc::mem_fun(*this, &Item::proxyReady), bus_name,
cancellable_, interface); object_path,
SNI_INTERFACE_NAME,
sigc::mem_fun(*this, &Item::proxyReady),
cancellable_,
interface);
} }
void waybar::modules::SNI::Item::proxyReady(Glib::RefPtr<Gio::AsyncResult>& result) { void Item::proxyReady(Glib::RefPtr<Gio::AsyncResult>& result) {
try { try {
this->proxy_ = Gio::DBus::Proxy::create_for_bus_finish(result); this->proxy_ = Gio::DBus::Proxy::create_for_bus_finish(result);
/* Properties are already cached during object creation */ /* Properties are already cached during object creation */
auto cached_properties = this->proxy_->get_cached_property_names(); auto cached_properties = this->proxy_->get_cached_property_names();
for (const auto& name: cached_properties) { for (const auto& name : cached_properties) {
Glib::VariantBase value; Glib::VariantBase value;
this->proxy_->get_cached_property(value, name); this->proxy_->get_cached_property(value, name);
setProperty(name, value); setProperty(name, value);
@ -41,34 +47,32 @@ void waybar::modules::SNI::Item::proxyReady(Glib::RefPtr<Gio::AsyncResult>& resu
this->proxy_->signal_signal().connect(sigc::mem_fun(*this, &Item::onSignal)); this->proxy_->signal_signal().connect(sigc::mem_fun(*this, &Item::onSignal));
if (this->id.empty() || this->category.empty() || this->status.empty()) { if (this->id.empty() || this->category.empty() || this->status.empty()) {
std::cerr << "Invalid Status Notifier Item: " + this->bus_name + "," + std::cerr << "Invalid Status Notifier Item: " + this->bus_name + "," + this->object_path
this->object_path << std::endl; << std::endl;
return; return;
} }
if (!this->icon_theme_path.empty()) {
Glib::RefPtr<Gtk::IconTheme> icon_theme = Gtk::IconTheme::get_default();
icon_theme->append_search_path(this->icon_theme_path);
}
this->updateImage(); this->updateImage();
// this->event_box.set_tooltip_text(this->title); // this->event_box.set_tooltip_text(this->title);
} catch (const Glib::Error& err) { } catch (const Glib::Error& err) {
g_error("Failed to create DBus Proxy for %s %s: %s", bus_name.c_str(), g_error("Failed to create DBus Proxy for %s %s: %s",
object_path.c_str(), err.what().c_str()); bus_name.c_str(),
object_path.c_str(),
err.what().c_str());
} catch (const std::exception& err) { } catch (const std::exception& err) {
g_error("Failed to create DBus Proxy for %s %s: %s", bus_name.c_str(), g_error("Failed to create DBus Proxy for %s %s: %s",
object_path.c_str(), err.what()); bus_name.c_str(),
object_path.c_str(),
err.what());
} }
} }
template<typename T> template <typename T>
T get_variant(VariantBase& value) { T get_variant(Glib::VariantBase& value) {
return VariantBase::cast_dynamic<Variant<T>>(value).get(); return Glib::VariantBase::cast_dynamic<Glib::Variant<T>>(value).get();
} }
void void Item::setProperty(const Glib::ustring& name, Glib::VariantBase& value) {
waybar::modules::SNI::Item::setProperty(const ustring& name,
VariantBase& value) {
if (name == "Category") { if (name == "Category") {
category = get_variant<std::string>(value); category = get_variant<std::string>(value);
} else if (name == "Id") { } else if (name == "Id") {
@ -97,6 +101,9 @@ waybar::modules::SNI::Item::setProperty(const ustring& name,
// TODO: tooltip // TODO: tooltip
} else if (name == "IconThemePath") { } else if (name == "IconThemePath") {
icon_theme_path = get_variant<std::string>(value); icon_theme_path = get_variant<std::string>(value);
if (!icon_theme_path.empty()) {
icon_theme->set_search_path({icon_theme_path});
}
} else if (name == "Menu") { } else if (name == "Menu") {
menu = get_variant<std::string>(value); menu = get_variant<std::string>(value);
} else if (name == "ItemIsMenu") { } else if (name == "ItemIsMenu") {
@ -104,33 +111,30 @@ waybar::modules::SNI::Item::setProperty(const ustring& name,
} }
} }
void void Item::getUpdatedProperties() {
waybar::modules::SNI::Item::getUpdatedProperties() {
update_pending_ = false; update_pending_ = false;
auto params = VariantContainerBase::create_tuple({ auto params = Glib::VariantContainerBase::create_tuple(
Variant<ustring>::create(SNI_INTERFACE_NAME) {Glib::Variant<Glib::ustring>::create(SNI_INTERFACE_NAME)});
});
proxy_->call("org.freedesktop.DBus.Properties.GetAll", proxy_->call("org.freedesktop.DBus.Properties.GetAll",
sigc::mem_fun(*this, &Item::processUpdatedProperties), params); sigc::mem_fun(*this, &Item::processUpdatedProperties),
params);
}; };
void void Item::processUpdatedProperties(Glib::RefPtr<Gio::AsyncResult>& _result) {
waybar::modules::SNI::Item::processUpdatedProperties(
Glib::RefPtr<Gio::AsyncResult>& _result) {
try { try {
auto result = proxy_->call_finish(_result); auto result = proxy_->call_finish(_result);
// extract "a{sv}" from VariantContainerBase // extract "a{sv}" from VariantContainerBase
Variant<std::map<ustring, VariantBase>> properties_variant; Glib::Variant<std::map<Glib::ustring, Glib::VariantBase>> properties_variant;
result.get_child(properties_variant); result.get_child(properties_variant);
auto properties = properties_variant.get(); auto properties = properties_variant.get();
for (const auto& [name, value]: properties) { for (const auto& [name, value] : properties) {
VariantBase old_value; Glib::VariantBase old_value;
proxy_->get_cached_property(old_value, name); proxy_->get_cached_property(old_value, name);
if (!value.equal(old_value)) { if (!value.equal(old_value)) {
proxy_->set_cached_property(name, value); proxy_->set_cached_property(name, value);
setProperty(name, const_cast<VariantBase&>(value)); setProperty(name, const_cast<Glib::VariantBase&>(value));
} }
} }
@ -143,41 +147,34 @@ waybar::modules::SNI::Item::processUpdatedProperties(
} }
} }
void void Item::onSignal(const Glib::ustring& sender_name, const Glib::ustring& signal_name,
waybar::modules::SNI::Item::onSignal(const ustring& sender_name, const Glib::VariantContainerBase& arguments) {
const ustring& signal_name, const VariantContainerBase& arguments) {
if (!update_pending_ && signal_name.compare(0, 3, "New") == 0) { if (!update_pending_ && signal_name.compare(0, 3, "New") == 0) {
/* Debounce signals and schedule update of all properties. /* Debounce signals and schedule update of all properties.
* Based on behavior of Plasma dataengine for StatusNotifierItem. * Based on behavior of Plasma dataengine for StatusNotifierItem.
*/ */
update_pending_ = true; update_pending_ = true;
Glib::signal_timeout().connect_once( Glib::signal_timeout().connect_once(sigc::mem_fun(*this, &Item::getUpdatedProperties),
sigc::mem_fun(*this, &Item::getUpdatedProperties), UPDATE_DEBOUNCE_TIME); UPDATE_DEBOUNCE_TIME);
} }
} }
static void pixbuf_data_deleter(const guint8* data) { g_free((void*)data); }
static void Glib::RefPtr<Gdk::Pixbuf> Item::extractPixBuf(GVariant* variant) {
pixbuf_data_deleter(const guint8* data) { GVariantIter* it;
g_free((void*) data);
}
Glib::RefPtr<Gdk::Pixbuf>
waybar::modules::SNI::Item::extractPixBuf(GVariant *variant) {
GVariantIter *it;
g_variant_get(variant, "a(iiay)", &it); g_variant_get(variant, "a(iiay)", &it);
if (it == nullptr) { if (it == nullptr) {
return Glib::RefPtr<Gdk::Pixbuf>{}; return Glib::RefPtr<Gdk::Pixbuf>{};
} }
GVariant *val; GVariant* val;
gint lwidth = 0; gint lwidth = 0;
gint lheight = 0; gint lheight = 0;
gint width; gint width;
gint height; gint height;
guchar *array = nullptr; guchar* array = nullptr;
while (g_variant_iter_loop(it, "(ii@ay)", &width, &height, &val)) { while (g_variant_iter_loop(it, "(ii@ay)", &width, &height, &val)) {
if (width > 0 && height > 0 && val != nullptr && if (width > 0 && height > 0 && val != nullptr && width * height > lwidth * lheight) {
width * height > lwidth * lheight) {
auto size = g_variant_get_size(val); auto size = g_variant_get_size(val);
/* Sanity check */ /* Sanity check */
if (size == 4U * width * height) { if (size == 4U * width * height) {
@ -187,7 +184,7 @@ waybar::modules::SNI::Item::extractPixBuf(GVariant *variant) {
if (array != nullptr) { if (array != nullptr) {
g_free(array); g_free(array);
} }
array = static_cast<guchar *>(g_memdup(data, size)); array = static_cast<guchar*>(g_memdup(data, size));
lwidth = width; lwidth = width;
lheight = height; lheight = height;
} }
@ -204,15 +201,19 @@ waybar::modules::SNI::Item::extractPixBuf(GVariant *variant) {
array[i + 2] = array[i + 3]; array[i + 2] = array[i + 3];
array[i + 3] = alpha; array[i + 3] = alpha;
} }
return Gdk::Pixbuf::create_from_data(array, Gdk::Colorspace::COLORSPACE_RGB, return Gdk::Pixbuf::create_from_data(array,
true, 8, lwidth, lheight, 4 * lwidth, Gdk::Colorspace::COLORSPACE_RGB,
true,
8,
lwidth,
lheight,
4 * lwidth,
&pixbuf_data_deleter); &pixbuf_data_deleter);
} }
return Glib::RefPtr<Gdk::Pixbuf>{}; return Glib::RefPtr<Gdk::Pixbuf>{};
} }
void waybar::modules::SNI::Item::updateImage() void Item::updateImage() {
{
image.set_from_icon_name("image-missing", Gtk::ICON_SIZE_MENU); image.set_from_icon_name("image-missing", Gtk::ICON_SIZE_MENU);
image.set_pixel_size(icon_size); image.set_pixel_size(icon_size);
if (!icon_name.empty()) { if (!icon_name.empty()) {
@ -227,55 +228,60 @@ void waybar::modules::SNI::Item::updateImage()
if (pixbuf->gobj() != nullptr) { if (pixbuf->gobj() != nullptr) {
// An icon specified by path and filename may be the wrong size for // An icon specified by path and filename may be the wrong size for
// the tray // the tray
pixbuf = pixbuf->scale_simple(icon_size, icon_size, pixbuf = pixbuf->scale_simple(icon_size, icon_size, Gdk::InterpType::INTERP_BILINEAR);
Gdk::InterpType::INTERP_BILINEAR);
image.set(pixbuf); image.set(pixbuf);
} }
} else { } else {
image.set(getIconByName(icon_name, icon_size)); image.set(getIconByName(icon_name, icon_size));
} }
} catch (Glib::Error &e) { } catch (Glib::Error& e) {
std::cerr << "Exception: " << e.what() << std::endl; std::cerr << "Exception: " << e.what() << std::endl;
} }
} else if (icon_pixmap) { } else if (icon_pixmap) {
// An icon extracted may be the wrong size for the tray // An icon extracted may be the wrong size for the tray
icon_pixmap = icon_pixmap->scale_simple(icon_size, icon_size, icon_pixmap = icon_pixmap->scale_simple(icon_size, icon_size, Gdk::InterpType::INTERP_BILINEAR);
Gdk::InterpType::INTERP_BILINEAR);
image.set(icon_pixmap); image.set(icon_pixmap);
} }
} }
Glib::RefPtr<Gdk::Pixbuf> Glib::RefPtr<Gdk::Pixbuf> Item::getIconByName(const std::string& name, int request_size) {
waybar::modules::SNI::Item::getIconByName(std::string name, int request_size) {
int tmp_size = 0; int tmp_size = 0;
Glib::RefPtr<Gtk::IconTheme> icon_theme = Gtk::IconTheme::get_default();
icon_theme->rescan_if_needed(); icon_theme->rescan_if_needed();
auto sizes = icon_theme->get_icon_sizes(name.c_str()); auto sizes = icon_theme->get_icon_sizes(name.c_str());
for (auto const &size : sizes) { for (auto const& size : sizes) {
// -1 == scalable // -1 == scalable
if (size == request_size || size == -1) { if (size == request_size || size == -1) {
tmp_size = request_size; tmp_size = request_size;
break; break;
} else if (size < request_size || (size > tmp_size && tmp_size > 0)) { } else if (size < request_size) {
tmp_size = size; tmp_size = size;
} else if (size > tmp_size && tmp_size > 0) {
tmp_size = request_size;
break;
} }
} }
if (tmp_size == 0) { if (tmp_size == 0) {
tmp_size = request_size; tmp_size = request_size;
} }
return icon_theme->load_icon(name.c_str(), tmp_size, if (!icon_theme_path.empty() &&
Gtk::IconLookupFlags::ICON_LOOKUP_FORCE_SIZE); icon_theme->lookup_icon(
name.c_str(), tmp_size, Gtk::IconLookupFlags::ICON_LOOKUP_FORCE_SIZE)) {
return icon_theme->load_icon(
name.c_str(), tmp_size, Gtk::IconLookupFlags::ICON_LOOKUP_FORCE_SIZE);
}
Glib::RefPtr<Gtk::IconTheme> default_theme = Gtk::IconTheme::get_default();
default_theme->rescan_if_needed();
return default_theme->load_icon(
name.c_str(), tmp_size, Gtk::IconLookupFlags::ICON_LOOKUP_FORCE_SIZE);
} }
void waybar::modules::SNI::Item::onMenuDestroyed(Item *self) void Item::onMenuDestroyed(Item* self) {
{
self->gtk_menu = nullptr; self->gtk_menu = nullptr;
self->dbus_menu = nullptr; self->dbus_menu = nullptr;
} }
bool waybar::modules::SNI::Item::makeMenu(GdkEventButton *const &ev) bool Item::makeMenu(GdkEventButton* const& ev) {
{
if (gtk_menu == nullptr) { if (gtk_menu == nullptr) {
if (!menu.empty()) { if (!menu.empty()) {
dbus_menu = dbusmenu_gtkmenu_new(bus_name.data(), menu.data()); dbus_menu = dbusmenu_gtkmenu_new(bus_name.data(), menu.data());
@ -298,11 +304,9 @@ bool waybar::modules::SNI::Item::makeMenu(GdkEventButton *const &ev)
return false; return false;
} }
bool waybar::modules::SNI::Item::handleClick(GdkEventButton *const &ev) { bool Item::handleClick(GdkEventButton* const& ev) {
auto parameters = VariantContainerBase::create_tuple({ auto parameters = Glib::VariantContainerBase::create_tuple(
Variant<int>::create(ev->x), {Glib::Variant<int>::create(ev->x), Glib::Variant<int>::create(ev->y)});
Variant<int>::create(ev->y)
});
if ((ev->button == 1 && item_is_menu) || ev->button == 3) { if ((ev->button == 1 && item_is_menu) || ev->button == 3) {
if (!makeMenu(ev)) { if (!makeMenu(ev)) {
proxy_->call("ContextMenu", parameters); proxy_->call("ContextMenu", parameters);
@ -317,3 +321,5 @@ bool waybar::modules::SNI::Item::handleClick(GdkEventButton *const &ev) {
} }
return false; return false;
} }
} // namespace waybar::modules::SNI

View File

@ -1,43 +1,42 @@
#include "modules/sni/tray.hpp" #include "modules/sni/tray.hpp"
#include <iostream> #include <iostream>
waybar::modules::SNI::Tray::Tray(const std::string& id, const Bar& bar, namespace waybar::modules::SNI {
const Json::Value &config)
Tray::Tray(const std::string& id, const Bar& bar, const Json::Value& config)
: config_(config), : config_(config),
box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0), box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0),
watcher_(), host_(nb_hosts_, config, host_(nb_hosts_, config, std::bind(&Tray::onAdd, this, std::placeholders::_1),
std::bind(&Tray::onAdd, this, std::placeholders::_1), std::bind(&Tray::onRemove, this, std::placeholders::_1)) {
std::bind(&Tray::onRemove, this, std::placeholders::_1)) box_.set_name("tray");
{ if (!id.empty()) {
std::cout << "Tray is in beta, so there may be bugs or even be unusable." << std::endl; box_.get_style_context()->add_class(id);
}
if (config_["spacing"].isUInt()) { if (config_["spacing"].isUInt()) {
box_.set_spacing(config_["spacing"].asUInt()); box_.set_spacing(config_["spacing"].asUInt());
} }
nb_hosts_ += 1; nb_hosts_ += 1;
dp.emit();
} }
void waybar::modules::SNI::Tray::onAdd(std::unique_ptr<Item>& item) void Tray::onAdd(std::unique_ptr<Item>& item) {
{
box_.pack_start(item->event_box); box_.pack_start(item->event_box);
dp.emit(); dp.emit();
} }
void waybar::modules::SNI::Tray::onRemove(std::unique_ptr<Item>& item) void Tray::onRemove(std::unique_ptr<Item>& item) {
{
box_.remove(item->event_box); box_.remove(item->event_box);
dp.emit(); dp.emit();
} }
auto waybar::modules::SNI::Tray::update() -> void { auto Tray::update() -> void {
if (box_.get_children().size() > 0) { if (box_.get_children().empty()) {
box_.set_name("tray"); box_.hide();
box_.show_all();
} else { } else {
box_.set_name(""); box_.show_all();
} }
} }
waybar::modules::SNI::Tray::operator Gtk::Widget &() { Tray::operator Gtk::Widget&() { return box_; }
return box_;
} }

View File

@ -5,33 +5,49 @@
using namespace waybar::modules::SNI; using namespace waybar::modules::SNI;
Watcher::Watcher() Watcher::Watcher()
: bus_name_id_(Gio::DBus::own_name(Gio::DBus::BusType::BUS_TYPE_SESSION, : bus_name_id_(Gio::DBus::own_name(Gio::DBus::BusType::BUS_TYPE_SESSION,
"org.kde.StatusNotifierWatcher", sigc::mem_fun(*this, &Watcher::busAcquired), "org.kde.StatusNotifierWatcher",
Gio::DBus::SlotNameAcquired(), Gio::DBus::SlotNameLost(), sigc::mem_fun(*this, &Watcher::busAcquired),
Gio::DBus::BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT | Gio::DBus::BUS_NAME_OWNER_FLAGS_REPLACE)), Gio::DBus::SlotNameAcquired(), Gio::DBus::SlotNameLost(),
watcher_(sn_watcher_skeleton_new()) Gio::DBus::BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT |
{ Gio::DBus::BUS_NAME_OWNER_FLAGS_REPLACE)),
watcher_(sn_watcher_skeleton_new()) {}
Watcher::~Watcher() {
if (bus_name_id_ > 0) {
g_bus_unown_name(bus_name_id_);
bus_name_id_ = 0;
}
if (hosts_ != nullptr) {
g_slist_free_full(hosts_, gfWatchFree);
hosts_ = nullptr;
}
if (items_ != nullptr) {
g_slist_free_full(items_, gfWatchFree);
items_ = nullptr;
}
g_dbus_interface_skeleton_unexport(G_DBUS_INTERFACE_SKELETON(watcher_));
} }
void Watcher::busAcquired(const Glib::RefPtr<Gio::DBus::Connection>& conn, Glib::ustring name) void Watcher::busAcquired(const Glib::RefPtr<Gio::DBus::Connection>& conn, Glib::ustring name) {
{
GError* error = nullptr; GError* error = nullptr;
g_dbus_interface_skeleton_export(G_DBUS_INTERFACE_SKELETON(watcher_), g_dbus_interface_skeleton_export(
conn->gobj(), "/StatusNotifierWatcher", &error); G_DBUS_INTERFACE_SKELETON(watcher_), conn->gobj(), "/StatusNotifierWatcher", &error);
if (error != nullptr) { if (error != nullptr) {
std::cerr << error->message << std::endl; std::cerr << error->message << std::endl;
g_error_free(error); g_error_free(error);
return; return;
} }
g_signal_connect_swapped(watcher_, "handle-register-item", g_signal_connect_swapped(
G_CALLBACK(&Watcher::handleRegisterItem), this); watcher_, "handle-register-item", G_CALLBACK(&Watcher::handleRegisterItem), this);
g_signal_connect_swapped(watcher_, "handle-register-host", g_signal_connect_swapped(
G_CALLBACK(&Watcher::handleRegisterHost), this); watcher_, "handle-register-host", G_CALLBACK(&Watcher::handleRegisterHost), this);
} }
gboolean Watcher::handleRegisterHost(Watcher* obj, gboolean Watcher::handleRegisterHost(Watcher* obj, GDBusMethodInvocation* invocation,
GDBusMethodInvocation* invocation, const gchar* service) const gchar* service) {
{
const gchar* bus_name = service; const gchar* bus_name = service;
const gchar* object_path = "/StatusNotifierHost"; const gchar* object_path = "/StatusNotifierHost";
@ -40,15 +56,22 @@ gboolean Watcher::handleRegisterHost(Watcher* obj,
object_path = service; object_path = service;
} }
if (g_dbus_is_name(bus_name) == FALSE) { if (g_dbus_is_name(bus_name) == FALSE) {
g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR, g_dbus_method_invocation_return_error(invocation,
G_DBUS_ERROR_INVALID_ARGS, "D-Bus bus name '%s' is not valid", bus_name); G_DBUS_ERROR,
G_DBUS_ERROR_INVALID_ARGS,
"D-Bus bus name '%s' is not valid",
bus_name);
return TRUE; return TRUE;
} }
auto watch = gfWatchFind(obj->hosts_, bus_name, object_path); auto watch = gfWatchFind(obj->hosts_, bus_name, object_path);
if (watch != nullptr) { if (watch != nullptr) {
g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR, g_dbus_method_invocation_return_error(
G_DBUS_ERROR_INVALID_ARGS, "Status Notifier Host with bus name '%s' and object path '%s' is already registered", invocation,
bus_name, object_path); G_DBUS_ERROR,
G_DBUS_ERROR_INVALID_ARGS,
"Status Notifier Host with bus name '%s' and object path '%s' is already registered",
bus_name,
object_path);
return TRUE; return TRUE;
} }
watch = gfWatchNew(GF_WATCH_TYPE_HOST, service, bus_name, object_path, obj); watch = gfWatchNew(GF_WATCH_TYPE_HOST, service, bus_name, object_path, obj);
@ -61,9 +84,8 @@ gboolean Watcher::handleRegisterHost(Watcher* obj,
return TRUE; return TRUE;
} }
gboolean Watcher::handleRegisterItem(Watcher* obj, gboolean Watcher::handleRegisterItem(Watcher* obj, GDBusMethodInvocation* invocation,
GDBusMethodInvocation* invocation, const gchar* service) const gchar* service) {
{
const gchar* bus_name = service; const gchar* bus_name = service;
const gchar* object_path = "/StatusNotifierItem"; const gchar* object_path = "/StatusNotifierItem";
@ -72,14 +94,18 @@ gboolean Watcher::handleRegisterItem(Watcher* obj,
object_path = service; object_path = service;
} }
if (g_dbus_is_name(bus_name) == FALSE) { if (g_dbus_is_name(bus_name) == FALSE) {
g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR, g_dbus_method_invocation_return_error(invocation,
G_DBUS_ERROR_INVALID_ARGS, "D-Bus bus name '%s' is not valid", bus_name); G_DBUS_ERROR,
G_DBUS_ERROR_INVALID_ARGS,
"D-Bus bus name '%s' is not valid",
bus_name);
return TRUE; return TRUE;
} }
auto watch = gfWatchFind(obj->items_, bus_name, object_path); auto watch = gfWatchFind(obj->items_, bus_name, object_path);
if (watch != nullptr) { if (watch != nullptr) {
g_warning("Status Notifier Item with bus name '%s' and object path '%s' is already registered", g_warning("Status Notifier Item with bus name '%s' and object path '%s' is already registered",
bus_name, object_path); bus_name,
object_path);
sn_watcher_complete_register_item(obj->watcher_, invocation); sn_watcher_complete_register_item(obj->watcher_, invocation);
return TRUE; return TRUE;
} }
@ -94,40 +120,54 @@ gboolean Watcher::handleRegisterItem(Watcher* obj,
} }
Watcher::GfWatch* Watcher::gfWatchFind(GSList* list, const gchar* bus_name, Watcher::GfWatch* Watcher::gfWatchFind(GSList* list, const gchar* bus_name,
const gchar* object_path) const gchar* object_path) {
{ for (GSList* l = list; l != nullptr; l = g_slist_next(l)) {
for (GSList* l = list; l != nullptr; l = g_slist_next (l)) { auto watch = static_cast<GfWatch*>(l->data);
GfWatch* watch = static_cast<GfWatch*>(l->data); if (g_strcmp0(watch->bus_name, bus_name) == 0 &&
if (g_strcmp0 (watch->bus_name, bus_name) == 0 g_strcmp0(watch->object_path, object_path) == 0) {
&& g_strcmp0 (watch->object_path, object_path) == 0) {
return watch; return watch;
} }
} }
return nullptr; return nullptr;
} }
Watcher::GfWatch* Watcher::gfWatchNew(GfWatchType type, const gchar* service, void Watcher::gfWatchFree(gpointer data) {
const gchar* bus_name, const gchar* object_path, Watcher* watcher) auto watch = static_cast<GfWatch*>(data);
{
if (watch->watch_id > 0) {
g_bus_unwatch_name(watch->watch_id);
}
g_free(watch->service);
g_free(watch->bus_name);
g_free(watch->object_path);
g_free(watch);
}
Watcher::GfWatch* Watcher::gfWatchNew(GfWatchType type, const gchar* service, const gchar* bus_name,
const gchar* object_path, Watcher* watcher) {
GfWatch* watch = g_new0(GfWatch, 1); GfWatch* watch = g_new0(GfWatch, 1);
watch->type = type; watch->type = type;
watch->watcher = watcher; watch->watcher = watcher;
watch->service = g_strdup(service); watch->service = g_strdup(service);
watch->bus_name = g_strdup(bus_name); watch->bus_name = g_strdup(bus_name);
watch->object_path = g_strdup(object_path); watch->object_path = g_strdup(object_path);
watch->watch_id = g_bus_watch_name(G_BUS_TYPE_SESSION, bus_name, watch->watch_id = g_bus_watch_name(G_BUS_TYPE_SESSION,
G_BUS_NAME_WATCHER_FLAGS_NONE, nullptr, &Watcher::nameVanished, watch, bus_name,
nullptr); G_BUS_NAME_WATCHER_FLAGS_NONE,
nullptr,
&Watcher::nameVanished,
watch,
nullptr);
return watch; return watch;
} }
void Watcher::nameVanished(GDBusConnection* connection, const char* name, void Watcher::nameVanished(GDBusConnection* connection, const char* name, gpointer data) {
gpointer data) auto watch = static_cast<GfWatch*>(data);
{
auto watch = static_cast<GfWatch *>(data);
if (watch->type == GF_WATCH_TYPE_HOST) { if (watch->type == GF_WATCH_TYPE_HOST) {
watch->watcher->hosts_ = g_slist_remove(watch->watcher->hosts_, watch); watch->watcher->hosts_ = g_slist_remove(watch->watcher->hosts_, watch);
if (watch->watcher->hosts_ == nullptr) { if (watch->watcher->hosts_ == nullptr) {
sn_watcher_set_is_host_registered(watch->watcher->watcher_, FALSE); sn_watcher_set_is_host_registered(watch->watcher->watcher_, FALSE);
sn_watcher_emit_host_registered(watch->watcher->watcher_); sn_watcher_emit_host_registered(watch->watcher->watcher_);
} }
@ -140,17 +180,16 @@ void Watcher::nameVanished(GDBusConnection* connection, const char* name,
} }
} }
void Watcher::updateRegisteredItems(SnWatcher* obj) void Watcher::updateRegisteredItems(SnWatcher* obj) {
{
GVariantBuilder builder; GVariantBuilder builder;
g_variant_builder_init(&builder, G_VARIANT_TYPE("as")); g_variant_builder_init(&builder, G_VARIANT_TYPE("as"));
for (GSList* l = items_; l != nullptr; l = g_slist_next(l)) { for (GSList* l = items_; l != nullptr; l = g_slist_next(l)) {
GfWatch* watch = static_cast<GfWatch*>(l->data); auto watch = static_cast<GfWatch*>(l->data);
gchar* item = g_strdup_printf("%s%s", watch->bus_name, watch->object_path); gchar* item = g_strdup_printf("%s%s", watch->bus_name, watch->object_path);
g_variant_builder_add(&builder, "s", item); g_variant_builder_add(&builder, "s", item);
g_free(item); g_free(item);
} }
GVariant* variant = g_variant_builder_end(&builder); GVariant* variant = g_variant_builder_end(&builder);
const gchar** items = g_variant_get_strv(variant, nullptr); const gchar** items = g_variant_get_strv(variant, nullptr);
sn_watcher_set_registered_items(obj, items); sn_watcher_set_registered_items(obj, items);
g_variant_unref(variant); g_variant_unref(variant);

View File

@ -1,33 +1,38 @@
#include "modules/sway/ipc/client.hpp" #include "modules/sway/ipc/client.hpp"
#include <fcntl.h>
waybar::modules::sway::Ipc::Ipc() namespace waybar::modules::sway {
{
Ipc::Ipc() {
const std::string& socketPath = getSocketPath(); const std::string& socketPath = getSocketPath();
fd_ = open(socketPath); fd_ = open(socketPath);
fd_event_ = open(socketPath); fd_event_ = open(socketPath);
} }
waybar::modules::sway::Ipc::~Ipc() Ipc::~Ipc() {
{
// To fail the IPC header // To fail the IPC header
write(fd_, "close-sway-ipc", 14); write(fd_, "close-sway-ipc", 14);
write(fd_event_, "close-sway-ipc", 14); write(fd_event_, "close-sway-ipc", 14);
if (fd_ > 0) {
close(fd_); close(fd_);
close(fd_event_); fd_ = -1;
}
if (fd_event_ > 0) {
close(fd_event_);
fd_event_ = -1;
}
} }
const std::string waybar::modules::sway::Ipc::getSocketPath() const const std::string Ipc::getSocketPath() const {
{ const char* env = getenv("SWAYSOCK");
const char *env = getenv("SWAYSOCK");
if (env != nullptr) { if (env != nullptr) {
return std::string(env); return std::string(env);
} }
std::string str; std::string str;
{ {
std::string str_buf; std::string str_buf;
FILE* in; FILE* in;
char buf[512] = { 0 }; char buf[512] = {0};
if ((in = popen("sway --get-socketpath 2>/dev/null", "r")) == nullptr) { if ((in = popen("sway --get-socketpath 2>/dev/null", "r")) == nullptr) {
throw std::runtime_error("Failed to get socket path"); throw std::runtime_error("Failed to get socket path");
} }
@ -36,6 +41,9 @@ const std::string waybar::modules::sway::Ipc::getSocketPath() const
} }
pclose(in); pclose(in);
str = str_buf; str = str_buf;
if (str.empty()) {
throw std::runtime_error("Socket path is empty");
}
} }
if (str.back() == '\n') { if (str.back() == '\n') {
str.pop_back(); str.pop_back();
@ -43,39 +51,41 @@ const std::string waybar::modules::sway::Ipc::getSocketPath() const
return str; return str;
} }
int waybar::modules::sway::Ipc::open(const std::string& socketPath) const int Ipc::open(const std::string& socketPath) const {
{ int32_t fd = socket(AF_UNIX, SOCK_STREAM, 0);
struct sockaddr_un addr = {0}; if (fd == -1) {
int fd = -1;
if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
throw std::runtime_error("Unable to open Unix socket"); throw std::runtime_error("Unable to open Unix socket");
} }
(void)fcntl(fd, F_SETFD, FD_CLOEXEC);
struct sockaddr_un addr;
memset(&addr, 0, sizeof(struct sockaddr_un));
addr.sun_family = AF_UNIX; addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, socketPath.c_str(), sizeof(addr.sun_path) - 1); strncpy(addr.sun_path, socketPath.c_str(), sizeof(addr.sun_path) - 1);
addr.sun_path[sizeof(addr.sun_path) - 1] = 0; addr.sun_path[sizeof(addr.sun_path) - 1] = 0;
int l = sizeof(struct sockaddr_un); int l = sizeof(struct sockaddr_un);
if (::connect(fd, reinterpret_cast<struct sockaddr *>(&addr), l) == -1) { if (::connect(fd, reinterpret_cast<struct sockaddr*>(&addr), l) == -1) {
throw std::runtime_error("Unable to connect to Sway"); throw std::runtime_error("Unable to connect to Sway");
} }
return fd; return fd;
} }
struct waybar::modules::sway::Ipc::ipc_response struct Ipc::ipc_response Ipc::recv(int fd) {
waybar::modules::sway::Ipc::recv(int fd) const
{
std::string header; std::string header;
header.resize(ipc_header_size_); header.resize(ipc_header_size_);
auto data32 = reinterpret_cast<uint32_t *>(header.data() + ipc_magic_.size()); auto data32 = reinterpret_cast<uint32_t*>(header.data() + ipc_magic_.size());
size_t total = 0; size_t total = 0;
while (total < ipc_header_size_) { while (total < ipc_header_size_) {
auto res = ::recv(fd, header.data() + total, ipc_header_size_ - total, 0); auto res = ::recv(fd, header.data() + total, ipc_header_size_ - total, 0);
if (fd_event_ == -1 || fd_ == -1) {
// IPC is closed so just return an empty response
return {0, 0, ""};
}
if (res <= 0) { if (res <= 0) {
throw std::runtime_error("Unable to receive IPC header"); throw std::runtime_error("Unable to receive IPC header");
} }
total += res; total += res;
} }
auto magic = std::string(header.data(), header.data() + ipc_magic_.size()); auto magic = std::string(header.data(), header.data() + ipc_magic_.size());
if (magic != ipc_magic_) { if (magic != ipc_magic_) {
throw std::runtime_error("Invalid IPC magic"); throw std::runtime_error("Invalid IPC magic");
@ -87,20 +97,22 @@ struct waybar::modules::sway::Ipc::ipc_response
while (total < data32[0]) { while (total < data32[0]) {
auto res = ::recv(fd, payload.data() + total, data32[0] - total, 0); auto res = ::recv(fd, payload.data() + total, data32[0] - total, 0);
if (res < 0) { if (res < 0) {
if (errno == EINTR || errno == EAGAIN) {
continue;
}
throw std::runtime_error("Unable to receive IPC payload"); throw std::runtime_error("Unable to receive IPC payload");
} }
total += res; total += res;
} }
return { data32[0], data32[1], &payload.front() }; std::lock_guard<std::mutex> lock(mutex_parser_);
auto parsed = parser_.parse(&payload.front());
return {data32[0], data32[1], parsed};
} }
struct waybar::modules::sway::Ipc::ipc_response struct Ipc::ipc_response Ipc::send(int fd, uint32_t type, const std::string& payload) {
waybar::modules::sway::Ipc::send(int fd, uint32_t type,
const std::string& payload) const
{
std::string header; std::string header;
header.resize(ipc_header_size_); header.resize(ipc_header_size_);
auto data32 = reinterpret_cast<uint32_t *>(header.data() + ipc_magic_.size()); auto data32 = reinterpret_cast<uint32_t*>(header.data() + ipc_magic_.size());
memcpy(header.data(), ipc_magic_.c_str(), ipc_magic_.size()); memcpy(header.data(), ipc_magic_.c_str(), ipc_magic_.size());
data32[0] = payload.size(); data32[0] = payload.size();
data32[1] = type; data32[1] = type;
@ -111,26 +123,27 @@ struct waybar::modules::sway::Ipc::ipc_response
if (::send(fd, payload.c_str(), payload.size(), 0) == -1) { if (::send(fd, payload.c_str(), payload.size(), 0) == -1) {
throw std::runtime_error("Unable to send IPC payload"); throw std::runtime_error("Unable to send IPC payload");
} }
return recv(fd); return Ipc::recv(fd);
} }
struct waybar::modules::sway::Ipc::ipc_response void Ipc::sendCmd(uint32_t type, const std::string& payload) {
waybar::modules::sway::Ipc::sendCmd(uint32_t type, std::lock_guard<std::mutex> lock(mutex_);
const std::string& payload) const const auto res = Ipc::send(fd_, type, payload);
{ signal_cmd.emit(res);
return send(fd_, type, payload);
} }
void waybar::modules::sway::Ipc::subscribe(const std::string& payload) const void Ipc::subscribe(const std::string& payload) {
{ std::lock_guard<std::mutex> lock(mutex_event_);
auto res = send(fd_event_, IPC_SUBSCRIBE, payload); auto res = Ipc::send(fd_event_, IPC_SUBSCRIBE, payload);
if (res.payload != "{\"success\": true}") { if (!res.payload["success"].asBool()) {
throw std::runtime_error("Unable to subscribe ipc event"); throw std::runtime_error("Unable to subscribe ipc event");
} }
} }
struct waybar::modules::sway::Ipc::ipc_response void Ipc::handleEvent() {
waybar::modules::sway::Ipc::handleEvent() const std::lock_guard<std::mutex> lock(mutex_event_);
{ const auto res = Ipc::recv(fd_event_);
return recv(fd_event_); signal_event.emit(res);
} }
} // namespace waybar::modules::sway

View File

@ -1,38 +1,40 @@
#include "modules/sway/mode.hpp" #include "modules/sway/mode.hpp"
waybar::modules::sway::Mode::Mode(const std::string& id, const Bar& bar, const Json::Value& config) namespace waybar::modules::sway {
: ALabel(config, "{}"), bar_(bar)
{ Mode::Mode(const std::string& id, const Bar& bar, const Json::Value& config)
: ALabel(config, "{}"), bar_(bar) {
label_.set_name("mode"); label_.set_name("mode");
if (!id.empty()) { if (!id.empty()) {
label_.get_style_context()->add_class(id); label_.get_style_context()->add_class(id);
} }
ipc_.subscribe("[ \"mode\" ]"); ipc_.subscribe(R"(["mode"])");
ipc_.signal_event.connect(sigc::mem_fun(*this, &Mode::onEvent));
// Launch worker // Launch worker
worker(); worker();
dp.emit(); dp.emit();
} }
void waybar::modules::sway::Mode::worker() void Mode::onEvent(const struct Ipc::ipc_response &res) {
{ if (res.payload["change"] != "default") {
mode_ = res.payload["change"].asString();
} else {
mode_.clear();
}
dp.emit();
}
void Mode::worker() {
thread_ = [this] { thread_ = [this] {
try { try {
auto res = ipc_.handleEvent(); ipc_.handleEvent();
auto parsed = parser_.parse(res.payload);
if (parsed["change"] != "default") {
mode_ = parsed["change"].asString();
} else {
mode_.clear();
}
dp.emit();
} catch (const std::exception& e) { } catch (const std::exception& e) {
std::cerr << "Mode: " << e.what() << std::endl; std::cerr << "Mode: " << e.what() << std::endl;
} }
}; };
} }
auto waybar::modules::sway::Mode::update() -> void auto Mode::update() -> void {
{
if (mode_.empty()) { if (mode_.empty()) {
event_box_.hide(); event_box_.hide();
} else { } else {
@ -43,3 +45,5 @@ auto waybar::modules::sway::Mode::update() -> void
event_box_.show(); event_box_.show();
} }
} }
} // namespace waybar::modules::sway

View File

@ -1,8 +1,9 @@
#include "modules/sway/window.hpp" #include "modules/sway/window.hpp"
waybar::modules::sway::Window::Window(const std::string& id, const Bar &bar, const Json::Value& config) namespace waybar::modules::sway {
: ALabel(config, "{}"), bar_(bar), windowId_(-1)
{ Window::Window(const std::string& id, const Bar& bar, const Json::Value& config)
: ALabel(config, "{}"), bar_(bar), windowId_(-1) {
label_.set_name("window"); label_.set_name("window");
if (!id.empty()) { if (!id.empty()) {
label_.get_style_context()->add_class(id); label_.get_style_context()->add_class(id);
@ -11,73 +12,104 @@ waybar::modules::sway::Window::Window(const std::string& id, const Bar &bar, con
label_.set_hexpand(true); label_.set_hexpand(true);
label_.set_ellipsize(Pango::EllipsizeMode::ELLIPSIZE_END); label_.set_ellipsize(Pango::EllipsizeMode::ELLIPSIZE_END);
} }
ipc_.subscribe("[\"window\",\"workspace\"]"); ipc_.subscribe(R"(["window","workspace"])");
getFocusedWindow(); ipc_.signal_event.connect(sigc::mem_fun(*this, &Window::onEvent));
ipc_.signal_cmd.connect(sigc::mem_fun(*this, &Window::onCmd));
// Get Initial focused window
getTree();
// Launch worker // Launch worker
worker(); worker();
} }
void waybar::modules::sway::Window::worker() void Window::onEvent(const struct Ipc::ipc_response& res) {
{ auto data = res.payload;
// Check for waybar prevents flicker when hovering window module
if ((data["change"] == "focus" || data["change"] == "title") &&
data["container"]["focused"].asBool() && data["container"]["name"].asString() != "waybar") {
window_ = Glib::Markup::escape_text(data["container"]["name"].asString());
windowId_ = data["container"]["id"].asInt();
dp.emit();
getTree();
} else if ((data["change"] == "close" && data["container"]["focused"].asBool() &&
windowId_ == data["container"]["id"].asInt()) ||
(data["change"] == "focus" && data["current"]["focus"].isArray() &&
data["current"]["focus"].empty())) {
window_.clear();
windowId_ = -1;
dp.emit();
getTree();
}
}
void Window::onCmd(const struct Ipc::ipc_response& res) {
auto [nb, id, name, app_id] = getFocusedNode(res.payload);
if (!app_id_.empty()) {
bar_.window.get_style_context()->remove_class(app_id_);
}
if (nb == 0) {
bar_.window.get_style_context()->add_class("empty");
} else if (nb == 1) {
bar_.window.get_style_context()->add_class("solo");
if (!app_id.empty()) {
bar_.window.get_style_context()->add_class(app_id);
}
} else {
bar_.window.get_style_context()->remove_class("solo");
bar_.window.get_style_context()->remove_class("empty");
}
app_id_ = app_id;
if (windowId_ != id || window_ != name) {
windowId_ = id;
window_ = name;
dp.emit();
}
}
void Window::worker() {
thread_ = [this] { thread_ = [this] {
try { try {
auto res = ipc_.handleEvent(); ipc_.handleEvent();
auto parsed = parser_.parse(res.payload);
// Check for waybar prevents flicker when hovering window module
if ((parsed["change"] == "focus" || parsed["change"] == "title")
&& parsed["container"]["focused"].asBool()
&& parsed["container"]["name"].asString() != "waybar") {
window_ = Glib::Markup::escape_text(parsed["container"]["name"].asString());
windowId_ = parsed["container"]["id"].asInt();
dp.emit();
} else if ((parsed["change"] == "close"
&& parsed["container"]["focused"].asBool()
&& windowId_ == parsed["container"]["id"].asInt())
|| (parsed["change"] == "focus" && parsed["current"]["focus"].isArray()
&& parsed["current"]["focus"].empty())) {
window_.clear();
windowId_ = -1;
dp.emit();
}
} catch (const std::exception& e) { } catch (const std::exception& e) {
std::cerr << "Window: " << e.what() << std::endl; std::cerr << "Window: " << e.what() << std::endl;
} }
}; };
} }
auto waybar::modules::sway::Window::update() -> void auto Window::update() -> void {
{
label_.set_markup(fmt::format(format_, window_)); label_.set_markup(fmt::format(format_, window_));
if (tooltipEnabled()) { if (tooltipEnabled()) {
label_.set_tooltip_text(window_); label_.set_tooltip_text(window_);
} }
} }
std::tuple<int, std::string> waybar::modules::sway::Window::getFocusedNode( std::tuple<std::size_t, int, std::string, std::string> Window::getFocusedNode(
Json::Value nodes) const Json::Value& nodes) {
{ for (auto const& node : nodes["nodes"]) {
for (auto const& node : nodes) {
if (node["focused"].asBool() && node["type"] == "con") { if (node["focused"].asBool() && node["type"] == "con") {
return { node["id"].asInt(), node["name"].asString() }; if ((!config_["all-outputs"].asBool() && nodes["output"] == bar_.output->name) ||
config_["all-outputs"].asBool()) {
auto app_id = node["app_id"].isString() ? node["app_id"].asString()
: node["window_properties"]["instance"].asString();
return {nodes["nodes"].size(),
node["id"].asInt(),
Glib::Markup::escape_text(node["name"].asString()),
app_id};
}
} }
auto [id, name] = getFocusedNode(node["nodes"]); auto [nb, id, name, app_id] = getFocusedNode(node);
if (id > -1 && !name.empty()) { if (id > -1 && !name.empty()) {
return { id, name }; return {nb, id, name, app_id};
} }
} }
return { -1, std::string() }; return {0, -1, "", ""};
} }
void waybar::modules::sway::Window::getFocusedWindow() void Window::getTree() {
{
try { try {
auto res = ipc_.sendCmd(IPC_GET_TREE); ipc_.sendCmd(IPC_GET_TREE);
auto parsed = parser_.parse(res.payload); } catch (const std::exception& e) {
auto [id, name] = getFocusedNode(parsed["nodes"]);
windowId_ = id;
window_ = name;
Glib::signal_idle().connect_once(sigc::mem_fun(*this, &Window::update));
} catch (const std::exception &e) {
std::cerr << e.what() << std::endl; std::cerr << e.what() << std::endl;
} }
} }
} // namespace waybar::modules::sway

View File

@ -1,147 +1,141 @@
#include "modules/sway/workspaces.hpp" #include "modules/sway/workspaces.hpp"
waybar::modules::sway::Workspaces::Workspaces(const std::string& id, const Bar& bar, namespace waybar::modules::sway {
const Json::Value& config)
: bar_(bar), config_(config), Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value &config)
box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0), : bar_(bar),
scrolling_(false) config_(config),
{ box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0),
scrolling_(false) {
box_.set_name("workspaces"); box_.set_name("workspaces");
if (!id.empty()) { if (!id.empty()) {
box_.get_style_context()->add_class(id); box_.get_style_context()->add_class(id);
} }
ipc_.subscribe("[ \"workspace\" ]"); ipc_.subscribe(R"(["workspace"])");
ipc_.signal_event.connect(sigc::mem_fun(*this, &Workspaces::onEvent));
ipc_.signal_cmd.connect(sigc::mem_fun(*this, &Workspaces::onCmd));
ipc_.sendCmd(IPC_GET_WORKSPACES);
// Launch worker // Launch worker
worker(); worker();
} }
void waybar::modules::sway::Workspaces::worker() void Workspaces::onEvent(const struct Ipc::ipc_response &res) { ipc_.sendCmd(IPC_GET_WORKSPACES); }
{
void Workspaces::onCmd(const struct Ipc::ipc_response &res) {
if (res.type == IPC_GET_WORKSPACES) {
if (res.payload.isArray()) {
std::lock_guard<std::mutex> lock(mutex_);
workspaces_.clear();
std::copy_if(res.payload.begin(),
res.payload.end(),
std::back_inserter(workspaces_),
[&](const auto &workspace) {
return !config_["all-outputs"].asBool()
? workspace["output"].asString() == bar_.output->name
: true;
});
dp.emit();
}
} else {
if (scrolling_) {
scrolling_ = false;
}
}
}
void Workspaces::worker() {
thread_ = [this] { thread_ = [this] {
try { try {
if (!workspaces_.empty()) { ipc_.handleEvent();
ipc_.handleEvent(); } catch (const std::exception &e) {
}
{
std::lock_guard<std::mutex> lock(mutex_);
auto res = ipc_.sendCmd(IPC_GET_WORKSPACES);
if (thread_.isRunning()) {
workspaces_ = parser_.parse(res.payload);
}
}
dp.emit();
} catch (const std::exception& e) {
std::cerr << "Workspaces: " << e.what() << std::endl; std::cerr << "Workspaces: " << e.what() << std::endl;
} }
}; };
} }
auto waybar::modules::sway::Workspaces::update() -> void bool Workspaces::filterButtons() {
{
bool needReorder = false; bool needReorder = false;
std::lock_guard<std::mutex> lock(mutex_);
for (auto it = buttons_.begin(); it != buttons_.end();) { for (auto it = buttons_.begin(); it != buttons_.end();) {
auto ws = std::find_if(workspaces_.begin(), workspaces_.end(), auto ws = std::find_if(workspaces_.begin(), workspaces_.end(), [it](const auto &node) {
[it](auto node) -> bool { return node["name"].asString() == it->first; }); return node["name"].asString() == it->first;
});
if (ws == workspaces_.end() || if (ws == workspaces_.end() ||
(!config_["all-outputs"].asBool() && (*ws)["output"].asString() != bar_.output_name)) { (!config_["all-outputs"].asBool() && (*ws)["output"].asString() != bar_.output->name)) {
it = buttons_.erase(it); it = buttons_.erase(it);
needReorder = true; needReorder = true;
} else { } else {
++it; ++it;
} }
} }
for (auto const& node : workspaces_) { return needReorder;
if (!config_["all-outputs"].asBool() }
&& bar_.output_name != node["output"].asString()) {
continue; auto Workspaces::update() -> void {
} std::lock_guard<std::mutex> lock(mutex_);
auto it = buttons_.find(node["name"].asString()); bool needReorder = filterButtons();
if (it == buttons_.end()) { for (auto it = workspaces_.begin(); it != workspaces_.end(); ++it) {
addWorkspace(node); auto bit = buttons_.find((*it)["name"].asString());
if (bit == buttons_.end()) {
needReorder = true; 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, getWorkspaceIndex(node["name"].asString()));
}
auto icon = getIcon(node["name"].asString(), node);
if (config_["format"].isString()) {
auto format = config_["format"].asString();
button.set_label(fmt::format(format, fmt::arg("icon", icon),
fmt::arg("name", trimWorkspaceName(node["name"].asString())),
fmt::arg("index", node["num"].asString())));
} else {
button.set_label(icon);
}
onButtonReady(node, button);
} }
} auto &button = bit == buttons_.end() ? addButton(*it) : bit->second;
if (scrolling_) { if ((*it)["focused"].asBool()) {
scrolling_ = false; button.get_style_context()->add_class("focused");
} else {
button.get_style_context()->remove_class("focused");
}
if ((*it)["visible"].asBool()) {
button.get_style_context()->add_class("visible");
} else {
button.get_style_context()->remove_class("visible");
}
if ((*it)["urgent"].asBool()) {
button.get_style_context()->add_class("urgent");
} else {
button.get_style_context()->remove_class("urgent");
}
if (needReorder) {
box_.reorder_child(button, it - workspaces_.begin());
}
std::string output = getIcon((*it)["name"].asString(), *it);
if (config_["format"].isString()) {
auto format = config_["format"].asString();
output = fmt::format(format,
fmt::arg("icon", output),
fmt::arg("name", trimWorkspaceName((*it)["name"].asString())),
fmt::arg("index", (*it)["num"].asString()));
}
if (!config_["disable-markup"].asBool()) {
static_cast<Gtk::Label *>(button.get_children()[0])->set_markup(output);
} else {
button.set_label(output);
}
onButtonReady(*it, button);
} }
} }
void waybar::modules::sway::Workspaces::addWorkspace(const Json::Value &node) Gtk::Button &Workspaces::addButton(const Json::Value &node) {
{ auto pair = buttons_.emplace(node["name"].asString(), node["name"].asString());
auto icon = getIcon(node["name"].asString(), node);
auto format = config_["format"].isString()
? fmt::format(config_["format"].asString(), fmt::arg("icon", icon),
fmt::arg("name", trimWorkspaceName(node["name"].asString())),
fmt::arg("index", node["num"].asString()))
: icon;
auto pair = buttons_.emplace(node["name"].asString(), format);
auto &button = pair.first->second; auto &button = pair.first->second;
box_.pack_start(button, false, false, 0); box_.pack_start(button, false, false, 0);
button.set_relief(Gtk::RELIEF_NONE); button.set_relief(Gtk::RELIEF_NONE);
button.signal_clicked().connect([this, pair] { button.signal_clicked().connect([this, pair] {
try { try {
std::lock_guard<std::mutex> lock(mutex_); ipc_.sendCmd(IPC_COMMAND, fmt::format("workspace \"{}\"", pair.first->first));
auto cmd = fmt::format("workspace \"{}\"", pair.first->first); } catch (const std::exception &e) {
ipc_.sendCmd(IPC_COMMAND, cmd);
} catch (const std::exception& e) {
std::cerr << e.what() << std::endl; std::cerr << e.what() << std::endl;
} }
}); });
if (!config_["disable-scroll"].asBool()) { if (!config_["disable-scroll"].asBool()) {
button.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK); button.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK);
button.signal_scroll_event() button.signal_scroll_event().connect(sigc::mem_fun(*this, &Workspaces::handleScroll));
.connect(sigc::mem_fun(*this, &Workspaces::handleScroll));
} }
box_.reorder_child(button, getWorkspaceIndex(node["name"].asString())); return button;
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");
}
onButtonReady(node, button);
} }
std::string waybar::modules::sway::Workspaces::getIcon(const std::string &name, std::string Workspaces::getIcon(const std::string &name, const Json::Value &node) {
const Json::Value &node) std::vector<std::string> keys = {name, "urgent", "focused", "visible", "default"};
{ for (auto const &key : keys) {
std::vector<std::string> keys = { name, "urgent", "focused", "visible", "default" };
for (auto const& key : keys) {
if (key == "focused" || key == "visible" || key == "urgent") { if (key == "focused" || key == "visible" || key == "urgent") {
if (config_["format-icons"][key].isString() && node[key].asBool()) { if (config_["format-icons"][key].isString() && node[key].asBool()) {
return config_["format-icons"][key].asString(); return config_["format-icons"][key].asString();
@ -153,112 +147,87 @@ std::string waybar::modules::sway::Workspaces::getIcon(const std::string &name,
return name; return name;
} }
bool waybar::modules::sway::Workspaces::handleScroll(GdkEventScroll *e) bool Workspaces::handleScroll(GdkEventScroll *e) {
{
// Avoid concurrent scroll event // Avoid concurrent scroll event
if (scrolling_) { if (scrolling_) {
return false; return false;
} }
std::lock_guard<std::mutex> lock(mutex_);
uint8_t idx;
scrolling_ = true;
for (idx = 0; idx < workspaces_.size(); idx += 1) {
if (workspaces_[idx]["focused"].asBool()) {
break;
}
}
if (idx == workspaces_.size()) {
scrolling_ = false;
return false;
}
std::string name; std::string name;
if (e->direction == GDK_SCROLL_UP) { scrolling_ = true;
name = getCycleWorkspace(idx, true); {
} std::lock_guard<std::mutex> lock(mutex_);
if (e->direction == GDK_SCROLL_DOWN) { auto it = std::find_if(workspaces_.begin(), workspaces_.end(), [](const auto &workspace) {
name = getCycleWorkspace(idx, false); return workspace["focused"].asBool();
} });
if (e->direction == GDK_SCROLL_SMOOTH) { if (it == workspaces_.end()) {
gdouble delta_x, delta_y; scrolling_ = false;
gdk_event_get_scroll_deltas(reinterpret_cast<const GdkEvent *>(e), return false;
&delta_x, &delta_y); }
if (delta_y < 0) { switch (e->direction) {
name = getCycleWorkspace(idx, true); case GDK_SCROLL_DOWN:
} else if (delta_y > 0) { case GDK_SCROLL_RIGHT:
name = getCycleWorkspace(idx, false); name = getCycleWorkspace(it, false);
break;
case GDK_SCROLL_UP:
case GDK_SCROLL_LEFT:
name = getCycleWorkspace(it, true);
break;
case 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) {
name = getCycleWorkspace(it, true);
} else if (delta_y > 0) {
name = getCycleWorkspace(it, false);
}
break;
default:
break;
}
if (name.empty() || name == (*it)["name"].asString()) {
scrolling_ = false;
return false;
} }
}
if (name.empty() || name == workspaces_[idx]["name"].asString()) {
scrolling_ = false;
return false;
} }
ipc_.sendCmd(IPC_COMMAND, fmt::format("workspace \"{}\"", name)); ipc_.sendCmd(IPC_COMMAND, fmt::format("workspace \"{}\"", name));
std::this_thread::sleep_for(std::chrono::milliseconds(150));
return true; return true;
} }
const std::string waybar::modules::sway::Workspaces::getCycleWorkspace( const std::string Workspaces::getCycleWorkspace(std::vector<Json::Value>::iterator it,
uint8_t focused_workspace, bool prev) const bool prev) const {
{ if (prev && it == workspaces_.begin()) {
auto inc = prev ? -1 : 1; return (*(--workspaces_.end()))["name"].asString();
int size = workspaces_.size();
uint8_t idx = 0;
for (int i = focused_workspace; i < size && i >= 0; i += inc) {
bool same_output = (workspaces_[i]["output"].asString() == bar_.output_name
&& !config_["all-outputs"].asBool()) || config_["all-outputs"].asBool();
bool same_name =
workspaces_[i]["name"].asString() == workspaces_[focused_workspace]["name"].asString();
if (same_output && !same_name) {
return workspaces_[i]["name"].asString();
}
if (prev && i - 1 < 0) {
i = size;
} else if (!prev && i + 1 >= size) {
i = -1;
} else if (idx >= workspaces_.size()) {
return "";
}
idx += 1;
} }
return ""; if (prev && it != workspaces_.begin())
--it;
else if (!prev && it != workspaces_.end())
++it;
if (!prev && it == workspaces_.end()) {
return (*(++workspaces_.begin()))["name"].asString();
}
return (*it)["name"].asString();
} }
uint16_t waybar::modules::sway::Workspaces::getWorkspaceIndex(const std::string &name) const std::string Workspaces::trimWorkspaceName(std::string name) {
{ std::size_t found = name.find(':');
uint16_t idx = 0; if (found != std::string::npos) {
for (const auto &workspace : workspaces_) { return name.substr(found + 1);
if (workspace["name"].asString() == name) {
return idx;
}
if (!(!config_["all-outputs"].asBool() && workspace["output"].asString() != bar_.output_name)) {
idx += 1;
}
}
return workspaces_.size();
}
std::string waybar::modules::sway::Workspaces::trimWorkspaceName(std::string name)
{
std::size_t found = name.find(":");
if (found!=std::string::npos) {
return name.substr(found+1);
} }
return name; return name;
} }
void waybar::modules::sway::Workspaces::onButtonReady(const Json::Value& node, Gtk::Button& button) void Workspaces::onButtonReady(const Json::Value &node, Gtk::Button &button) {
{ if (config_["current-only"].asBool()) {
if (config_["current-only"].asBool()) { if (node["focused"].asBool()) {
if (node["focused"].asBool()) { button.show();
button.show(); } else {
} else { button.hide();
button.hide(); }
} } else {
} else { button.show();
button.show(); }
}
} }
waybar::modules::sway::Workspaces::operator Gtk::Widget &() { Workspaces::operator Gtk::Widget &() { return box_; }
return box_;
} } // namespace waybar::modules::sway

View File

@ -1,16 +1,20 @@
#include "modules/temperature.hpp" #include "modules/temperature.hpp"
waybar::modules::Temperature::Temperature(const std::string& id, const Json::Value& config) waybar::modules::Temperature::Temperature(const std::string& id, const Json::Value& config)
: ALabel(config, "{temperatureC}°C", 10) : ALabel(config, "{temperatureC}°C", 10) {
{
if (config_["hwmon-path"].isString()) { if (config_["hwmon-path"].isString()) {
file_path_ = config_["hwmon-path"].asString(); file_path_ = config_["hwmon-path"].asString();
} else { } else {
auto zone = auto zone = config_["thermal-zone"].isInt() ? config_["thermal-zone"].asInt() : 0;
config_["thermal-zone"].isInt() ? config_["thermal-zone"].asInt() : 0;
file_path_ = fmt::format("/sys/class/thermal/thermal_zone{}/temp", zone); file_path_ = fmt::format("/sys/class/thermal/thermal_zone{}/temp", zone);
} }
#ifdef FILESYSTEM_EXPERIMENTAL
if (!std::experimental::filesystem::exists(file_path_)) {
#else
if (!std::filesystem::exists(file_path_)) {
#endif
throw std::runtime_error("Can't open " + file_path_);
}
label_.set_name("temperature"); label_.set_name("temperature");
if (!id.empty()) { if (!id.empty()) {
label_.get_style_context()->add_class(id); label_.get_style_context()->add_class(id);
@ -21,24 +25,21 @@ waybar::modules::Temperature::Temperature(const std::string& id, const Json::Val
}; };
} }
auto waybar::modules::Temperature::update() -> void auto waybar::modules::Temperature::update() -> void {
{
auto [temperature_c, temperature_f] = getTemperature(); auto [temperature_c, temperature_f] = getTemperature();
auto critical = isCritical(temperature_c); auto critical = isCritical(temperature_c);
auto format = format_; auto format = format_;
if (critical) { if (critical) {
format = format = config_["format-critical"].isString() ? config_["format-critical"].asString() : format;
config_["format-critical"].isString() ? config_["format-critical"].asString() : format;
label_.get_style_context()->add_class("critical"); label_.get_style_context()->add_class("critical");
} else { } else {
label_.get_style_context()->remove_class("critical"); label_.get_style_context()->remove_class("critical");
} }
label_.set_markup(fmt::format(format, label_.set_markup(fmt::format(
fmt::arg("temperatureC", temperature_c), fmt::arg("temperatureF", temperature_f))); format, fmt::arg("temperatureC", temperature_c), fmt::arg("temperatureF", temperature_f)));
} }
std::tuple<uint16_t, uint16_t> waybar::modules::Temperature::getTemperature() std::tuple<uint16_t, uint16_t> waybar::modules::Temperature::getTemperature() {
{
std::ifstream temp(file_path_); std::ifstream temp(file_path_);
if (!temp.is_open()) { if (!temp.is_open()) {
throw std::runtime_error("Can't open " + file_path_); throw std::runtime_error("Can't open " + file_path_);
@ -48,13 +49,13 @@ std::tuple<uint16_t, uint16_t> waybar::modules::Temperature::getTemperature()
getline(temp, line); getline(temp, line);
} }
temp.close(); temp.close();
auto temperature_c = std::strtol(line.c_str(), nullptr, 10) / 1000.0; auto temperature_c = std::strtol(line.c_str(), nullptr, 10) / 1000.0;
auto temperature_f = temperature_c * 1.8 + 32; auto temperature_f = temperature_c * 1.8 + 32;
std::tuple<uint16_t, uint16_t> temperatures(std::round(temperature_c), std::round(temperature_f)); std::tuple<uint16_t, uint16_t> temperatures(std::round(temperature_c), std::round(temperature_f));
return temperatures; return temperatures;
} }
bool waybar::modules::Temperature::isCritical(uint16_t temperature_c) bool waybar::modules::Temperature::isCritical(uint16_t temperature_c) {
{ return config_["critical-threshold"].isInt() &&
return config_["critical-threshold"].isInt() && temperature_c >= config_["critical-threshold"].asInt(); temperature_c >= config_["critical-threshold"].asInt();
} }

View File

@ -1,10 +1,10 @@
[wrap-file] [wrap-file]
directory = fmt-5.2.1 directory = fmt-5.3.0
source_url = https://github.com/fmtlib/fmt/archive/5.2.1.tar.gz source_url = https://github.com/fmtlib/fmt/archive/5.3.0.tar.gz
source_filename = fmt-5.2.1.tar.gz source_filename = fmt-5.3.0.tar.gz
source_hash = 3c812a18e9f72a88631ab4732a97ce9ef5bcbefb3235e9fd465f059ba204359b source_hash = defa24a9af4c622a7134076602070b45721a43c51598c8456ec6f2c4dbb51c89
patch_url = https://wrapdb.mesonbuild.com/v1/projects/fmt/5.2.1/1/get_zip patch_url = https://wrapdb.mesonbuild.com/v1/projects/fmt/5.3.0/1/get_zip
patch_filename = fmt-5.2.1-1-wrap.zip patch_filename = fmt-5.3.0-1-wrap.zip
patch_hash = 7add08bb4e168c0809e88c6aa64ed5c3494b74deb6be12a93e1e4dc5bb3a1fc1 patch_hash = 18f21a3b8833949c35d4ac88a7059577d5fa24b98786e4b1b2d3d81bb811440f