Compare commits

..

86 Commits
0.8.0 ... 0.9.0

Author SHA1 Message Date
35d806b801 chore: 0.9.0 2019-12-28 12:35:09 +01:00
220028051f chore: auto gtk-layer-shell option 2019-12-28 12:13:33 +01:00
7f73a8cd45 Merge pull request #441 from alebastr/gtk-layer-shell
Use gtk-layer-shell library for correct positioning of popups
2019-12-28 11:56:46 +01:00
dde700f2c9 feat: use gtk-layer-shell library for correct positioning of popups
To enable: use sway >= 1.2, compile waybar with `-Dgtk-layer-shell=enabled` meson option.
Original behavior could be restored at runtime by setting `"gtk-layer-shell": false` in waybar config.
2019-12-27 16:42:14 -08:00
0e87b3938a deps: meson dependencies for gtk-layer-shell feature 2019-12-27 16:42:13 -08:00
d1637d34cf refactor: fetch outputs from Gtk::Display instead of wl_registry.
gtk-layer-shell wants Gdk::Monitor instead of wl_output;
change code to deal with Gdk objects and slightly simplify it.
Requires gtkmm 3.22.0+ (first release with Gdk::Monitor support).
2019-12-27 16:42:12 -08:00
2277ddd156 Merge pull request #520 from Alexays/revert-517-master
Revert "Add info on "on-click-middle" option to custom module man page"
2019-12-17 11:58:42 +01:00
f9543e47dd Revert "Add info on "on-click-middle" option to custom module man page" 2019-12-17 11:58:30 +01:00
4c548a95e8 Merge pull request #517 from fuzxi/master
Add info on "on-click-middle" option to custom module man page
2019-12-16 15:19:44 +01:00
6c3cb7c85a Update waybar-temperature.5.scd 2019-12-15 15:11:06 -08:00
a307042d6b Update waybar-sway-window.5.scd 2019-12-15 15:10:26 -08:00
548589ee5d Update waybar-sway-mode.5.scd 2019-12-15 15:10:14 -08:00
b2cec74c04 Update waybar-pulseaudio.5.scd 2019-12-15 15:09:44 -08:00
6895b3b6b6 Update waybar-network.5.scd 2019-12-15 15:09:33 -08:00
ac8e892cbd Update waybar-mpd.5.scd 2019-12-15 15:09:08 -08:00
1a05e230b9 Update waybar-memory.5.scd 2019-12-15 15:08:41 -08:00
ad08130b52 Update waybar-idle-inhibitor.5.scd 2019-12-15 15:08:27 -08:00
a94d5c5ff2 Update waybar-disk.5.scd 2019-12-15 15:08:15 -08:00
ecce607afe Update waybar-cpu.5.scd 2019-12-15 15:07:38 -08:00
73bb42d40c Update waybar-clock.5.scd 2019-12-15 15:07:23 -08:00
3bb03852fe Update waybar-battery.5.scd 2019-12-15 15:07:07 -08:00
2aaf5510b8 Update waybar-backlight.5.scd 2019-12-15 15:06:23 -08:00
278f26e597 add info on "on-click-middle" option 2019-12-14 22:28:31 -08:00
03dab1bbdd Merge pull request #512 from josteink/patch-1
Provide easy bootstrap command for Ubuntu-users
2019-12-09 12:39:40 +00:00
0572f75055 Provide easy bootstrap command for Ubuntu-users 2019-12-09 13:20:29 +01:00
de0a3cb020 Merge pull request #510 from HardDie/master
Toggle opacity
2019-11-28 21:05:00 +00:00
eb4c76f5e4 Toggle opacity 2019-11-28 20:28:28 +03:00
502b33b64a Merge pull request #507 from xaiki/master
pulseaudio: export desc as a format
2019-11-25 21:33:36 +00:00
6d5b502bbb pulseaudio: export desc as a format
Signed-off-by: Niv Sardi <xaiki@evilgiggle.com>
2019-11-25 05:16:58 -03:00
2faf629c88 Merge pull request #502 from maxice8/master
Add Alpine Linux to list of distros
2019-11-11 13:39:57 +00:00
Leo
c0355a3fd3 Add Alpine Linux to list of distros 2019-11-11 07:46:10 -03:00
67f6dad717 feat: clang-format 2019-10-23 15:03:32 +01:00
7f7ebb5367 fix(workspaces): add int comparison 2019-10-19 16:19:35 +01:00
b97ca2ec9b Merge pull request #485 from Ventto/fix-on-click-options
fix: No need to format string with unused 'arg' named argument (#484)
2019-10-18 16:18:04 +01:00
358426cb1c fix: No need to format string with unused 'arg' named argument (#484)
The fmt::format() function looks for the "{arg}" named argument
in the given "format" string which does not exist. It will fail
if the string contains any {...} curled-brace substring.

Consequently, any "on-click*" option's command line containing for
instance substring like "${var}" or "awk '{...}'" will crash the program.

Signed-off-by: Thomas Venriès <thomas.venries@gmail.com>
2019-10-16 22:39:45 +02:00
5e4fdb1530 Detect timezone changes (#480)
Detect timezone changes
2019-10-07 08:23:00 +01:00
1e82982dbd Detect timezone changes
Fixes #479, because upstream does not intend to.

It may be less expensive to do that only once in a while, or to inotify-watch on /etc/timezone, but this is good enough.
2019-10-04 23:25:09 +02:00
048d8d328e refactor(Battery): ouput 'Plugged' instead of 'Charging' in the fallback 2019-10-03 09:37:37 +01:00
6d5e328928 feat(style): add comment about icons font 2019-10-02 10:27:46 +01:00
97e3226801 Merge pull request #475 from SJK132/SJK132-audio-bound
change audio scroll bound
2019-10-01 10:09:54 +01:00
3d1fd4f5ea change audio scroll bound
Change default behavior from 0% - 99% to 0% - 100%.
2019-09-28 18:05:31 -05:00
3131eb0774 Add a custom formatter for "pow" numbers. (#472)
Add a custom formatter for "pow" numbers.
2019-09-25 13:17:18 +01:00
f4d2ca2736 custom formatter for numbers in 'pow' units format 2019-09-25 12:53:06 +02:00
b3f9425d70 fix: ci 2019-09-25 09:59:07 +01:00
211b1c2785 fix: no need to wait on start 2019-09-25 09:32:22 +01:00
9e13161385 chore: update README 2019-09-25 09:27:53 +01:00
bd5a215d2a Add a disk module (#471)
Add a disk module
2019-09-25 09:23:02 +01:00
247589efbb Add some documentation for disks 2019-09-25 09:11:03 +02:00
1d39ef5c8e Add a disk module 2019-09-25 08:47:33 +02:00
4667afaa98 fix: avoid pulling submodule on ci 2019-09-25 00:02:07 +01:00
bae83ee4e3 fix: static_cast clamp 2019-09-19 22:07:38 +01:00
52db1e8e68 Merge pull request #467 from jspngh/bugfix/mpd_module
Add more locking in mpd module
2019-09-19 22:03:23 +01:00
334bc1e52a fix(Network): clamp signal strength 2019-09-19 21:59:28 +01:00
e6599d8ed5 modules/mpd: take lock in waitForEvent to prevent SIGABORT
+ replaced deprecated MPD_IDLE_PLAYLIST with MPD_IDLE_QUEUE
+ add mutex for periodic_updater
2019-09-19 21:43:12 +02:00
5ee4c54b13 Merge pull request #465 from krystiancha/fix-mpd-style
add padding for mpd module in default style
2019-09-17 19:38:10 +01:00
8830247b9e add padding for mpd module in default style
In the default style.css many modules (clock, battery, cpu, ...) get horizontal padding, but mpd module does not. This commit adds mpd to the list of modules that get the padding.
2019-09-17 19:50:53 +02:00
db74a46d7b Merge pull request #460 from gdamjan/master
service file: allow for waybar installed in different prefix
2019-09-12 17:40:03 +02:00
4b24c19fa7 add prefix to the configuration data 2019-09-10 14:12:52 +02:00
ce50249c04 service file: allow for waybar installed in different prefix 2019-09-10 13:56:34 +02:00
02811293e0 Revert "service file: allow for waybar installed in different pr… (#459)
Revert "service file: allow for waybar installed in different prefix"
2019-09-10 12:06:19 +02:00
5bb6fa384d Revert "service file: allow for waybar installed in different prefix" 2019-09-10 12:06:06 +02:00
b6cb02ce85 service file: allow for waybar installed in different prefix (#458)
service file: allow for waybar installed in different prefix
2019-09-10 11:56:20 +02:00
a0a218c6e2 Merge branch 'master' into master 2019-09-09 19:05:51 +02:00
f152ad9fa9 service file: allow for waybar installed in different prefix 2019-09-08 14:04:34 +02:00
05d083726c Merge pull request #456 from gdamjan/master
install a systemd user service unit
2019-09-08 10:25:09 +02:00
a0a3c01e79 install a systemd user unit
add a systemd --user unit/service file, so that one can run waybar as a
--user systemd service. when the service is enabled, Waybar will start
when the wayland-session.target is started.

this feature is automatically enabled if systemd is found, but can be disabled
with -Dsystemd=disabled
2019-09-07 22:16:03 +02:00
228078d538 chore: remove opensuse as broken atm 2019-09-07 11:52:14 +02:00
e43b63ff02 Merge pull request #455 from lolzballs/master
Add configurable time display for battery module
2019-09-07 11:50:58 +02:00
dc93fa8218 Documentation for format-time 2019-09-06 14:53:43 -04:00
a63bc84918 Add configurable time display for battery module
Adds a `format-time` configuration for the battery module so that users
can configure how they want their remaining time to be displayed.

The default format remains the same as before, i.e. `{H} h {M} min`,
but users can choose something like `{H}:{M:02d}` to give an output
like `4:29` if wanted.
2019-09-04 14:43:52 -04:00
060b614eb3 Merge pull request #449 from MichaelAquilina/fix_typo
Fix some issues in man pages
2019-09-03 13:06:34 +02:00
dfb5ec0c65 Fix formatting in waybar-puluseaudio 2019-09-03 10:34:12 +01:00
1f84936639 Add vim ft=Dockerfile for syntax highlight detection in editors 2019-09-02 18:07:49 +01:00
f9b65baa2e Build scdocs in travis to pervent breakages 2019-09-02 17:37:46 +01:00
ec23984c4d Fix minor typo in waybar-states 2019-09-02 17:33:31 +01:00
56d9cdc245 Merge pull request #447 from alebastr/exclusive-zone
Fix some issues with exclusive zone and margins
2019-09-02 10:24:29 +02:00
529daedcec fix: correct handling of margins on anchored axis 2019-09-01 01:08:15 -07:00
089d1299c4 fix: correct calculation of exclusive zone 2019-09-01 01:08:07 -07:00
36fc8365ee Merge pull request #446 from bschacht/fix-typo-persistant
Fix typo: persistant -> persistent
2019-08-31 21:13:31 +02:00
f2f1c4f9cb Merge pull request #445 from bschacht/patch-1
Fix typo
2019-08-31 21:11:38 +02:00
2646390e28 Fix typo: persistant -> persistent 2019-08-31 19:57:44 +02:00
66f2cbed51 Fix typo
ouput -> output
2019-08-31 17:50:56 +00:00
19d7bd4ecf Merge pull request #444 from MichaelAquilina/fix_memory_man
fix: correct man memory description
2019-08-30 13:57:17 +02:00
2e4de1ae3e fix: correct man memory description 2019-08-30 09:41:01 +01:00
ce909eea0b Merge pull request #443 from jbeich/man
Allow downstream to disable manpages even if scdoc is found
2019-08-29 13:27:27 +02:00
0b008154d9 Add man-pages option like swaywm/sway@ba16f16e4d 2019-08-29 10:25:13 +00:00
38 changed files with 672 additions and 229 deletions

View File

@ -3,18 +3,21 @@ sudo: false
services:
- docker
git:
submodules: false
env:
- distro: debian
- distro: archlinux
- distro: opensuse
- distro: fedora
- distro: alpine
before_install:
- docker pull alexays/waybar:${distro}
- find . -type f \( -name '*.cpp' -o -name '*.h' \) -print0 | xargs -r0 clang-format -i
script:
- echo FROM alexays/waybar:${distro} > Dockerfile
- echo ADD . /root >> Dockerfile
- docker build -t waybar .
- docker run waybar /bin/sh -c "cd /root && meson build && ninja -C build"
- docker run waybar /bin/sh -c "cd /root && meson build -Dman-pages=enabled && ninja -C build"

View File

@ -1,3 +1,5 @@
# vim: ft=Dockerfile
FROM alpine:latest
RUN apk add --no-cache git meson alpine-sdk libinput-dev wayland-dev wayland-protocols mesa-dev libxkbcommon-dev eudev-dev pixman-dev gtkmm3-dev jsoncpp-dev libnl3-dev pulseaudio-dev libmpdclient-dev scdoc

View File

@ -1,3 +1,5 @@
# vim: ft=Dockerfile
FROM archlinux/base:latest
RUN pacman -Syu --noconfirm && \

View File

@ -1,3 +1,5 @@
# vim: ft=Dockerfile
FROM debian:sid
RUN apt-get update && \

View File

@ -1,3 +1,5 @@
# vim: ft=Dockerfile
FROM fedora:30
RUN dnf install sway meson git libinput-devel wayland-devel wayland-protocols-devel egl-wayland-devel mesa-libEGL-devel mesa-libGLES-devel mesa-libgbm-devel libxkbcommon-devel libudev-devel pixman-devel gtkmm30-devel jsoncpp-devel scdoc -y && \

View File

@ -1,3 +1,5 @@
# vim: ft=Dockerfile
FROM opensuse/tumbleweed:latest
RUN zypper -n up && \

View File

@ -2,7 +2,7 @@
> 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
[AUR](https://aur.archlinux.org/packages/waybar-git/) and [openSUSE](https://build.opensuse.org/package/show/X11:Wayland/waybar)<br>
[AUR](https://aur.archlinux.org/packages/waybar-git/), [openSUSE](https://build.opensuse.org/package/show/X11:Wayland/waybar), and [Alpine Linux](https://pkgs.alpinelinux.org/packages?name=waybar)<br>
> *Waybar [examples](https://github.com/Alexays/Waybar/wiki/Examples)*
**Current features**
@ -12,6 +12,7 @@
- Battery
- Network
- Pulseaudio
- Disk
- Memory
- Cpu load average
- Temperature
@ -55,6 +56,13 @@ libdbusmenu-gtk3 [Tray module]
libmpdclient [MPD module]
```
On Ubuntu 19.10 you can install all the relevant dependencies using this command:
```
sudo apt install libgtkmm-3.0-dev libjsoncpp-dev libinput-dev libsigc++-2.0-dev libpulse-dev libnl-3-dev libdbusmenu-gtk3-dev libnl-genl-3-dev libfmt-dev clang-tidy scdoc libmpdclient-dev
```
Contributions welcome! - have fun :)<br>
The style guidelines is [Google's](https://google.github.io/styleguide/cppguide.html)

View File

@ -26,7 +26,6 @@ class AModule : public IModule {
const Json::Value &config_;
Gtk::EventBox event_box_;
std::string click_param_;
virtual bool handleToggle(GdkEventButton *const &ev);
virtual bool handleScroll(GdkEventScroll *);

View File

@ -1,5 +1,6 @@
#pragma once
#include <gdkmm/monitor.h>
#include <glibmm/refptr.h>
#include <gtkmm/box.h>
#include <gtkmm/cssprovider.h>
@ -15,10 +16,11 @@ namespace waybar {
class Factory;
struct waybar_output {
struct wl_output * output = nullptr;
std::string name;
uint32_t wl_name;
struct zxdg_output_v1 *xdg_output = nullptr;
Glib::RefPtr<Gdk::Monitor> monitor;
std::string name;
std::unique_ptr<struct zxdg_output_v1, decltype(&zxdg_output_v1_destroy)> xdg_output = {
nullptr, &zxdg_output_v1_destroy};
};
class Bar {
@ -30,13 +32,12 @@ class Bar {
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;
struct waybar_output *output;
Json::Value config;
Gtk::Window window;
struct wl_surface * surface;
bool visible = true;
bool vertical = false;
private:
static constexpr const char *MIN_HEIGHT_MSG =
@ -51,11 +52,14 @@ class Bar {
uint32_t, uint32_t);
static void layerSurfaceHandleClosed(void *, struct zwlr_layer_surface_v1 *);
void destroyOutput();
#ifdef HAVE_GTK_LAYER_SHELL
void initGtkLayerShell();
#endif
void onConfigure(GdkEventConfigure *ev);
void onRealize();
void onMap(GdkEventAny *ev);
void setMarginsAndZone(uint32_t height, uint32_t width);
void setExclusiveZone(uint32_t width, uint32_t height);
void setSurfaceSize(uint32_t width, uint32_t height);
auto setupWidgets() -> void;
void getModules(const Factory &, const std::string &);
void setupAltFormatKeyForModule(const std::string &module_name);
@ -67,6 +71,9 @@ class Bar {
int bottom = 0;
int left = 0;
} margins_;
struct zwlr_layer_surface_v1 *layer_surface_;
// use gtk-layer-shell instead of handling layer surfaces directly
bool use_gls_ = false;
uint32_t width_ = 0;
uint32_t height_ = 1;
uint8_t anchor_;

View File

@ -34,17 +34,15 @@ class Client {
bool isValidOutput(const Json::Value &config, std::unique_ptr<struct waybar_output> &output);
auto setupConfig(const std::string &config_file) -> void;
auto setupCss(const std::string &css_file) -> void;
std::unique_ptr<struct waybar_output> &getOutput(uint32_t wl_name);
std::unique_ptr<struct waybar_output> &getOutput(void *);
std::vector<Json::Value> getOutputConfigs(std::unique_ptr<struct waybar_output> &output);
static void handleGlobal(void *data, struct wl_registry *registry, uint32_t name,
const char *interface, uint32_t version);
static void handleGlobalRemove(void *data, struct wl_registry *registry, uint32_t name);
static void 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 handleOutputName(void *, struct zxdg_output_v1 *, const char *);
void handleMonitorAdded(Glib::RefPtr<Gdk::Monitor> monitor);
void handleMonitorRemoved(Glib::RefPtr<Gdk::Monitor> monitor);
Json::Value config_;
Glib::RefPtr<Gtk::StyleContext> style_context_;

View File

@ -13,6 +13,7 @@
#include "modules/cpu.hpp"
#include "modules/idle_inhibitor.hpp"
#include "modules/memory.hpp"
#include "modules/disk.hpp"
#if defined(HAVE_DBUSMENU) && !defined(NO_FILESYSTEM)
#include "modules/sni/tray.hpp"
#endif

23
include/modules/disk.hpp Normal file
View File

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

View File

@ -65,6 +65,9 @@ class MPD : public ALabel {
unique_status status_;
mpd_state state_;
unique_song song_;
// To make sure the previous periodic_updater stops before creating a new one
std::mutex periodic_lock_;
};
} // namespace waybar::modules

View File

@ -39,7 +39,7 @@ class Network : public ALabel {
void parseFreq(struct nlattr**);
bool associatedOrJoined(struct nlattr**);
bool checkInterface(struct ifinfomsg* rtif, std::string name);
int getPreferredIface(int skip_idx = -1) const;
int getPreferredIface(int skip_idx = -1, bool wait = true) const;
auto getInfo() -> void;
void checkNewInterface(struct ifinfomsg* rtif);
const std::string getNetworkState() const;

84
include/util/format.hpp Normal file
View File

@ -0,0 +1,84 @@
#pragma once
#include <fmt/format.h>
class pow_format {
public:
pow_format(long long val, std::string&& unit, bool binary = false):
val_(val), unit_(unit), binary_(binary) { };
long long val_;
std::string unit_;
bool binary_;
};
namespace fmt {
template <>
struct formatter<pow_format> {
char spec = 0;
int width = 0;
template <typename ParseContext>
constexpr auto parse(ParseContext& ctx) -> decltype (ctx.begin()) {
auto it = ctx.begin(), end = ctx.end();
if (it != end && *it == ':') ++it;
if (*it == '>' || *it == '<' || *it == '=') {
spec = *it;
++it;
}
if (it == end || *it == '}') return it;
if ('0' <= *it && *it <= '9') {
// We ignore it for now, but keep it for compatibility with
// existing configs where the format for pow_format'ed numbers was
// 'string' and specifications such as {:>9} were valid.
// The rationale for ignoring it is that the only reason to specify
// an alignment and a with is to get a fixed width bar, and ">" is
// sufficient in this implementation.
width = parse_nonnegative_int(it, end, ctx);
}
return it;
}
template<class FormatContext>
auto format(const pow_format& s, FormatContext &ctx) -> decltype (ctx.out()) {
const char* units[] = { "", "k", "M", "G", "T", "P", nullptr};
auto base = s.binary_ ? 1024ull : 1000ll;
auto fraction = (double) s.val_;
int pow;
for (pow = 0; units[pow+1] != nullptr && fraction / base >= 1; ++pow) {
fraction /= base;
}
auto max_width = 4 // coeff in {:.3g} format
+ 1 // prefix from units array
+ s.binary_ // for the 'i' in GiB.
+ s.unit_.length();
const char * format;
std::string string;
switch (spec) {
case '>':
return format_to(ctx.out(), "{:>{}}", fmt::format("{}", s), max_width);
case '<':
return format_to(ctx.out(), "{:<{}}", fmt::format("{}", s), max_width);
case '=':
format = "{coefficient:<4.3g}{padding}{prefix}{unit}";
break;
case 0:
default:
format = "{coefficient:.3g}{prefix}{unit}";
break;
}
return format_to(ctx.out(), format
, fmt::arg("coefficient", fraction)
, fmt::arg("prefix", std::string() + units[pow] + ((s.binary_ && pow) ? "i" : ""))
, fmt::arg("unit", s.unit_)
, fmt::arg("padding", pow ? "" : s.binary_ ? " " : " ")
);
}
};
}

View File

@ -32,6 +32,11 @@ The *battery* module displays the current capacity and state (eg. charging) of y
default: {capacity}% ++
The format, how the time should be displayed.
*format-time* ++
typeof: string ++
default: {H} h {M} min ++
The format, how the time should be displayed.
*format-icons*
typeof: array/object
Based on the current capacity, the corresponding icon gets selected. ++
@ -78,6 +83,14 @@ The *battery* module displays the current capacity and state (eg. charging) of y
*{time}*: Estimate of time until full or empty. Note that this is based on the power draw at the last refresh time, not an average.
# TIME FORMAT
The *battery* module allows you to define how time should be formatted via *format-time*.
The two arguments are:
*{H}*: Hours
*{M}*: Minutes
# CUSTOM FORMATS
The *battery* module allows to define custom formats based on up to two factors. The best fitting format will be selected.

93
man/waybar-disk.5.scd Normal file
View File

@ -0,0 +1,93 @@
waybar-disk(5)
# NAME
waybar - disk module
# DESCRIPTION
The *disk* module displays the current disk space used.
# CONFIGURATION
Addressed by *disk*
*path*: ++
typeof: string ++
default: "/" ++
Any path residing in the filesystem or mountpoint for which the information should be displayed.
*interval*: ++
typeof: integer++
default: 30 ++
The interval in which the information gets polled.
*format*: ++
typeof: string ++
default: "{percentage_used}%" ++
The format, how information should be displayed.
*rotate*: ++
typeof: integer ++
Positive value to rotate the text label.
*max-length*: ++
typeof: integer ++
The maximum length in character the module should display.
*on-click*: ++
typeof: string ++
Command to execute when clicked on the module.
*on-click-right*: ++
typeof: string ++
Command to execute when you right clicked on the module.
*on-scroll-up*: ++
typeof: string ++
Command to execute when scrolling up on the module.
*on-scroll-down*: ++
typeof: string ++
Command to execute when scrolling down on the module.
*smooth-scrolling-threshold*: ++
typeof: double ++
Threshold to be used when scrolling.
*tooltip*: ++
typeof: bool ++
default: true ++
Option to disable tooltip on hover.
*tooltip-format*: ++
typeof: string ++
default: "{used} out of {total} used ({percentage_used}%)" ++
The format of the information displayed in the tooltip.
# FORMAT REPLACEMENTS
*{percentage_used}*: Percentage of disk in use.
*{percentage_free}*: Percentage of free disk space
*{total}*: Total amount of space on the disk, partition or mountpoint.
*{used}*: Amount of used disk space.
*{free}*: Amount of available disk space for normal users.
*{path}*: The path specified in the configuration.
# EXAMPLES
```
"disk": {
"interval": 30,
"format": "{percentage_free}% free on {path}",
}
```
# STYLE
- *#disk*

View File

@ -6,7 +6,7 @@ waybar - memory module
# DESCRIPTION
The *memory* module displays the current date and time.
The *memory* module displays the current memory utilization.
# CONFIGURATION

View File

@ -36,7 +36,7 @@ Additionally you can control the volume by scrolling *up* or *down* while the cu
*format-icons*: ++
typeof: array ++
Based on the current port-name and volume, the corresponding icon gets selected. The order is *low* to *high*. See [`Icons`](#module-pulseaudio-config-icons)
Based on the current port-name and volume, the corresponding icon gets selected. The order is *low* to *high*. See *Icons*.
*rotate*: ++
typeof: integer ++
@ -82,15 +82,17 @@ Additionally you can control the volume by scrolling *up* or *down* while the cu
# FORMAT REPLACEMENTS
*{desc}*: Pulseaudio port's description, for bluetooth it'll be the device name.
*{volume}*: Volume in percentage.
*{icon}*: Icon, as defined in `format-icons`.
*{icon}*: Icon, as defined in *format-icons*.
*{format_source}*: Source format, `format-source`, `format-source-muted`.
*{format_source}*: Source format, *format-source*, *format-source-muted*.
# ICONS:
The following strings for `format-icons` are supported.
The following strings for *format-icons* are supported.
If they are found in the current PulseAudio port name, the corresponding icons will be selected.
- *default* (Shown, when no other port is found)

View File

@ -7,7 +7,7 @@ apply a class when the value matches the declared state value.
# STATES
- Every entry (*state*) consits of a *<name>* (typeof: *string*) and a *<value>* (typeof: *integer*).
- Every entry (*state*) consists of a *<name>* (typeof: *string*) and a *<value>* (typeof: *integer*).
- The state can be addressed as a CSS class in the *style.css*. The name of the CSS class is the *<name>* of the state.
Each class gets activated when the current capacity is equal or below the configured *<value>*.

View File

@ -76,15 +76,15 @@ Additional to workspace name matching, the following *format-icons* can be set.
- *urgent*: Will be shown, when workspace is flagged as urgent
- *focused*: Will be shown, when workspace is focused
# PERSISTANT WORKSPACES
# PERSISTENT WORKSPACES
Each entry of *persistant_workspace* names a workspace that should always be shown.
Each entry of *persistent_workspace* names a workspace that should always be shown.
Associated with that value is a list of outputs indicating *where* the workspace should be shown,
an empty list denoting all outputs.
```
"sway/workspaces": {
"persistant_workspaces": {
"persistent_workspaces": {
"3": [], // Always show a workspace with name '3', on all outputs if it does not exists
"4": ["eDP-1"], // Always show a workspace with name '4', on output 'eDP-1' if it does not exists
"5": ["eDP-1", "DP-2"] // Always show a workspace with name '5', on outputs 'eDP-1' and 'DP-2' if it does not exists
@ -120,4 +120,4 @@ n.b.: the list of outputs can be obtained from command line using *swaymsg -t ge
- *#workspaces button.visible*
- *#workspaces button.focused*
- *#workspaces button.urgent*
- *#workspaces button.persistant*
- *#workspaces button.persistent*

View File

@ -1,6 +1,6 @@
project(
'waybar', 'cpp', 'c',
version: '0.8.0',
version: '0.9.0',
license: 'MIT',
default_options : [
'cpp_std=c++17',
@ -52,7 +52,7 @@ spdlog = dependency('spdlog', version : ['>=1.3.1'], fallback : ['spdlog', 'spdl
wayland_client = dependency('wayland-client')
wayland_cursor = dependency('wayland-cursor')
wayland_protos = dependency('wayland-protocols')
gtkmm = dependency('gtkmm-3.0')
gtkmm = dependency('gtkmm-3.0', version : ['>=3.22.0'])
dbusmenu_gtk = dependency('dbusmenu-gtk3-0.4', required: get_option('dbusmenu-gtk'))
giounix = dependency('gio-unix-2.0', required: get_option('dbusmenu-gtk'))
jsoncpp = dependency('jsoncpp')
@ -62,6 +62,25 @@ libnlgen = dependency('libnl-genl-3.0', required: get_option('libnl'))
libpulse = dependency('libpulse', required: get_option('pulseaudio'))
libudev = dependency('libudev', required: get_option('libudev'))
libmpdclient = dependency('libmpdclient', required: get_option('mpd'))
gtk_layer_shell = dependency('gtk-layer-shell-0',
required: get_option('gtk-layer-shell'),
fallback : ['gtk-layer-shell', 'gtk_layer_shell_dep'])
systemd = dependency('systemd', required: get_option('systemd'))
prefix = get_option('prefix')
conf_data = configuration_data()
conf_data.set('prefix', prefix)
if systemd.found()
user_units_dir = systemd.get_pkgconfig_variable('systemduserunitdir')
configure_file(
configuration: conf_data,
input: './resources/waybar.service.in',
output: '@BASENAME@',
install_dir: user_units_dir
)
endif
src_files = files(
'src/factory.cpp',
@ -72,6 +91,7 @@ src_files = files(
'src/modules/clock.cpp',
'src/modules/custom.cpp',
'src/modules/cpu.cpp',
'src/modules/disk.cpp',
'src/modules/idle_inhibitor.cpp',
'src/modules/temperature.cpp',
'src/main.cpp',
@ -119,6 +139,10 @@ if libmpdclient.found()
src_files += 'src/modules/mpd.cpp'
endif
if gtk_layer_shell.found()
add_project_arguments('-DHAVE_GTK_LAYER_SHELL', language: 'cpp')
endif
subdir('protocol')
executable(
@ -141,7 +165,8 @@ executable(
libnlgen,
libpulse,
libudev,
libmpdclient
libmpdclient,
gtk_layer_shell
],
include_directories: [include_directories('include')],
install: true,
@ -153,7 +178,7 @@ install_data(
install_dir: join_paths(get_option('out'), 'etc/xdg/waybar')
)
scdoc = dependency('scdoc', version: '>=1.9.2', native: true, required: false)
scdoc = dependency('scdoc', version: '>=1.9.2', native: true, required: get_option('man-pages'))
if scdoc.found()
scdoc_prog = find_program(scdoc.get_pkgconfig_variable('scdoc'), native: true)
@ -166,6 +191,7 @@ if scdoc.found()
'waybar-clock.5.scd',
'waybar-cpu.5.scd',
'waybar-custom.5.scd',
'waybar-disk.5.scd',
'waybar-idle-inhibitor.5.scd',
'waybar-memory.5.scd',
'waybar-mpd.5.scd',

View File

@ -2,6 +2,9 @@ option('libcxx', type : 'boolean', value : false, description : 'Build with Clan
option('libnl', type: 'feature', value: 'auto', description: 'Enable libnl support for network 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('systemd', type: 'feature', value: 'auto', description: 'Install systemd user service unit')
option('dbusmenu-gtk', type: 'feature', value: 'auto', description: 'Enable support for tray')
option('man-pages', type: 'feature', value: 'auto', description: 'Generate and install man pages')
option('mpd', type: 'feature', value: 'auto', description: 'Enable support for the Music Player Daemon')
option('out', type: 'string', value : '/', description: 'output prefix directory')
option('gtk-layer-shell', type: 'feature', value: 'auto', description: 'Use gtk-layer-shell library for popups support')

View File

@ -1,6 +1,7 @@
* {
border: none;
border-radius: 0;
/* `otf-font-awesome` is required to be installed for icons */
font-family: Roboto, Helvetica, Arial, sans-serif;
font-size: 13px;
min-height: 0;
@ -75,7 +76,8 @@ window#waybar.chromium {
#custom-media,
#tray,
#mode,
#idle_inhibitor {
#idle_inhibitor,
#mpd {
padding: 0 10px;
margin: 0 4px;
color: #ffffff;

View File

@ -0,0 +1,12 @@
[Unit]
Description=Highly customizable Wayland bar for Sway and Wlroots based compositors.
Documentation=https://github.com/Alexays/Waybar/wiki/
PartOf=wayland-session.target
[Service]
Type=dbus
BusName=fr.arouillard.waybar
ExecStart=@prefix@/bin/waybar
[Install]
WantedBy=wayland-session.target

View File

@ -46,7 +46,7 @@ bool AModule::handleToggle(GdkEventButton* const& e) {
format = config_["on-click-forward"].asString();
}
if (!format.empty()) {
pid_.push_back(util::command::forkExec(fmt::format(format, fmt::arg("arg", click_param_))));
pid_.push_back(util::command::forkExec(format));
}
dp.emit();
return true;

View File

@ -1,3 +1,7 @@
#ifdef HAVE_GTK_LAYER_SHELL
#include <gtk-layer-shell.h>
#endif
#include "bar.hpp"
#include "client.hpp"
#include "factory.hpp"
@ -8,7 +12,7 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config)
config(w_config),
window{Gtk::WindowType::WINDOW_TOPLEVEL},
surface(nullptr),
layer_surface(nullptr),
layer_surface_(nullptr),
anchor_(ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP),
left_(Gtk::ORIENTATION_HORIZONTAL, 0),
center_(Gtk::ORIENTATION_HORIZONTAL, 0),
@ -28,11 +32,6 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config)
height_ = config["height"].isUInt() ? config["height"].asUInt() : height_;
width_ = config["width"].isUInt() ? config["width"].asUInt() : width_;
window.signal_realize().connect_notify(sigc::mem_fun(*this, &Bar::onRealize));
window.signal_map_event().connect_notify(sigc::mem_fun(*this, &Bar::onMap));
window.signal_configure_event().connect_notify(sigc::mem_fun(*this, &Bar::onConfigure));
window.set_size_request(width_, height_);
if (config["position"] == "bottom") {
anchor_ = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
} else if (config["position"] == "left") {
@ -53,74 +52,6 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config)
vertical = true;
}
setupWidgets();
if (window.get_realized()) {
onRealize();
}
window.show_all();
}
void waybar::Bar::onConfigure(GdkEventConfigure* ev) {
auto tmp_height = height_;
auto tmp_width = width_;
if (ev->height > static_cast<int>(height_)) {
// Default minimal value
if (height_ != 1) {
spdlog::warn(MIN_HEIGHT_MSG, height_, ev->height);
}
if (config["height"].isUInt()) {
spdlog::info(SIZE_DEFINED, "Height");
} else {
tmp_height = ev->height;
}
}
if (ev->width > static_cast<int>(width_)) {
// Default minimal value
if (width_ != 1) {
spdlog::warn(MIN_WIDTH_MSG, width_, ev->width);
}
if (config["width"].isUInt()) {
spdlog::info(SIZE_DEFINED, "Width");
} else {
tmp_width = ev->width;
}
}
if (tmp_width != width_ || tmp_height != height_) {
zwlr_layer_surface_v1_set_size(layer_surface, tmp_width, tmp_height);
}
}
void waybar::Bar::onRealize() {
auto gdk_window = window.get_window()->gobj();
gdk_wayland_window_set_use_custom_surface(gdk_window);
}
void waybar::Bar::onMap(GdkEventAny* ev) {
auto gdk_window = window.get_window()->gobj();
surface = gdk_wayland_window_get_wl_surface(gdk_window);
auto client = waybar::Client::inst();
auto 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(
client->layer_shell, surface, output->output, layer, "waybar");
zwlr_layer_surface_v1_set_keyboard_interactivity(layer_surface, false);
zwlr_layer_surface_v1_set_anchor(layer_surface, anchor_);
zwlr_layer_surface_v1_set_size(layer_surface, width_, height_);
setMarginsAndZone(height_, width_);
static const struct zwlr_layer_surface_v1_listener layer_surface_listener = {
.configure = layerSurfaceHandleConfigure,
.closed = layerSurfaceHandleClosed,
};
zwlr_layer_surface_v1_add_listener(layer_surface, &layer_surface_listener, this);
wl_surface_commit(surface);
wl_display_roundtrip(client->wl_display);
}
void waybar::Bar::setMarginsAndZone(uint32_t height, uint32_t width) {
if (config["margin-top"].isInt() || config["margin-right"].isInt() ||
config["margin-bottom"].isInt() || config["margin-left"].isInt()) {
margins_ = {
@ -165,10 +96,165 @@ void waybar::Bar::setMarginsAndZone(uint32_t height, uint32_t width) {
auto gaps = config["margin"].asInt();
margins_ = {.top = gaps, .right = gaps, .bottom = gaps, .left = gaps};
}
#ifdef HAVE_GTK_LAYER_SHELL
use_gls_ = config["gtk-layer-shell"].isBool() ? config["gtk-layer-shell"].asBool() : true;
if (use_gls_) {
initGtkLayerShell();
}
#endif
window.signal_realize().connect_notify(sigc::mem_fun(*this, &Bar::onRealize));
window.signal_map_event().connect_notify(sigc::mem_fun(*this, &Bar::onMap));
window.signal_configure_event().connect_notify(sigc::mem_fun(*this, &Bar::onConfigure));
window.set_size_request(width_, height_);
setupWidgets();
if (window.get_realized()) {
onRealize();
}
window.show_all();
}
void waybar::Bar::onConfigure(GdkEventConfigure* ev) {
auto tmp_height = height_;
auto tmp_width = width_;
if (ev->height > static_cast<int>(height_)) {
// Default minimal value
if (height_ != 1) {
spdlog::warn(MIN_HEIGHT_MSG, height_, ev->height);
}
if (config["height"].isUInt()) {
spdlog::info(SIZE_DEFINED, "Height");
} else {
tmp_height = ev->height;
}
}
if (ev->width > static_cast<int>(width_)) {
// Default minimal value
if (width_ != 1) {
spdlog::warn(MIN_WIDTH_MSG, width_, ev->width);
}
if (config["width"].isUInt()) {
spdlog::info(SIZE_DEFINED, "Width");
} else {
tmp_width = ev->width;
}
}
if (use_gls_) {
width_ = tmp_width;
height_ = tmp_height;
spdlog::debug("Set surface size {}x{} for output {}", width_, height_, output->name);
setExclusiveZone(tmp_width, tmp_height);
} else if (tmp_width != width_ || tmp_height != height_) {
setSurfaceSize(tmp_width, tmp_height);
}
}
#ifdef HAVE_GTK_LAYER_SHELL
void waybar::Bar::initGtkLayerShell() {
auto gtk_window = window.gobj();
// this has to be executed before GtkWindow.realize
gtk_layer_init_for_window(gtk_window);
gtk_layer_set_keyboard_interactivity(gtk_window, FALSE);
auto layer = config["layer"] == "top" ? GTK_LAYER_SHELL_LAYER_TOP : GTK_LAYER_SHELL_LAYER_BOTTOM;
gtk_layer_set_layer(gtk_window, layer);
gtk_layer_set_monitor(gtk_window, output->monitor->gobj());
gtk_layer_set_namespace(gtk_window, "waybar");
gtk_layer_set_anchor(
gtk_window, GTK_LAYER_SHELL_EDGE_LEFT, anchor_ & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT);
gtk_layer_set_anchor(
gtk_window, GTK_LAYER_SHELL_EDGE_RIGHT, anchor_ & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT);
gtk_layer_set_anchor(
gtk_window, GTK_LAYER_SHELL_EDGE_TOP, anchor_ & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP);
gtk_layer_set_anchor(
gtk_window, GTK_LAYER_SHELL_EDGE_BOTTOM, anchor_ & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM);
gtk_layer_set_margin(gtk_window, GTK_LAYER_SHELL_EDGE_LEFT, margins_.left);
gtk_layer_set_margin(gtk_window, GTK_LAYER_SHELL_EDGE_RIGHT, margins_.right);
gtk_layer_set_margin(gtk_window, GTK_LAYER_SHELL_EDGE_TOP, margins_.top);
gtk_layer_set_margin(gtk_window, GTK_LAYER_SHELL_EDGE_BOTTOM, margins_.bottom);
}
#endif
void waybar::Bar::onRealize() {
auto gdk_window = window.get_window()->gobj();
gdk_wayland_window_set_use_custom_surface(gdk_window);
}
void waybar::Bar::onMap(GdkEventAny* ev) {
auto gdk_window = window.get_window()->gobj();
surface = gdk_wayland_window_get_wl_surface(gdk_window);
if (use_gls_) {
return;
}
auto client = waybar::Client::inst();
// owned by output->monitor; no need to destroy
auto wl_output = gdk_wayland_monitor_get_wl_output(output->monitor->gobj());
auto 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(
client->layer_shell, surface, wl_output, layer, "waybar");
zwlr_layer_surface_v1_set_keyboard_interactivity(layer_surface_, false);
zwlr_layer_surface_v1_set_anchor(layer_surface_, anchor_);
zwlr_layer_surface_v1_set_margin(
layer_surface, margins_.top, margins_.right, margins_.bottom, margins_.left);
auto zone = vertical ? width + margins_.right : height + margins_.bottom;
zwlr_layer_surface_v1_set_exclusive_zone(layer_surface, zone);
layer_surface_, margins_.top, margins_.right, margins_.bottom, margins_.left);
setSurfaceSize(width_, height_);
setExclusiveZone(width_, height_);
static const struct zwlr_layer_surface_v1_listener layer_surface_listener = {
.configure = layerSurfaceHandleConfigure,
.closed = layerSurfaceHandleClosed,
};
zwlr_layer_surface_v1_add_listener(layer_surface_, &layer_surface_listener, this);
wl_surface_commit(surface);
wl_display_roundtrip(client->wl_display);
}
void waybar::Bar::setExclusiveZone(uint32_t width, uint32_t height) {
auto zone = 0;
if (visible) {
// exclusive zone already includes margin for anchored edge,
// only opposite margin should be added
if (vertical) {
zone += width;
zone += (anchor_ & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT) ? margins_.right : margins_.left;
} else {
zone += height;
zone += (anchor_ & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP) ? margins_.bottom : margins_.top;
}
}
spdlog::debug("Set exclusive zone {} for output {}", zone, output->name);
#ifdef HAVE_GTK_LAYER_SHELL
if (use_gls_) {
gtk_layer_set_exclusive_zone(window.gobj(), zone);
} else
#endif
{
zwlr_layer_surface_v1_set_exclusive_zone(layer_surface_, zone);
}
}
void waybar::Bar::setSurfaceSize(uint32_t width, uint32_t height) {
/* If the client is anchored to two opposite edges, layer_surface.configure will return
* size without margins for the axis.
* layer_surface.set_size, however, expects size with margins for the anchored axis.
* This is not specified by wlr-layer-shell and based on actual behavior of sway.
*/
if (vertical && height > 1) {
height += margins_.top + margins_.bottom;
}
if (!vertical && width > 1) {
width += margins_.right + margins_.left;
}
spdlog::debug("Set surface size {}x{} for output {}", width, height, output->name);
zwlr_layer_surface_v1_set_size(layer_surface_, width, height);
}
// Converting string to button code rn as to avoid doing it later
@ -240,8 +326,7 @@ void waybar::Bar::layerSurfaceHandleConfigure(void* data, struct zwlr_layer_surf
o->height_ = height;
o->window.set_size_request(o->width_, o->height_);
o->window.resize(o->width_, o->height_);
auto zone = o->vertical ? width + o->margins_.right : height + o->margins_.bottom;
zwlr_layer_surface_v1_set_exclusive_zone(o->layer_surface, zone);
o->setExclusiveZone(width, height);
spdlog::info(BAR_SIZE_MSG,
o->width_ == 1 ? "auto" : std::to_string(o->width_),
o->height_ == 1 ? "auto" : std::to_string(o->height_),
@ -253,9 +338,9 @@ void waybar::Bar::layerSurfaceHandleConfigure(void* data, struct zwlr_layer_surf
void waybar::Bar::layerSurfaceHandleClosed(void* data, struct zwlr_layer_surface_v1* /*surface*/) {
auto o = static_cast<waybar::Bar*>(data);
if (o->layer_surface) {
zwlr_layer_surface_v1_destroy(o->layer_surface);
o->layer_surface = nullptr;
if (o->layer_surface_) {
zwlr_layer_surface_v1_destroy(o->layer_surface_);
o->layer_surface_ = nullptr;
}
o->modules_left_.clear();
o->modules_center_.clear();
@ -264,13 +349,14 @@ void waybar::Bar::layerSurfaceHandleClosed(void* data, struct zwlr_layer_surface
auto waybar::Bar::toggle() -> void {
visible = !visible;
auto zone = visible ? height_ : 0;
if (!visible) {
window.get_style_context()->add_class("hidden");
window.set_opacity(0);
} else {
window.get_style_context()->remove_class("hidden");
window.set_opacity(1);
}
zwlr_layer_surface_v1_set_exclusive_zone(layer_surface, zone);
setExclusiveZone(width_, height_);
wl_surface_commit(surface);
}

View File

@ -1,4 +1,5 @@
#include "client.hpp"
#include <fmt/ostream.h>
#include <spdlog/spdlog.h>
#include <fstream>
#include <iostream>
@ -33,11 +34,6 @@ void waybar::Client::handleGlobal(void *data, struct wl_registry *registry, uint
if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
client->layer_shell = static_cast<struct zwlr_layer_shell_v1 *>(
wl_registry_bind(registry, name, &zwlr_layer_shell_v1_interface, version));
} else if (strcmp(interface, wl_output_interface.name) == 0) {
auto wl_output = static_cast<struct wl_output *>(
wl_registry_bind(registry, name, &wl_output_interface, version));
client->outputs_.emplace_back(new struct waybar_output({wl_output, "", name, nullptr}));
client->handleOutput(client->outputs_.back());
} else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0 &&
version >= ZXDG_OUTPUT_V1_NAME_SINCE_VERSION) {
client->xdg_output_manager = static_cast<struct zxdg_output_manager_v1 *>(wl_registry_bind(
@ -50,58 +46,21 @@ void waybar::Client::handleGlobal(void *data, struct wl_registry *registry, uint
void waybar::Client::handleGlobalRemove(void * data, struct wl_registry * /*registry*/,
uint32_t name) {
auto client = static_cast<Client *>(data);
for (auto it = client->bars.begin(); it != client->bars.end();) {
if ((*it)->output->wl_name == name) {
auto output_name = (*it)->output->name;
(*it)->window.close();
it = client->bars.erase(it);
spdlog::info("Bar removed from output: {}", output_name);
} 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()) {
if ((*it)->xdg_output != nullptr) {
zxdg_output_v1_destroy((*it)->xdg_output);
(*it)->xdg_output = nullptr;
}
if ((*it)->output != nullptr) {
wl_output_destroy((*it)->output);
(*it)->output = nullptr;
}
client->outputs_.erase(it);
}
// Nothing here
}
void waybar::Client::handleOutput(std::unique_ptr<struct waybar_output> &output) {
static const struct zxdg_output_v1_listener xdgOutputListener = {
.logical_position = handleLogicalPosition,
.logical_size = handleLogicalSize,
.done = handleDone,
.name = handleName,
.description = handleDescription,
.logical_position = [](void *, struct zxdg_output_v1 *, int32_t, int32_t) {},
.logical_size = [](void *, struct zxdg_output_v1 *, int32_t, int32_t) {},
.done = [](void *, struct zxdg_output_v1 *) {},
.name = &handleOutputName,
.description = [](void *, struct zxdg_output_v1 *, const char *) {},
};
output->xdg_output = zxdg_output_manager_v1_get_xdg_output(xdg_output_manager, output->output);
zxdg_output_v1_add_listener(output->xdg_output, &xdgOutputListener, &output->wl_name);
}
void waybar::Client::handleLogicalPosition(void * /*data*/,
struct zxdg_output_v1 * /*zxdg_output_v1*/,
int32_t /*x*/, int32_t /*y*/) {
// Nothing here
}
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
// owned by output->monitor; no need to destroy
auto wl_output = gdk_wayland_monitor_get_wl_output(output->monitor->gobj());
output->xdg_output.reset(zxdg_output_manager_v1_get_xdg_output(xdg_output_manager, wl_output));
zxdg_output_v1_add_listener(output->xdg_output.get(), &xdgOutputListener, output.get());
}
bool waybar::Client::isValidOutput(const Json::Value & config,
@ -123,9 +82,9 @@ bool waybar::Client::isValidOutput(const Json::Value & config
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;
std::unique_ptr<struct waybar::waybar_output> &waybar::Client::getOutput(void *addr) {
auto it = std::find_if(outputs_.begin(), outputs_.end(), [&addr](const auto &output) {
return output.get() == addr;
});
if (it == outputs_.end()) {
throw std::runtime_error("Unable to find valid output");
@ -148,23 +107,19 @@ std::vector<Json::Value> waybar::Client::getOutputConfigs(
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);
void waybar::Client::handleOutputName(void * data, struct zxdg_output_v1 * /*xdg_output*/,
const char *name) {
auto client = waybar::Client::inst();
try {
auto &output = client->getOutput(wl_name);
auto &output = client->getOutput(data);
output->name = name;
spdlog::debug("Output detected: {} ({} {})",
name,
output->monitor->get_manufacturer(),
output->monitor->get_model());
auto configs = client->getOutputConfigs(output);
if (configs.empty()) {
if (output->output != nullptr) {
wl_output_destroy(output->output);
output->output = nullptr;
}
if (output->xdg_output != nullptr) {
zxdg_output_v1_destroy(output->xdg_output);
output->xdg_output = nullptr;
}
output->xdg_output.reset();
} else {
wl_display_roundtrip(client->wl_display);
for (const auto &config : configs) {
@ -179,9 +134,26 @@ void waybar::Client::handleName(void * data, struct zxdg_output_v1 * /*xdg_
}
}
void waybar::Client::handleDescription(void * /*data*/, struct zxdg_output_v1 * /*zxdg_output_v1*/,
const char * /*description*/) {
// Nothing here
void waybar::Client::handleMonitorAdded(Glib::RefPtr<Gdk::Monitor> monitor) {
auto &output = outputs_.emplace_back(new struct waybar_output({monitor}));
handleOutput(output);
}
void waybar::Client::handleMonitorRemoved(Glib::RefPtr<Gdk::Monitor> monitor) {
spdlog::debug("Output removed: {} {}", monitor->get_manufacturer(), monitor->get_model());
for (auto it = bars.begin(); it != bars.end();) {
if ((*it)->output->monitor == monitor) {
auto output_name = (*it)->output->name;
(*it)->window.close();
it = bars.erase(it);
spdlog::info("Bar removed from output: {}", output_name);
} else {
++it;
}
}
std::remove_if(outputs_.begin(), outputs_.end(), [&monitor](const auto &output) {
return output->monitor == monitor;
});
}
std::tuple<const std::string, const std::string> waybar::Client::getConfigs(
@ -240,6 +212,14 @@ void waybar::Client::bindInterfaces() {
if (layer_shell == nullptr || xdg_output_manager == nullptr) {
throw std::runtime_error("Failed to acquire required resources.");
}
// add existing outputs and subscribe to updates
for (auto i = 0; i < gdk_display->get_n_monitors(); ++i) {
auto monitor = gdk_display->get_monitor(i);
handleMonitorAdded(monitor);
}
gdk_display->signal_monitor_added().connect(sigc::mem_fun(*this, &Client::handleMonitorAdded));
gdk_display->signal_monitor_removed().connect(
sigc::mem_fun(*this, &Client::handleMonitorRemoved));
}
int waybar::Client::main(int argc, char *argv[]) {

View File

@ -35,6 +35,9 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const {
if (ref == "clock") {
return new waybar::modules::Clock(id, config_[name]);
}
if (ref == "disk") {
return new waybar::modules::Disk(id, config_[name]);
}
#if defined(HAVE_DBUSMENU) && !defined(NO_FILESYSTEM)
if (ref == "tray") {
return new waybar::modules::SNI::Tray(id, bar_, config_[name]);

View File

@ -130,7 +130,7 @@ const std::string waybar::modules::Battery::getAdapterStatus(uint8_t capacity) c
return "Full";
}
if (online) {
return "Charging";
return "Plugged";
}
return "Discharging";
}
@ -141,7 +141,11 @@ const std::string waybar::modules::Battery::formatTimeRemaining(float hoursRemai
hoursRemaining = std::fabs(hoursRemaining);
uint16_t full_hours = static_cast<uint16_t>(hoursRemaining);
uint16_t minutes = static_cast<uint16_t>(60 * (hoursRemaining - full_hours));
return std::to_string(full_hours) + " h " + std::to_string(minutes) + " min";
auto format = std::string("{H} h {M} min");
if (config_["format-time"].isString()) {
format = config_["format-time"].asString();
}
return fmt::format(format, fmt::arg("H", full_hours), fmt::arg("M", minutes));
}
auto waybar::modules::Battery::update() -> void {

View File

@ -1,4 +1,5 @@
#include "modules/clock.hpp"
#include <time.h>
waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
: ALabel(config, "clock", id, "{:%H:%M}", 60) {
@ -12,6 +13,7 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
}
auto waybar::modules::Clock::update() -> void {
tzset(); // Update timezone information
auto now = std::chrono::system_clock::now();
auto localtime = fmt::localtime(std::chrono::system_clock::to_time_t(now));
auto text = fmt::format(format_, localtime);

76
src/modules/disk.cpp Normal file
View File

@ -0,0 +1,76 @@
#include "modules/disk.hpp"
using namespace waybar::util;
waybar::modules::Disk::Disk(const std::string& id, const Json::Value& config)
: ALabel(config, "disk", id, "{}%", 30)
, path_("/")
{
thread_ = [this] {
dp.emit();
thread_.sleep_for(interval_);
};
if (config["path"].isString()) {
path_ = config["path"].asString();
}
}
auto waybar::modules::Disk::update() -> void {
struct statvfs /* {
unsigned long f_bsize; // filesystem block size
unsigned long f_frsize; // fragment size
fsblkcnt_t f_blocks; // size of fs in f_frsize units
fsblkcnt_t f_bfree; // # free blocks
fsblkcnt_t f_bavail; // # free blocks for unprivileged users
fsfilcnt_t f_files; // # inodes
fsfilcnt_t f_ffree; // # free inodes
fsfilcnt_t f_favail; // # free inodes for unprivileged users
unsigned long f_fsid; // filesystem ID
unsigned long f_flag; // mount flags
unsigned long f_namemax; // maximum filename length
}; */ stats;
int err = statvfs(path_.c_str(), &stats);
/* Conky options
fs_bar - Bar that shows how much space is used
fs_free - Free space on a file system
fs_free_perc - Free percentage of space
fs_size - File system size
fs_used - File system used space
*/
if (err != 0) {
event_box_.hide();
return;
}
auto free = pow_format(stats.f_bavail * stats.f_bsize, "B", true);
auto used = pow_format((stats.f_blocks - stats.f_bavail) * stats.f_bsize, "B", true);
auto total = pow_format(stats.f_blocks * stats.f_bsize, "B", true);
label_.set_markup(fmt::format(format_
, stats.f_bavail * 100 / stats.f_blocks
, fmt::arg("free", free)
, fmt::arg("percentage_free", stats.f_bavail * 100 / stats.f_blocks)
, fmt::arg("used", used)
, fmt::arg("percentage_used", (stats.f_blocks - stats.f_bavail) * 100 / stats.f_blocks)
, fmt::arg("total", total)
, fmt::arg("path", path_)
));
if (tooltipEnabled()) {
std::string tooltip_format = "{used} used out of {total} on {path} ({percentage_used}%)";
if (config_["tooltip-format"].isString()) {
tooltip_format = config_["tooltip-format"].asString();
}
label_.set_tooltip_text(fmt::format(tooltip_format
, stats.f_bavail * 100 / stats.f_blocks
, fmt::arg("free", free)
, fmt::arg("percentage_free", stats.f_bavail * 100 / stats.f_blocks)
, fmt::arg("used", used)
, fmt::arg("percentage_used", (stats.f_blocks - stats.f_bavail) * 100 / stats.f_blocks)
, fmt::arg("total", total)
, fmt::arg("path", path_)
));
}
event_box_.show();
}

View File

@ -41,6 +41,10 @@ auto waybar::modules::MPD::update() -> void {
if (connection_ != nullptr) {
try {
bool wasPlaying = playing();
if(!wasPlaying) {
// Wait until the periodic_updater has stopped
std::lock_guard periodic_guard(periodic_lock_);
}
fetchState();
if (!wasPlaying && playing()) {
periodic_updater().detach();
@ -75,6 +79,7 @@ std::thread waybar::modules::MPD::event_listener() {
std::thread waybar::modules::MPD::periodic_updater() {
return std::thread([this] {
std::lock_guard guard(periodic_lock_);
while (connection_ != nullptr && playing()) {
dp.emit();
std::this_thread::sleep_for(std::chrono::seconds(1));
@ -297,7 +302,7 @@ void waybar::modules::MPD::waitForEvent() {
// 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))) {
conn, static_cast<mpd_idle>(MPD_IDLE_PLAYER | MPD_IDLE_OPTIONS | MPD_IDLE_QUEUE))) {
checkErrors(conn);
return;
}
@ -306,6 +311,10 @@ void waybar::modules::MPD::waitForEvent() {
// See issue #277:
// https://github.com/Alexays/Waybar/issues/277
mpd_recv_idle(conn, /* disable_timeout = */ false);
// See issue #281:
// https://github.com/Alexays/Waybar/issues/281
std::lock_guard guard(connection_lock_);
checkErrors(conn);
mpd_response_finish(conn);

View File

@ -2,9 +2,13 @@
#include <spdlog/spdlog.h>
#include <sys/eventfd.h>
#include <fstream>
#include "util/format.hpp"
namespace {
using namespace waybar::util;
constexpr const char *NETSTAT_FILE =
"/proc/net/netstat"; // std::ifstream does not take std::string_view as param
constexpr std::string_view BANDWIDTH_CATEGORY = "IpExt";
@ -98,7 +102,7 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf
createEventSocket();
createInfoSocket();
auto default_iface = getPreferredIface();
auto default_iface = getPreferredIface(-1, false);
if (default_iface != -1) {
ifid_ = default_iface;
char ifname[IF_NAMESIZE];
@ -259,26 +263,6 @@ auto waybar::modules::Network::update() -> void {
}
getState(signal_strength_);
auto pow_format = [](unsigned long long value, const std::string &unit) {
if (value > 2000ull * 1000ull * 1000ull) { // > 2G
auto go = value / (1000 * 1000 * 1000);
return std::to_string(go) + "." +
std::to_string((value - go * 1000 * 1000 * 1000) / (100 * 1000 * 1000)) + "G" + unit;
} else if (value > 2000ull * 1000ull) { // > 2M
auto mo = value / (1000 * 1000);
return std::to_string(mo) + "." + std::to_string((value - mo * 1000 * 1000) / (100 * 1000)) +
"M" + unit;
} else if (value > 2000ull) { // > 2k
auto ko = value / 1000;
return std::to_string(ko) + "." + std::to_string((value - ko * 1000) / 100) + "k" + unit;
} else {
return std::to_string(value) + unit;
}
};
auto text = fmt::format(
format_,
fmt::arg("essid", essid_),
@ -531,7 +515,7 @@ bool waybar::modules::Network::checkInterface(struct ifinfomsg *rtif, std::strin
return false;
}
int waybar::modules::Network::getPreferredIface(int skip_idx) const {
int waybar::modules::Network::getPreferredIface(int skip_idx, bool wait) const {
int ifid = -1;
if (config_["interface"].isString()) {
ifid = if_nametoindex(config_["interface"].asCString());
@ -563,7 +547,9 @@ int waybar::modules::Network::getPreferredIface(int skip_idx) const {
if (ifid > 0) {
return ifid;
}
std::this_thread::sleep_for(std::chrono::milliseconds(500));
if (wait) {
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
}
return -1;
}
@ -717,8 +703,9 @@ void waybar::modules::Network::parseSignal(struct nlattr **bss) {
// WiFi-hardware usually operates in the range -90 to -20dBm.
const int hardwareMax = -20;
const int hardwareMin = -90;
signal_strength_ =
((signal_strength_dbm_ - hardwareMin) / double{hardwareMax - hardwareMin}) * 100;
const int strength =
((signal_strength_dbm_ - hardwareMin) / double{hardwareMax - hardwareMin}) * 100;
signal_strength_ = std::clamp(strength, 0, 100);
}
if (bss[NL80211_BSS_SIGNAL_UNSPEC] != nullptr) {
signal_strength_ = nla_get_u8(bss[NL80211_BSS_SIGNAL_UNSPEC]);

View File

@ -86,7 +86,7 @@ bool waybar::modules::Pulseaudio::handleScroll(GdkEventScroll *e) {
change = round(config_["scroll-step"].asDouble() * volume_tick);
}
if (dir == SCROLL_DIR::UP) {
if (volume_ + 1 < 100) {
if (volume_ + 1 <= 100) {
pa_cvolume_inc(&pa_volume, change);
}
} else if (dir == SCROLL_DIR::DOWN) {
@ -221,6 +221,7 @@ auto waybar::modules::Pulseaudio::update() -> void {
}
format_source = fmt::format(format_source, fmt::arg("volume", source_volume_));
label_.set_markup(fmt::format(format,
fmt::arg("desc", desc_),
fmt::arg("volume", volume_),
fmt::arg("format_source", format_source),
fmt::arg("icon", getIcon(volume_, getPortIcon()))));

View File

@ -20,7 +20,7 @@ void Window::onCmd(const struct Ipc::ipc_response& res) {
try {
std::lock_guard<std::mutex> lock(mutex_);
auto payload = parser_.parse(res.payload);
auto output = payload["ouput"].isString() ? payload["output"].asString() : "";
auto output = payload["output"].isString() ? payload["output"].asString() : "";
std::tie(app_nb_, windowId_, window_, app_id_) = getFocusedNode(payload["nodes"], output);
dp.emit();
} catch (const std::exception& e) {

View File

@ -49,9 +49,9 @@ void Workspaces::onCmd(const struct Ipc::ipc_response &res) {
: true;
});
// adding persistant workspaces (as per the config file)
if (config_["persistant_workspaces"].isObject()) {
const Json::Value & p_workspaces = config_["persistant_workspaces"];
// adding persistent workspaces (as per the config file)
if (config_["persistent_workspaces"].isObject()) {
const Json::Value & p_workspaces = config_["persistent_workspaces"];
const std::vector<std::string> p_workspaces_names = p_workspaces.getMemberNames();
for (const std::string &p_w_name : p_workspaces_names) {
@ -88,6 +88,9 @@ void Workspaces::onCmd(const struct Ipc::ipc_response &res) {
std::sort(workspaces_.begin(),
workspaces_.end(),
[](const Json::Value &lhs, const Json::Value &rhs) {
if (lhs["name"].isInt() && rhs["name"].isInt()) {
return lhs["name"].asInt() < rhs["name"].asInt();
}
return lhs["name"].asString() < rhs["name"].asString();
});
}
@ -151,9 +154,9 @@ auto Workspaces::update() -> void {
button.get_style_context()->remove_class("urgent");
}
if ((*it)["target_output"].isString()) {
button.get_style_context()->add_class("persistant");
button.get_style_context()->add_class("persistent");
} else {
button.get_style_context()->remove_class("persistant");
button.get_style_context()->remove_class("persistent");
}
if (needReorder) {
box_.reorder_child(button, it - workspaces_.begin());

View File

@ -0,0 +1,5 @@
[wrap-file]
directory = gtk-layer-shell-0.1.0
source_filename = gtk-layer-shell-0.1.0.tar.gz
source_hash = f7569e27ae30b1a94c3ad6c955cf56240d6bc272b760d9d266ce2ccdb94a5cf0
source_url = https://github.com/wmww/gtk-layer-shell/archive/v0.1.0/gtk-layer-shell-0.1.0.tar.gz