Merge branch 'master' into taskbar/remove-trim
commit
d6381eeaff
|
@ -29,7 +29,9 @@ jobs:
|
||||||
compiler: clang
|
compiler: clang
|
||||||
env:
|
env:
|
||||||
before_install:
|
before_install:
|
||||||
- sudo pkg install -y gtk-layer-shell gtkmm30 jsoncpp libdbusmenu
|
- export CPPFLAGS+=-isystem/usr/local/include LDFLAGS+=-L/usr/local/lib # sndio
|
||||||
|
- sudo sed -i '' 's/quarterly/latest/' /etc/pkg/FreeBSD.conf
|
||||||
|
- sudo pkg install -y gtk-layer-shell gtkmm30 jsoncpp libdbusmenu sndio
|
||||||
libfmt libmpdclient libudev-devd meson pulseaudio scdoc spdlog
|
libfmt libmpdclient libudev-devd meson pulseaudio scdoc spdlog
|
||||||
script:
|
script:
|
||||||
- meson build -Dman-pages=enabled
|
- meson build -Dman-pages=enabled
|
||||||
|
|
|
@ -2,4 +2,4 @@
|
||||||
|
|
||||||
FROM alpine:latest
|
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 pugixml-dev libnl3-dev pulseaudio-dev libmpdclient-dev scdoc
|
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 pugixml-dev libnl3-dev pulseaudio-dev libmpdclient-dev sndio-dev scdoc
|
||||||
|
|
|
@ -3,4 +3,4 @@
|
||||||
FROM archlinux/base:latest
|
FROM archlinux/base:latest
|
||||||
|
|
||||||
RUN pacman -Syu --noconfirm && \
|
RUN pacman -Syu --noconfirm && \
|
||||||
pacman -S git meson base-devel libinput wayland wayland-protocols pixman libxkbcommon mesa gtkmm3 jsoncpp pugixml scdoc libpulse libdbusmenu-gtk3 libmpdclient --noconfirm
|
pacman -S git meson base-devel libinput wayland wayland-protocols pixman libxkbcommon mesa gtkmm3 jsoncpp pugixml scdoc libpulse libdbusmenu-gtk3 libmpdclient gobject-introspection --noconfirm
|
||||||
|
|
|
@ -3,5 +3,5 @@
|
||||||
FROM debian:sid
|
FROM debian:sid
|
||||||
|
|
||||||
RUN apt-get update && \
|
RUN apt-get update && \
|
||||||
apt-get install -y build-essential meson ninja-build git pkg-config libinput10 libpugixml-dev libinput-dev wayland-protocols libwayland-client0 libwayland-cursor0 libwayland-dev libegl1-mesa-dev libgles2-mesa-dev libgbm-dev libxkbcommon-dev libudev-dev libpixman-1-dev libgtkmm-3.0-dev libjsoncpp-dev scdoc libdbusmenu-gtk3-dev libnl-3-dev libnl-genl-3-dev libpulse-dev libmpdclient-dev gobject-introspection && \
|
apt-get install -y build-essential meson ninja-build git pkg-config libinput10 libpugixml-dev libinput-dev wayland-protocols libwayland-client0 libwayland-cursor0 libwayland-dev libegl1-mesa-dev libgles2-mesa-dev libgbm-dev libxkbcommon-dev libudev-dev libpixman-1-dev libgtkmm-3.0-dev libjsoncpp-dev scdoc libdbusmenu-gtk3-dev libnl-3-dev libnl-genl-3-dev libpulse-dev libmpdclient-dev gobject-introspection libgirepository1.0-dev && \
|
||||||
apt-get clean
|
apt-get clean
|
||||||
|
|
17
README.md
17
README.md
|
@ -5,7 +5,7 @@
|
||||||
[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>
|
[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)*
|
> *Waybar [examples](https://github.com/Alexays/Waybar/wiki/Examples)*
|
||||||
|
|
||||||
**Current features**
|
#### Current features
|
||||||
- Sway (Workspaces, Binding mode, Focused window name)
|
- Sway (Workspaces, Binding mode, Focused window name)
|
||||||
- Tray [#21](https://github.com/Alexays/Waybar/issues/21)
|
- Tray [#21](https://github.com/Alexays/Waybar/issues/21)
|
||||||
- Local time
|
- Local time
|
||||||
|
@ -22,11 +22,21 @@
|
||||||
- Multiple output configuration
|
- Multiple output configuration
|
||||||
- And much more customizations
|
- And much more customizations
|
||||||
|
|
||||||
**Configuration and Styling**
|
#### Configuration and Styling
|
||||||
|
|
||||||
[See the wiki for more details](https://github.com/Alexays/Waybar/wiki).
|
[See the wiki for more details](https://github.com/Alexays/Waybar/wiki).
|
||||||
|
|
||||||
**How to build**
|
### Installation
|
||||||
|
|
||||||
|
Waybar is available from a number of Linux distributions:
|
||||||
|
|
||||||
|
[![Packaging status](https://repology.org/badge/vertical-allrepos/waybar.svg)](https://repology.org/project/waybar/versions)
|
||||||
|
|
||||||
|
An Ubuntu PPA with more recent versions is available
|
||||||
|
[here](https://launchpad.net/~nschloe/+archive/ubuntu/waybar).
|
||||||
|
|
||||||
|
|
||||||
|
#### Building from source
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ git clone https://github.com/Alexays/Waybar
|
$ git clone https://github.com/Alexays/Waybar
|
||||||
|
@ -57,6 +67,7 @@ libnl [Network module]
|
||||||
libappindicator-gtk3 [Tray module]
|
libappindicator-gtk3 [Tray module]
|
||||||
libdbusmenu-gtk3 [Tray module]
|
libdbusmenu-gtk3 [Tray module]
|
||||||
libmpdclient [MPD module]
|
libmpdclient [MPD module]
|
||||||
|
libsndio [sndio module]
|
||||||
```
|
```
|
||||||
|
|
||||||
**Build dependencies**
|
**Build dependencies**
|
||||||
|
|
|
@ -10,7 +10,7 @@ namespace waybar {
|
||||||
class ALabel : public AModule {
|
class ALabel : public AModule {
|
||||||
public:
|
public:
|
||||||
ALabel(const Json::Value &, const std::string &, const std::string &, const std::string &format,
|
ALabel(const Json::Value &, const std::string &, const std::string &, const std::string &format,
|
||||||
uint16_t interval = 0, bool ellipsize = false);
|
uint16_t interval = 0, bool ellipsize = false, bool enable_click = false, bool enable_scroll = false);
|
||||||
virtual ~ALabel() = default;
|
virtual ~ALabel() = default;
|
||||||
virtual auto update() -> void;
|
virtual auto update() -> void;
|
||||||
virtual std::string getIcon(uint16_t, const std::string &alt = "", uint16_t max = 0);
|
virtual std::string getIcon(uint16_t, const std::string &alt = "", uint16_t max = 0);
|
||||||
|
|
|
@ -53,13 +53,18 @@ class Bar {
|
||||||
static void layerSurfaceHandleClosed(void *, struct zwlr_layer_surface_v1 *);
|
static void layerSurfaceHandleClosed(void *, struct zwlr_layer_surface_v1 *);
|
||||||
|
|
||||||
#ifdef HAVE_GTK_LAYER_SHELL
|
#ifdef HAVE_GTK_LAYER_SHELL
|
||||||
|
/* gtk-layer-shell code */
|
||||||
void initGtkLayerShell();
|
void initGtkLayerShell();
|
||||||
|
void onConfigureGLS(GdkEventConfigure *ev);
|
||||||
|
void onMapGLS(GdkEventAny *ev);
|
||||||
#endif
|
#endif
|
||||||
|
/* fallback layer-surface code */
|
||||||
void onConfigure(GdkEventConfigure *ev);
|
void onConfigure(GdkEventConfigure *ev);
|
||||||
void onRealize();
|
void onRealize();
|
||||||
void onMap(GdkEventAny *ev);
|
void onMap(GdkEventAny *ev);
|
||||||
void setExclusiveZone(uint32_t width, uint32_t height);
|
|
||||||
void setSurfaceSize(uint32_t width, uint32_t height);
|
void setSurfaceSize(uint32_t width, uint32_t height);
|
||||||
|
/* common code */
|
||||||
|
void setExclusiveZone(uint32_t width, uint32_t height);
|
||||||
auto setupWidgets() -> void;
|
auto setupWidgets() -> void;
|
||||||
void getModules(const Factory &, const std::string &);
|
void getModules(const Factory &, const std::string &);
|
||||||
void setupAltFormatKeyForModule(const std::string &module_name);
|
void setupAltFormatKeyForModule(const std::string &module_name);
|
||||||
|
|
|
@ -39,12 +39,17 @@
|
||||||
#ifdef HAVE_LIBMPDCLIENT
|
#ifdef HAVE_LIBMPDCLIENT
|
||||||
#include "modules/mpd.hpp"
|
#include "modules/mpd.hpp"
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef HAVE_LIBSNDIO
|
||||||
|
#include "modules/sndio.hpp"
|
||||||
|
#endif
|
||||||
#include "bar.hpp"
|
#include "bar.hpp"
|
||||||
#include "modules/custom.hpp"
|
#include "modules/custom.hpp"
|
||||||
#include "modules/temperature.hpp"
|
#include "modules/temperature.hpp"
|
||||||
#if defined(__linux__)
|
#if defined(__linux__)
|
||||||
|
# ifdef WANT_RFKILL
|
||||||
# include "modules/bluetooth.hpp"
|
# include "modules/bluetooth.hpp"
|
||||||
# endif
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace waybar {
|
namespace waybar {
|
||||||
|
|
||||||
|
|
|
@ -28,9 +28,12 @@ class Clock : public ALabel {
|
||||||
std::locale locale_;
|
std::locale locale_;
|
||||||
const date::time_zone* time_zone_;
|
const date::time_zone* time_zone_;
|
||||||
bool fixed_time_zone_;
|
bool fixed_time_zone_;
|
||||||
|
int time_zone_idx_;
|
||||||
date::year_month_day cached_calendar_ymd_;
|
date::year_month_day cached_calendar_ymd_;
|
||||||
std::string cached_calendar_text_;
|
std::string cached_calendar_text_;
|
||||||
|
|
||||||
|
bool handleScroll(GdkEventScroll* e);
|
||||||
|
|
||||||
auto calendar_text(const waybar_time& wtime) -> std::string;
|
auto calendar_text(const waybar_time& wtime) -> std::string;
|
||||||
auto weekdays_header(const date::weekday& first_dow, std::ostream& os) -> void;
|
auto weekdays_header(const date::weekday& first_dow, std::ostream& os) -> void;
|
||||||
auto first_day_of_week() -> date::weekday;
|
auto first_day_of_week() -> date::weekday;
|
||||||
|
|
|
@ -22,6 +22,7 @@ class Custom : public ALabel {
|
||||||
void continuousWorker();
|
void continuousWorker();
|
||||||
void parseOutputRaw();
|
void parseOutputRaw();
|
||||||
void parseOutputJson();
|
void parseOutputJson();
|
||||||
|
void handleEvent();
|
||||||
bool handleScroll(GdkEventScroll* e);
|
bool handleScroll(GdkEventScroll* e);
|
||||||
bool handleToggle(GdkEventButton* const& e);
|
bool handleToggle(GdkEventButton* const& e);
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,9 @@
|
||||||
#include <sys/epoll.h>
|
#include <sys/epoll.h>
|
||||||
#include "ALabel.hpp"
|
#include "ALabel.hpp"
|
||||||
#include "util/sleeper_thread.hpp"
|
#include "util/sleeper_thread.hpp"
|
||||||
|
#ifdef WANT_RFKILL
|
||||||
#include "util/rfkill.hpp"
|
#include "util/rfkill.hpp"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace waybar::modules {
|
namespace waybar::modules {
|
||||||
|
|
||||||
|
@ -52,6 +54,8 @@ class Network : public ALabel {
|
||||||
struct sockaddr_nl nladdr_ = {0};
|
struct sockaddr_nl nladdr_ = {0};
|
||||||
struct nl_sock* sock_ = nullptr;
|
struct nl_sock* sock_ = nullptr;
|
||||||
struct nl_sock* ev_sock_ = nullptr;
|
struct nl_sock* ev_sock_ = nullptr;
|
||||||
|
int efd_;
|
||||||
|
int ev_fd_;
|
||||||
int nl80211_id_;
|
int nl80211_id_;
|
||||||
std::mutex mutex_;
|
std::mutex mutex_;
|
||||||
|
|
||||||
|
@ -70,9 +74,11 @@ class Network : public ALabel {
|
||||||
|
|
||||||
util::SleeperThread thread_;
|
util::SleeperThread thread_;
|
||||||
util::SleeperThread thread_timer_;
|
util::SleeperThread thread_timer_;
|
||||||
|
#ifdef WANT_RFKILL
|
||||||
util::SleeperThread thread_rfkill_;
|
util::SleeperThread thread_rfkill_;
|
||||||
|
|
||||||
util::Rfkill rfkill_;
|
util::Rfkill rfkill_;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace waybar::modules
|
} // namespace waybar::modules
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <sndio.h>
|
||||||
|
#include <vector>
|
||||||
|
#include "ALabel.hpp"
|
||||||
|
#include "util/sleeper_thread.hpp"
|
||||||
|
|
||||||
|
namespace waybar::modules {
|
||||||
|
|
||||||
|
class Sndio : public ALabel {
|
||||||
|
public:
|
||||||
|
Sndio(const std::string&, const Json::Value&);
|
||||||
|
~Sndio();
|
||||||
|
auto update() -> void;
|
||||||
|
auto set_desc(struct sioctl_desc *, unsigned int) -> void;
|
||||||
|
auto put_val(unsigned int, unsigned int) -> void;
|
||||||
|
bool handleScroll(GdkEventScroll *);
|
||||||
|
bool handleToggle(GdkEventButton* const&);
|
||||||
|
|
||||||
|
private:
|
||||||
|
auto connect_to_sndio() -> void;
|
||||||
|
util::SleeperThread thread_;
|
||||||
|
struct sioctl_hdl *hdl_;
|
||||||
|
std::vector<struct pollfd> pfds_;
|
||||||
|
unsigned int addr_;
|
||||||
|
unsigned int volume_, old_volume_, maxval_;
|
||||||
|
bool muted_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace waybar::modules
|
|
@ -7,6 +7,9 @@
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
|
||||||
|
extern std::mutex reap_mtx;
|
||||||
|
extern std::list<pid_t> reap;
|
||||||
|
|
||||||
namespace waybar::util::command {
|
namespace waybar::util::command {
|
||||||
|
|
||||||
struct res {
|
struct res {
|
||||||
|
@ -32,10 +35,11 @@ inline std::string read(FILE* fp) {
|
||||||
|
|
||||||
inline int close(FILE* fp, pid_t pid) {
|
inline int close(FILE* fp, pid_t pid) {
|
||||||
int stat = -1;
|
int stat = -1;
|
||||||
|
pid_t ret;
|
||||||
|
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
do {
|
do {
|
||||||
waitpid(pid, &stat, WCONTINUED | WUNTRACED);
|
ret = waitpid(pid, &stat, WCONTINUED | WUNTRACED);
|
||||||
|
|
||||||
if (WIFEXITED(stat)) {
|
if (WIFEXITED(stat)) {
|
||||||
spdlog::debug("Cmd exited with code {}", WEXITSTATUS(stat));
|
spdlog::debug("Cmd exited with code {}", WEXITSTATUS(stat));
|
||||||
|
@ -45,6 +49,8 @@ inline int close(FILE* fp, pid_t pid) {
|
||||||
spdlog::debug("Cmd stopped by {}", WSTOPSIG(stat));
|
spdlog::debug("Cmd stopped by {}", WSTOPSIG(stat));
|
||||||
} else if (WIFCONTINUED(stat)) {
|
} else if (WIFCONTINUED(stat)) {
|
||||||
spdlog::debug("Cmd continued");
|
spdlog::debug("Cmd continued");
|
||||||
|
} else if (ret == -1) {
|
||||||
|
spdlog::debug("waitpid failed: {}", strerror(errno));
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -65,6 +71,12 @@ inline FILE* open(const std::string& cmd, int& pid) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!child_pid) {
|
if (!child_pid) {
|
||||||
|
int err;
|
||||||
|
sigset_t mask;
|
||||||
|
sigfillset(&mask);
|
||||||
|
// Reset sigmask
|
||||||
|
err = pthread_sigmask(SIG_UNBLOCK, &mask, nullptr);
|
||||||
|
if (err != 0) spdlog::error("pthread_sigmask in open failed: {}", strerror(err));
|
||||||
::close(fd[0]);
|
::close(fd[0]);
|
||||||
dup2(fd[1], 1);
|
dup2(fd[1], 1);
|
||||||
setpgid(child_pid, child_pid);
|
setpgid(child_pid, child_pid);
|
||||||
|
@ -97,7 +109,7 @@ inline struct res execNoRead(const std::string& cmd) {
|
||||||
inline int32_t forkExec(const std::string& cmd) {
|
inline int32_t forkExec(const std::string& cmd) {
|
||||||
if (cmd == "") return -1;
|
if (cmd == "") return -1;
|
||||||
|
|
||||||
int32_t pid = fork();
|
pid_t pid = fork();
|
||||||
|
|
||||||
if (pid < 0) {
|
if (pid < 0) {
|
||||||
spdlog::error("Unable to exec cmd {}, error {}", cmd.c_str(), strerror(errno));
|
spdlog::error("Unable to exec cmd {}, error {}", cmd.c_str(), strerror(errno));
|
||||||
|
@ -106,12 +118,20 @@ inline int32_t forkExec(const std::string& cmd) {
|
||||||
|
|
||||||
// Child executes the command
|
// Child executes the command
|
||||||
if (!pid) {
|
if (!pid) {
|
||||||
|
int err;
|
||||||
|
sigset_t mask;
|
||||||
|
sigfillset(&mask);
|
||||||
|
// Reset sigmask
|
||||||
|
err = pthread_sigmask(SIG_UNBLOCK, &mask, nullptr);
|
||||||
|
if (err != 0) spdlog::error("pthread_sigmask in forkExec failed: {}", strerror(err));
|
||||||
setpgid(pid, pid);
|
setpgid(pid, pid);
|
||||||
signal(SIGCHLD, SIG_DFL);
|
|
||||||
execl("/bin/sh", "sh", "-c", cmd.c_str(), (char*)0);
|
execl("/bin/sh", "sh", "-c", cmd.c_str(), (char*)0);
|
||||||
exit(0);
|
exit(0);
|
||||||
} else {
|
} else {
|
||||||
signal(SIGCHLD, SIG_IGN);
|
reap_mtx.lock();
|
||||||
|
reap.push_back(pid);
|
||||||
|
reap_mtx.unlock();
|
||||||
|
spdlog::debug("Added child to reap list: {}", pid);
|
||||||
}
|
}
|
||||||
|
|
||||||
return pid;
|
return pid;
|
||||||
|
|
|
@ -23,7 +23,7 @@ namespace fmt {
|
||||||
constexpr auto parse(ParseContext& ctx) -> decltype (ctx.begin()) {
|
constexpr auto parse(ParseContext& ctx) -> decltype (ctx.begin()) {
|
||||||
auto it = ctx.begin(), end = ctx.end();
|
auto it = ctx.begin(), end = ctx.end();
|
||||||
if (it != end && *it == ':') ++it;
|
if (it != end && *it == ':') ++it;
|
||||||
if (*it == '>' || *it == '<' || *it == '=') {
|
if (it && (*it == '>' || *it == '<' || *it == '=')) {
|
||||||
spec = *it;
|
spec = *it;
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,11 +26,21 @@ The *clock* module displays the current date and time.
|
||||||
default: inferred local timezone ++
|
default: inferred local timezone ++
|
||||||
The timezone to display the time in, e.g. America/New_York.
|
The timezone to display the time in, e.g. America/New_York.
|
||||||
|
|
||||||
|
*timezones*: ++
|
||||||
|
typeof: list of strings ++
|
||||||
|
A list of timezones to use for time display, changed using the scroll wheel. ++
|
||||||
|
Use "" to represent the system's local timezone. Using %Z in the format or tooltip format is useful to track which time zone is currently displayed.
|
||||||
|
|
||||||
*locale*: ++
|
*locale*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
default: inferred from current locale ++
|
default: inferred from current locale ++
|
||||||
A locale to be used to display the time. Intended to render times in custom timezones with the proper language and format.
|
A locale to be used to display the time. Intended to render times in custom timezones with the proper language and format.
|
||||||
|
|
||||||
|
*today-format*: ++
|
||||||
|
typeof: string ++
|
||||||
|
default: <b><u>{}</u></b> ++
|
||||||
|
The format of today's date in the calendar.
|
||||||
|
|
||||||
*max-length*: ++
|
*max-length*: ++
|
||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
The maximum length in character the module should display.
|
The maximum length in character the module should display.
|
||||||
|
|
|
@ -22,6 +22,12 @@ Addressed by *custom/<name>*
|
||||||
The path to a script, which determines if the script in *exec* should be executed.
|
The path to a script, which determines if the script in *exec* should be executed.
|
||||||
*exec* will be executed if the exit code of *exec-if* equals 0.
|
*exec* will be executed if the exit code of *exec-if* equals 0.
|
||||||
|
|
||||||
|
*exec-on-event*: ++
|
||||||
|
typeof: bool ++
|
||||||
|
default: true ++
|
||||||
|
If an event command is set (e.g. *on-click* or *on-scroll-up*) then re-execute the script after
|
||||||
|
executing the event command.
|
||||||
|
|
||||||
*return-type*: ++
|
*return-type*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
See *return-type*
|
See *return-type*
|
||||||
|
|
|
@ -31,6 +31,10 @@ Addressed by *disk*
|
||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
Positive value to rotate the text label.
|
Positive value to rotate the text label.
|
||||||
|
|
||||||
|
*states*: ++
|
||||||
|
typeof: array ++
|
||||||
|
A number of disk utilization states which get activated on certain percentage thresholds (percentage_used). See *waybar-states(5)*.
|
||||||
|
|
||||||
*max-length*: ++
|
*max-length*: ++
|
||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
The maximum length in character the module should display.
|
The maximum length in character the module should display.
|
||||||
|
|
|
@ -148,6 +148,10 @@ Addressed by *mpd*
|
||||||
|
|
||||||
*{totalTime}*: The length of the current song. To format as a date/time (see example configuration)
|
*{totalTime}*: The length of the current song. To format as a date/time (see example configuration)
|
||||||
|
|
||||||
|
*{songPosition}*: The position of the current song.
|
||||||
|
|
||||||
|
*{queueLength}*: The length of the current queue.
|
||||||
|
|
||||||
*{stateIcon}*: The icon corresponding the playing or paused status of the player (see *state-icons* option)
|
*{stateIcon}*: The icon corresponding the playing or paused status of the player (see *state-icons* option)
|
||||||
|
|
||||||
*{consumeIcon}*: The icon corresponding the "consume" option (see *consume-icons* option)
|
*{consumeIcon}*: The icon corresponding the "consume" option (see *consume-icons* option)
|
||||||
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
waybar-sndio(5)
|
||||||
|
|
||||||
|
# NAME
|
||||||
|
|
||||||
|
waybar - sndio module
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
|
||||||
|
The *sndio* module displays the current volume reported by sndio(7).
|
||||||
|
|
||||||
|
Additionally, you can control the volume by scrolling *up* or *down* while the
|
||||||
|
cursor is over the module, and clicking on the module toggles mute.
|
||||||
|
|
||||||
|
# CONFIGURATION
|
||||||
|
|
||||||
|
*format*: ++
|
||||||
|
typeof: string ++
|
||||||
|
default: {volume}% ++
|
||||||
|
The format for 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.
|
||||||
|
|
||||||
|
*scroll-step*: ++
|
||||||
|
typeof: int ++
|
||||||
|
default: 5 ++
|
||||||
|
The speed in which to change the volume when scrolling.
|
||||||
|
|
||||||
|
*on-click*: ++
|
||||||
|
typeof: string ++
|
||||||
|
Command to execute when clicked on the module.
|
||||||
|
This replaces the default behaviour of toggling mute.
|
||||||
|
|
||||||
|
*on-click-middle*: ++
|
||||||
|
typeof: string ++
|
||||||
|
Command to execute when middle-clicked on the module using mousewheel.
|
||||||
|
|
||||||
|
*on-click-right*: ++
|
||||||
|
typeof: string ++
|
||||||
|
Command to execute when you right clicked on the module.
|
||||||
|
|
||||||
|
*on-update*: ++
|
||||||
|
typeof: string ++
|
||||||
|
Command to execute when the module is updated.
|
||||||
|
|
||||||
|
*on-scroll-up*: ++
|
||||||
|
typeof: string ++
|
||||||
|
Command to execute when scrolling up on the module.
|
||||||
|
This replaces the default behaviour of volume control.
|
||||||
|
|
||||||
|
*on-scroll-down*: ++
|
||||||
|
typeof: string ++
|
||||||
|
Command to execute when scrolling down on the module.
|
||||||
|
This replaces the default behaviour of volume control.
|
||||||
|
|
||||||
|
*smooth-scrolling-threshold*: ++
|
||||||
|
typeof: double ++
|
||||||
|
Threshold to be used when scrolling.
|
||||||
|
|
||||||
|
# FORMAT REPLACEMENTS
|
||||||
|
|
||||||
|
*{volume}*: Volume in percentage.
|
||||||
|
|
||||||
|
*{raw_value}*: Volume as value reported by sndio.
|
||||||
|
|
||||||
|
# EXAMPLES
|
||||||
|
|
||||||
|
```
|
||||||
|
"sndio": {
|
||||||
|
"format": "{raw_value} 🎜",
|
||||||
|
"scroll-step": 3
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
# STYLE
|
||||||
|
|
||||||
|
- *#sndio*
|
||||||
|
- *#sndio.muted*
|
|
@ -31,6 +31,11 @@ Addressed by *sway/workspaces*
|
||||||
default: false ++
|
default: false ++
|
||||||
If set to false, you can scroll to cycle through workspaces. If set to true this behaviour is disabled.
|
If set to false, you can scroll to cycle through workspaces. If set to true this behaviour is disabled.
|
||||||
|
|
||||||
|
*disable-click*: ++
|
||||||
|
typeof: bool ++
|
||||||
|
default: false ++
|
||||||
|
If set to false, you can click to change workspace. If set to true this behaviour is disabled.
|
||||||
|
|
||||||
*smooth-scrolling-threshold*: ++
|
*smooth-scrolling-threshold*: ++
|
||||||
typeof: double ++
|
typeof: double ++
|
||||||
Threshold to be used when scrolling.
|
Threshold to be used when scrolling.
|
||||||
|
@ -134,3 +139,4 @@ n.b.: the list of outputs can be obtained from command line using *swaymsg -t ge
|
||||||
- *#workspaces button.urgent*
|
- *#workspaces button.urgent*
|
||||||
- *#workspaces button.persistent*
|
- *#workspaces button.persistent*
|
||||||
- *#workspaces button.current_output*
|
- *#workspaces button.current_output*
|
||||||
|
- *#workspaces button#sway-workspace-${name}*
|
||||||
|
|
|
@ -32,6 +32,11 @@ Addressed by *wlr/taskbar*
|
||||||
default: 16 ++
|
default: 16 ++
|
||||||
The size of the icon.
|
The size of the icon.
|
||||||
|
|
||||||
|
*markup*: ++
|
||||||
|
typeof: bool ++
|
||||||
|
default: false ++
|
||||||
|
If set to true, pango markup will be accepted in format and tooltip-format.
|
||||||
|
|
||||||
*tooltip*: ++
|
*tooltip*: ++
|
||||||
typeof: bool ++
|
typeof: bool ++
|
||||||
default: true ++
|
default: true ++
|
||||||
|
|
|
@ -14,6 +14,7 @@ Valid locations for this file are:
|
||||||
- *~/.config/waybar/config*
|
- *~/.config/waybar/config*
|
||||||
- *~/waybar/config*
|
- *~/waybar/config*
|
||||||
- */etc/xdg/waybar/config*
|
- */etc/xdg/waybar/config*
|
||||||
|
- *@sysconfdir@/xdg/waybar/config*
|
||||||
|
|
||||||
A good starting point is the default configuration found at https://github.com/Alexays/Waybar/blob/master/resources/config
|
A good starting point is the default configuration found at https://github.com/Alexays/Waybar/blob/master/resources/config
|
||||||
Also a minimal example configuration can be found on the at the bottom of this man page.
|
Also a minimal example configuration can be found on the at the bottom of this man page.
|
||||||
|
@ -185,14 +186,18 @@ Valid options for the "rotate" property are: 0, 90, 180 and 270.
|
||||||
|
|
||||||
- *waybar-backlight(5)*
|
- *waybar-backlight(5)*
|
||||||
- *waybar-battery(5)*
|
- *waybar-battery(5)*
|
||||||
|
- *waybar-bluetooth(5)*
|
||||||
- *waybar-clock(5)*
|
- *waybar-clock(5)*
|
||||||
- *waybar-cpu(5)*
|
- *waybar-cpu(5)*
|
||||||
- *waybar-custom(5)*
|
- *waybar-custom(5)*
|
||||||
|
- *waybar-disk(5)*
|
||||||
- *waybar-idle-inhibitor(5)*
|
- *waybar-idle-inhibitor(5)*
|
||||||
- *waybar-memory(5)*
|
- *waybar-memory(5)*
|
||||||
- *waybar-mpd(5)*
|
- *waybar-mpd(5)*
|
||||||
- *waybar-network(5)*
|
- *waybar-network(5)*
|
||||||
- *waybar-pulseaudio(5)*
|
- *waybar-pulseaudio(5)*
|
||||||
|
- *waybar-river-tags(5)*
|
||||||
|
- *waybar-states(5)*
|
||||||
- *waybar-sway-mode(5)*
|
- *waybar-sway-mode(5)*
|
||||||
- *waybar-sway-window(5)*
|
- *waybar-sway-window(5)*
|
||||||
- *waybar-sway-workspaces(5)*
|
- *waybar-sway-workspaces(5)*
|
61
meson.build
61
meson.build
|
@ -1,6 +1,6 @@
|
||||||
project(
|
project(
|
||||||
'waybar', 'cpp', 'c',
|
'waybar', 'cpp', 'c',
|
||||||
version: '0.9.2',
|
version: '0.9.4',
|
||||||
license: 'MIT',
|
license: 'MIT',
|
||||||
default_options : [
|
default_options : [
|
||||||
'cpp_std=c++17',
|
'cpp_std=c++17',
|
||||||
|
@ -9,6 +9,8 @@ project(
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fs = import('fs')
|
||||||
|
|
||||||
compiler = meson.get_compiler('cpp')
|
compiler = meson.get_compiler('cpp')
|
||||||
|
|
||||||
cpp_args = []
|
cpp_args = []
|
||||||
|
@ -94,6 +96,19 @@ 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'))
|
libmpdclient = dependency('libmpdclient', required: get_option('mpd'))
|
||||||
|
|
||||||
|
libsndio = compiler.find_library('sndio', required: get_option('sndio'))
|
||||||
|
if libsndio.found()
|
||||||
|
if not compiler.has_function('sioctl_open', prefix: '#include <sndio.h>', dependencies: libsndio)
|
||||||
|
if get_option('sndio').enabled()
|
||||||
|
error('libsndio is too old, required >=1.7.0')
|
||||||
|
else
|
||||||
|
warning('libsndio is too old, required >=1.7.0')
|
||||||
|
libsndio = dependency('', required: false)
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
gtk_layer_shell = dependency('gtk-layer-shell-0',
|
gtk_layer_shell = dependency('gtk-layer-shell-0',
|
||||||
required: get_option('gtk-layer-shell'),
|
required: get_option('gtk-layer-shell'),
|
||||||
fallback : ['gtk-layer-shell', 'gtk_layer_shell_dep'])
|
fallback : ['gtk-layer-shell', 'gtk_layer_shell_dep'])
|
||||||
|
@ -137,12 +152,10 @@ if is_linux
|
||||||
add_project_arguments('-DHAVE_MEMORY_LINUX', language: 'cpp')
|
add_project_arguments('-DHAVE_MEMORY_LINUX', language: 'cpp')
|
||||||
src_files += files(
|
src_files += files(
|
||||||
'src/modules/battery.cpp',
|
'src/modules/battery.cpp',
|
||||||
'src/modules/bluetooth.cpp',
|
|
||||||
'src/modules/cpu/common.cpp',
|
'src/modules/cpu/common.cpp',
|
||||||
'src/modules/cpu/linux.cpp',
|
'src/modules/cpu/linux.cpp',
|
||||||
'src/modules/memory/common.cpp',
|
'src/modules/memory/common.cpp',
|
||||||
'src/modules/memory/linux.cpp',
|
'src/modules/memory/linux.cpp',
|
||||||
'src/util/rfkill.cpp'
|
|
||||||
)
|
)
|
||||||
elif is_dragonfly or is_freebsd or is_netbsd or is_openbsd
|
elif is_dragonfly or is_freebsd or is_netbsd or is_openbsd
|
||||||
add_project_arguments('-DHAVE_CPU_BSD', language: 'cpp')
|
add_project_arguments('-DHAVE_CPU_BSD', language: 'cpp')
|
||||||
|
@ -207,6 +220,21 @@ if gtk_layer_shell.found()
|
||||||
add_project_arguments('-DHAVE_GTK_LAYER_SHELL', language: 'cpp')
|
add_project_arguments('-DHAVE_GTK_LAYER_SHELL', language: 'cpp')
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
if libsndio.found()
|
||||||
|
add_project_arguments('-DHAVE_LIBSNDIO', language: 'cpp')
|
||||||
|
src_files += 'src/modules/sndio.cpp'
|
||||||
|
endif
|
||||||
|
|
||||||
|
if get_option('rfkill').enabled()
|
||||||
|
if is_linux
|
||||||
|
add_project_arguments('-DWANT_RFKILL', language: 'cpp')
|
||||||
|
src_files += files(
|
||||||
|
'src/modules/bluetooth.cpp',
|
||||||
|
'src/util/rfkill.cpp'
|
||||||
|
)
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
subdir('protocol')
|
subdir('protocol')
|
||||||
|
|
||||||
executable(
|
executable(
|
||||||
|
@ -231,6 +259,7 @@ executable(
|
||||||
libepoll,
|
libepoll,
|
||||||
libmpdclient,
|
libmpdclient,
|
||||||
gtk_layer_shell,
|
gtk_layer_shell,
|
||||||
|
libsndio,
|
||||||
tz_dep
|
tz_dep
|
||||||
],
|
],
|
||||||
include_directories: [include_directories('include')],
|
include_directories: [include_directories('include')],
|
||||||
|
@ -248,9 +277,20 @@ scdoc = dependency('scdoc', version: '>=1.9.2', native: true, required: get_opti
|
||||||
if scdoc.found()
|
if scdoc.found()
|
||||||
scdoc_prog = find_program(scdoc.get_pkgconfig_variable('scdoc'), native: true)
|
scdoc_prog = find_program(scdoc.get_pkgconfig_variable('scdoc'), native: true)
|
||||||
sh = find_program('sh', native: true)
|
sh = find_program('sh', native: true)
|
||||||
|
|
||||||
|
main_manpage = configure_file(
|
||||||
|
input: 'man/waybar.5.scd.in',
|
||||||
|
output: 'waybar.5.scd',
|
||||||
|
configuration: {
|
||||||
|
'sysconfdir': join_paths(prefix, sysconfdir)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
main_manpage_path = join_paths(meson.build_root(), '@0@'.format(main_manpage))
|
||||||
|
|
||||||
mandir = get_option('mandir')
|
mandir = get_option('mandir')
|
||||||
man_files = [
|
man_files = [
|
||||||
'waybar.5.scd',
|
main_manpage_path,
|
||||||
'waybar-backlight.5.scd',
|
'waybar-backlight.5.scd',
|
||||||
'waybar-battery.5.scd',
|
'waybar-battery.5.scd',
|
||||||
'waybar-clock.5.scd',
|
'waybar-clock.5.scd',
|
||||||
|
@ -271,16 +311,21 @@ if scdoc.found()
|
||||||
'waybar-states.5.scd',
|
'waybar-states.5.scd',
|
||||||
'waybar-wlr-taskbar.5.scd',
|
'waybar-wlr-taskbar.5.scd',
|
||||||
'waybar-bluetooth.5.scd',
|
'waybar-bluetooth.5.scd',
|
||||||
|
'waybar-sndio.5.scd',
|
||||||
]
|
]
|
||||||
|
|
||||||
foreach filename : man_files
|
foreach file : man_files
|
||||||
topic = filename.split('.')[-3].split('/')[-1]
|
path = '@0@'.format(file)
|
||||||
section = filename.split('.')[-2]
|
basename = fs.name(path)
|
||||||
|
|
||||||
|
topic = basename.split('.')[-3].split('/')[-1]
|
||||||
|
section = basename.split('.')[-2]
|
||||||
output = '@0@.@1@'.format(topic, section)
|
output = '@0@.@1@'.format(topic, section)
|
||||||
|
|
||||||
custom_target(
|
custom_target(
|
||||||
output,
|
output,
|
||||||
input: 'man/@0@'.format(filename),
|
# drops the 'man' if `path` is an absolute path
|
||||||
|
input: join_paths('man', path),
|
||||||
output: output,
|
output: output,
|
||||||
command: [
|
command: [
|
||||||
sh, '-c', '@0@ < @INPUT@ > @1@'.format(scdoc_prog.path(), output)
|
sh, '-c', '@0@ < @INPUT@ > @1@'.format(scdoc_prog.path(), output)
|
||||||
|
|
|
@ -7,3 +7,5 @@ option('dbusmenu-gtk', type: 'feature', value: 'auto', description: 'Enable supp
|
||||||
option('man-pages', type: 'feature', value: 'auto', description: 'Generate and install man pages')
|
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('mpd', type: 'feature', value: 'auto', description: 'Enable support for the Music Player Daemon')
|
||||||
option('gtk-layer-shell', type: 'feature', value: 'auto', description: 'Use gtk-layer-shell library for popups support')
|
option('gtk-layer-shell', type: 'feature', value: 'auto', description: 'Use gtk-layer-shell library for popups support')
|
||||||
|
option('rfkill', type: 'feature', value: 'auto', description: 'Enable support for RFKILL')
|
||||||
|
option('sndio', type: 'feature', value: 'auto', description: 'Enable support for sndio')
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
THIS SOFTWARE.
|
THIS SOFTWARE.
|
||||||
</copyright>
|
</copyright>
|
||||||
|
|
||||||
<interface name="zwlr_layer_shell_v1" version="1">
|
<interface name="zwlr_layer_shell_v1" version="3">
|
||||||
<description summary="create surfaces that are layers of the desktop">
|
<description summary="create surfaces that are layers of the desktop">
|
||||||
Clients can use this interface to assign the surface_layer role to
|
Clients can use this interface to assign the surface_layer role to
|
||||||
wl_surfaces. Such surfaces are assigned to a "layer" of the output and
|
wl_surfaces. Such surfaces are assigned to a "layer" of the output and
|
||||||
|
@ -82,17 +82,27 @@
|
||||||
<entry name="top" value="2"/>
|
<entry name="top" value="2"/>
|
||||||
<entry name="overlay" value="3"/>
|
<entry name="overlay" value="3"/>
|
||||||
</enum>
|
</enum>
|
||||||
|
|
||||||
|
<!-- Version 3 additions -->
|
||||||
|
|
||||||
|
<request name="destroy" type="destructor" since="3">
|
||||||
|
<description summary="destroy the layer_shell object">
|
||||||
|
This request indicates that the client will not use the layer_shell
|
||||||
|
object any more. Objects that have been created through this instance
|
||||||
|
are not affected.
|
||||||
|
</description>
|
||||||
|
</request>
|
||||||
</interface>
|
</interface>
|
||||||
|
|
||||||
<interface name="zwlr_layer_surface_v1" version="1">
|
<interface name="zwlr_layer_surface_v1" version="3">
|
||||||
<description summary="layer metadata interface">
|
<description summary="layer metadata interface">
|
||||||
An interface that may be implemented by a wl_surface, for surfaces that
|
An interface that may be implemented by a wl_surface, for surfaces that
|
||||||
are designed to be rendered as a layer of a stacked desktop-like
|
are designed to be rendered as a layer of a stacked desktop-like
|
||||||
environment.
|
environment.
|
||||||
|
|
||||||
Layer surface state (size, anchor, exclusive zone, margin, interactivity)
|
Layer surface state (layer, size, anchor, exclusive zone,
|
||||||
is double-buffered, and will be applied at the time wl_surface.commit of
|
margin, interactivity) is double-buffered, and will be applied at the
|
||||||
the corresponding wl_surface is called.
|
time wl_surface.commit of the corresponding wl_surface is called.
|
||||||
</description>
|
</description>
|
||||||
|
|
||||||
<request name="set_size">
|
<request name="set_size">
|
||||||
|
@ -115,7 +125,7 @@
|
||||||
<request name="set_anchor">
|
<request name="set_anchor">
|
||||||
<description summary="configures the anchor point of the surface">
|
<description summary="configures the anchor point of the surface">
|
||||||
Requests that the compositor anchor the surface to the specified edges
|
Requests that the compositor anchor the surface to the specified edges
|
||||||
and corners. If two orthoginal edges are specified (e.g. 'top' and
|
and corners. If two orthogonal edges are specified (e.g. 'top' and
|
||||||
'left'), then the anchor point will be the intersection of the edges
|
'left'), then the anchor point will be the intersection of the edges
|
||||||
(e.g. the top left corner of the output); otherwise the anchor point
|
(e.g. the top left corner of the output); otherwise the anchor point
|
||||||
will be centered on that edge, or in the center if none is specified.
|
will be centered on that edge, or in the center if none is specified.
|
||||||
|
@ -127,20 +137,25 @@
|
||||||
|
|
||||||
<request name="set_exclusive_zone">
|
<request name="set_exclusive_zone">
|
||||||
<description summary="configures the exclusive geometry of this surface">
|
<description summary="configures the exclusive geometry of this surface">
|
||||||
Requests that the compositor avoids occluding an area of the surface
|
Requests that the compositor avoids occluding an area with other
|
||||||
with other surfaces. The compositor's use of this information is
|
surfaces. The compositor's use of this information is
|
||||||
implementation-dependent - do not assume that this region will not
|
implementation-dependent - do not assume that this region will not
|
||||||
actually be occluded.
|
actually be occluded.
|
||||||
|
|
||||||
A positive value is only meaningful if the surface is anchored to an
|
A positive value is only meaningful if the surface is anchored to one
|
||||||
edge, rather than a corner. The zone is the number of surface-local
|
edge or an edge and both perpendicular edges. If the surface is not
|
||||||
coordinates from the edge that are considered exclusive.
|
anchored, anchored to only two perpendicular edges (a corner), anchored
|
||||||
|
to only two parallel edges or anchored to all edges, a positive value
|
||||||
|
will be treated the same as zero.
|
||||||
|
|
||||||
|
A positive zone is the distance from the edge in surface-local
|
||||||
|
coordinates to consider exclusive.
|
||||||
|
|
||||||
Surfaces that do not wish to have an exclusive zone may instead specify
|
Surfaces that do not wish to have an exclusive zone may instead specify
|
||||||
how they should interact with surfaces that do. If set to zero, the
|
how they should interact with surfaces that do. If set to zero, the
|
||||||
surface indicates that it would like to be moved to avoid occluding
|
surface indicates that it would like to be moved to avoid occluding
|
||||||
surfaces with a positive excluzive zone. If set to -1, the surface
|
surfaces with a positive exclusive zone. If set to -1, the surface
|
||||||
indicates that it would not like to be moved to accomodate for other
|
indicates that it would not like to be moved to accommodate for other
|
||||||
surfaces, and the compositor should extend it all the way to the edges
|
surfaces, and the compositor should extend it all the way to the edges
|
||||||
it is anchored to.
|
it is anchored to.
|
||||||
|
|
||||||
|
@ -281,5 +296,16 @@
|
||||||
<entry name="left" value="4" summary="the left edge of the anchor rectangle"/>
|
<entry name="left" value="4" summary="the left edge of the anchor rectangle"/>
|
||||||
<entry name="right" value="8" summary="the right edge of the anchor rectangle"/>
|
<entry name="right" value="8" summary="the right edge of the anchor rectangle"/>
|
||||||
</enum>
|
</enum>
|
||||||
|
|
||||||
|
<!-- Version 2 additions -->
|
||||||
|
|
||||||
|
<request name="set_layer" since="2">
|
||||||
|
<description summary="change the layer of the surface">
|
||||||
|
Change the layer that the surface is rendered on.
|
||||||
|
|
||||||
|
Layer is double-buffered, see wl_surface.commit.
|
||||||
|
</description>
|
||||||
|
<arg name="layer" type="uint" enum="zwlr_layer_shell_v1.layer" summary="layer to move this surface to"/>
|
||||||
|
</request>
|
||||||
</interface>
|
</interface>
|
||||||
</protocol>
|
</protocol>
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
"format": "<span style=\"italic\">{}</span>"
|
"format": "<span style=\"italic\">{}</span>"
|
||||||
},
|
},
|
||||||
"mpd": {
|
"mpd": {
|
||||||
"format": "{stateIcon} {consumeIcon}{randomIcon}{repeatIcon}{singleIcon}{artist} - {album} - {title} ({elapsedTime:%M:%S}/{totalTime:%M:%S}) ",
|
"format": "{stateIcon} {consumeIcon}{randomIcon}{repeatIcon}{singleIcon}{artist} - {album} - {title} ({elapsedTime:%M:%S}/{totalTime:%M:%S}) ⸨{songPosition}|{queueLength}⸩ ",
|
||||||
"format-disconnected": "Disconnected ",
|
"format-disconnected": "Disconnected ",
|
||||||
"format-stopped": "{consumeIcon}{randomIcon}{repeatIcon}{singleIcon}Stopped ",
|
"format-stopped": "{consumeIcon}{randomIcon}{repeatIcon}{singleIcon}Stopped ",
|
||||||
"unknown-tag": "N/A",
|
"unknown-tag": "N/A",
|
||||||
|
|
|
@ -41,19 +41,19 @@ window#waybar.chromium {
|
||||||
padding: 0 5px;
|
padding: 0 5px;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
border-bottom: 3px solid transparent;
|
/* Use box-shadow instead of border so the text isn't offset */
|
||||||
|
box-shadow: inset 0 -3px transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 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 */
|
||||||
#workspaces button:hover {
|
#workspaces button:hover {
|
||||||
background: rgba(0, 0, 0, 0.2);
|
background: rgba(0, 0, 0, 0.2);
|
||||||
box-shadow: inherit;
|
box-shadow: inset 0 -3px #ffffff;
|
||||||
border-bottom: 3px solid #ffffff;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#workspaces button.focused {
|
#workspaces button.focused {
|
||||||
background-color: #64727D;
|
background-color: #64727D;
|
||||||
border-bottom: 3px solid #ffffff;
|
box-shadow: inset 0 -3px #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
#workspaces button.urgent {
|
#workspaces button.urgent {
|
||||||
|
|
|
@ -1,13 +1,11 @@
|
||||||
[Unit]
|
[Unit]
|
||||||
Description=Highly customizable Wayland bar for Sway and Wlroots based compositors.
|
Description=Highly customizable Wayland bar for Sway and Wlroots based compositors.
|
||||||
Documentation=https://github.com/Alexays/Waybar/wiki/
|
Documentation=https://github.com/Alexays/Waybar/wiki/
|
||||||
PartOf=wayland-session.target
|
PartOf=graphical-session.target
|
||||||
After=wayland-session.target
|
After=graphical-session.target
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=dbus
|
|
||||||
BusName=fr.arouillard.waybar
|
|
||||||
ExecStart=@prefix@/bin/waybar
|
ExecStart=@prefix@/bin/waybar
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=wayland-session.target
|
WantedBy=graphical-session.target
|
||||||
|
|
|
@ -5,8 +5,9 @@
|
||||||
namespace waybar {
|
namespace waybar {
|
||||||
|
|
||||||
ALabel::ALabel(const Json::Value& config, const std::string& name, const std::string& id,
|
ALabel::ALabel(const Json::Value& config, const std::string& name, const std::string& id,
|
||||||
const std::string& format, uint16_t interval, bool ellipsize)
|
const std::string& format, uint16_t interval, bool ellipsize, bool enable_click,
|
||||||
: AModule(config, name, id, config["format-alt"].isString()),
|
bool enable_scroll)
|
||||||
|
: AModule(config, name, id, config["format-alt"].isString() || enable_click, enable_scroll),
|
||||||
format_(config_["format"].isString() ? config_["format"].asString() : format),
|
format_(config_["format"].isString() ? config_["format"].asString() : format),
|
||||||
interval_(config_["interval"] == "once"
|
interval_(config_["interval"] == "once"
|
||||||
? std::chrono::seconds(100000000)
|
? std::chrono::seconds(100000000)
|
||||||
|
@ -21,8 +22,10 @@ ALabel::ALabel(const Json::Value& config, const std::string& name, const std::st
|
||||||
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);
|
||||||
|
label_.set_single_line_mode(true);
|
||||||
} else if (ellipsize && label_.get_max_width_chars() == -1) {
|
} else if (ellipsize && label_.get_max_width_chars() == -1) {
|
||||||
label_.set_ellipsize(Pango::EllipsizeMode::ELLIPSIZE_END);
|
label_.set_ellipsize(Pango::EllipsizeMode::ELLIPSIZE_END);
|
||||||
|
label_.set_single_line_mode(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config_["rotate"].isUInt()) {
|
if (config_["rotate"].isUInt()) {
|
||||||
|
|
|
@ -44,9 +44,9 @@ bool AModule::handleToggle(GdkEventButton* const& e) {
|
||||||
format = config_["on-click-middle"].asString();
|
format = 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) {
|
||||||
format = config_["on-click-right"].asString();
|
format = config_["on-click-right"].asString();
|
||||||
} else if (config_["on-click-forward"].isString() && e->button == 8) {
|
} else if (config_["on-click-backward"].isString() && e->button == 8) {
|
||||||
format = config_["on-click-backward"].asString();
|
format = config_["on-click-backward"].asString();
|
||||||
} else if (config_["on-click-backward"].isString() && e->button == 9) {
|
} else if (config_["on-click-forward"].isString() && e->button == 9) {
|
||||||
format = config_["on-click-forward"].asString();
|
format = config_["on-click-forward"].asString();
|
||||||
}
|
}
|
||||||
if (!format.empty()) {
|
if (!format.empty()) {
|
||||||
|
|
122
src/bar.cpp
122
src/bar.cpp
|
@ -24,6 +24,9 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config)
|
||||||
window.get_style_context()->add_class(output->name);
|
window.get_style_context()->add_class(output->name);
|
||||||
window.get_style_context()->add_class(config["name"].asString());
|
window.get_style_context()->add_class(config["name"].asString());
|
||||||
window.get_style_context()->add_class(config["position"].asString());
|
window.get_style_context()->add_class(config["position"].asString());
|
||||||
|
left_.get_style_context()->add_class("modules-left");
|
||||||
|
center_.get_style_context()->add_class("modules-center");
|
||||||
|
right_.get_style_context()->add_class("modules-right");
|
||||||
|
|
||||||
if (config["position"] == "right" || config["position"] == "left") {
|
if (config["position"] == "right" || config["position"] == "left") {
|
||||||
height_ = 0;
|
height_ = 0;
|
||||||
|
@ -101,56 +104,25 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config)
|
||||||
use_gls_ = config["gtk-layer-shell"].isBool() ? config["gtk-layer-shell"].asBool() : true;
|
use_gls_ = config["gtk-layer-shell"].isBool() ? config["gtk-layer-shell"].asBool() : true;
|
||||||
if (use_gls_) {
|
if (use_gls_) {
|
||||||
initGtkLayerShell();
|
initGtkLayerShell();
|
||||||
|
window.signal_map_event().connect_notify(sigc::mem_fun(*this, &Bar::onMapGLS));
|
||||||
|
window.signal_configure_event().connect_notify(sigc::mem_fun(*this, &Bar::onConfigureGLS));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (!use_gls_) {
|
||||||
window.signal_realize().connect_notify(sigc::mem_fun(*this, &Bar::onRealize));
|
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_map_event().connect_notify(sigc::mem_fun(*this, &Bar::onMap));
|
||||||
window.signal_configure_event().connect_notify(sigc::mem_fun(*this, &Bar::onConfigure));
|
window.signal_configure_event().connect_notify(sigc::mem_fun(*this, &Bar::onConfigure));
|
||||||
|
}
|
||||||
window.set_size_request(width_, height_);
|
window.set_size_request(width_, height_);
|
||||||
setupWidgets();
|
setupWidgets();
|
||||||
|
|
||||||
if (window.get_realized()) {
|
if (!use_gls_ && window.get_realized()) {
|
||||||
onRealize();
|
onRealize();
|
||||||
}
|
}
|
||||||
window.show_all();
|
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
|
#ifdef HAVE_GTK_LAYER_SHELL
|
||||||
void waybar::Bar::initGtkLayerShell() {
|
void waybar::Bar::initGtkLayerShell() {
|
||||||
auto gtk_window = window.gobj();
|
auto gtk_window = window.gobj();
|
||||||
|
@ -181,8 +153,80 @@ void waybar::Bar::initGtkLayerShell() {
|
||||||
setExclusiveZone(width_, height_);
|
setExclusiveZone(width_, height_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void waybar::Bar::onConfigureGLS(GdkEventConfigure* ev) {
|
||||||
|
/*
|
||||||
|
* GTK wants new size for the window.
|
||||||
|
* Actual resizing is done within the gtk-layer-shell code; the only remaining action is to apply
|
||||||
|
* exclusive zone.
|
||||||
|
* gtk_layer_auto_exclusive_zone_enable() could handle even that, but at the cost of ignoring
|
||||||
|
* margins on unanchored edge.
|
||||||
|
*
|
||||||
|
* Note: forced resizing to a window smaller than required by GTK would not work with
|
||||||
|
* gtk-layer-shell.
|
||||||
|
*/
|
||||||
|
if (vertical) {
|
||||||
|
if (width_ > 1 && ev->width > static_cast<int>(width_)) {
|
||||||
|
spdlog::warn(MIN_WIDTH_MSG, width_, ev->width);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!vertical && height_ > 1 && ev->height > static_cast<int>(height_)) {
|
||||||
|
spdlog::warn(MIN_HEIGHT_MSG, height_, ev->height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
width_ = ev->width;
|
||||||
|
height_ = ev->height;
|
||||||
|
spdlog::info(BAR_SIZE_MSG, width_, height_, output->name);
|
||||||
|
setExclusiveZone(width_, height_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void waybar::Bar::onMapGLS(GdkEventAny* ev) {
|
||||||
|
/*
|
||||||
|
* Obtain a pointer to the custom layer surface for modules that require it (idle_inhibitor).
|
||||||
|
*/
|
||||||
|
auto gdk_window = window.get_window();
|
||||||
|
surface = gdk_wayland_window_get_wl_surface(gdk_window->gobj());
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void waybar::Bar::onConfigure(GdkEventConfigure* ev) {
|
||||||
|
/*
|
||||||
|
* GTK wants new size for the window.
|
||||||
|
*
|
||||||
|
* Prefer configured size if it's non-default.
|
||||||
|
* If the size is not set and the window is smaller than requested by GTK, request resize from
|
||||||
|
* layer surface.
|
||||||
|
*/
|
||||||
|
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_) {
|
||||||
|
setSurfaceSize(tmp_width, tmp_height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void waybar::Bar::onRealize() {
|
void waybar::Bar::onRealize() {
|
||||||
auto gdk_window = window.get_window()->gobj();
|
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);
|
||||||
|
@ -192,10 +236,6 @@ void waybar::Bar::onMap(GdkEventAny* ev) {
|
||||||
auto gdk_window = window.get_window()->gobj();
|
auto gdk_window = window.get_window()->gobj();
|
||||||
surface = gdk_wayland_window_get_wl_surface(gdk_window);
|
surface = gdk_wayland_window_get_wl_surface(gdk_window);
|
||||||
|
|
||||||
if (use_gls_) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto client = waybar::Client::inst();
|
auto client = waybar::Client::inst();
|
||||||
// owned by output->monitor; no need to destroy
|
// owned by output->monitor; no need to destroy
|
||||||
auto wl_output = gdk_wayland_monitor_get_wl_output(output->monitor->gobj());
|
auto wl_output = gdk_wayland_monitor_get_wl_output(output->monitor->gobj());
|
||||||
|
@ -362,8 +402,10 @@ auto waybar::Bar::toggle() -> void {
|
||||||
window.set_opacity(1);
|
window.set_opacity(1);
|
||||||
}
|
}
|
||||||
setExclusiveZone(width_, height_);
|
setExclusiveZone(width_, height_);
|
||||||
|
if (!use_gls_) {
|
||||||
wl_surface_commit(surface);
|
wl_surface_commit(surface);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void waybar::Bar::getModules(const Factory& factory, const std::string& pos) {
|
void waybar::Bar::getModules(const Factory& factory, const std::string& pos) {
|
||||||
if (config[pos].isArray()) {
|
if (config[pos].isArray()) {
|
||||||
|
|
|
@ -162,6 +162,7 @@ std::tuple<const std::string, const std::string> waybar::Client::getConfigs(
|
||||||
"$XDG_CONFIG_HOME/waybar/config",
|
"$XDG_CONFIG_HOME/waybar/config",
|
||||||
"$HOME/.config/waybar/config",
|
"$HOME/.config/waybar/config",
|
||||||
"$HOME/waybar/config",
|
"$HOME/waybar/config",
|
||||||
|
"/etc/xdg/waybar/config",
|
||||||
SYSCONFDIR "/xdg/waybar/config",
|
SYSCONFDIR "/xdg/waybar/config",
|
||||||
"./resources/config",
|
"./resources/config",
|
||||||
})
|
})
|
||||||
|
@ -170,6 +171,7 @@ std::tuple<const std::string, const std::string> waybar::Client::getConfigs(
|
||||||
"$XDG_CONFIG_HOME/waybar/style.css",
|
"$XDG_CONFIG_HOME/waybar/style.css",
|
||||||
"$HOME/.config/waybar/style.css",
|
"$HOME/.config/waybar/style.css",
|
||||||
"$HOME/waybar/style.css",
|
"$HOME/waybar/style.css",
|
||||||
|
"/etc/xdg/waybar/style.css",
|
||||||
SYSCONFDIR "/xdg/waybar/style.css",
|
SYSCONFDIR "/xdg/waybar/style.css",
|
||||||
"./resources/style.css",
|
"./resources/style.css",
|
||||||
})
|
})
|
||||||
|
|
|
@ -76,14 +76,21 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const {
|
||||||
if (ref == "mpd") {
|
if (ref == "mpd") {
|
||||||
return new waybar::modules::MPD(id, config_[name]);
|
return new waybar::modules::MPD(id, config_[name]);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_LIBSNDIO
|
||||||
|
if (ref == "sndio") {
|
||||||
|
return new waybar::modules::Sndio(id, config_[name]);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
if (ref == "temperature") {
|
if (ref == "temperature") {
|
||||||
return new waybar::modules::Temperature(id, config_[name]);
|
return new waybar::modules::Temperature(id, config_[name]);
|
||||||
}
|
}
|
||||||
#if defined(__linux__)
|
#if defined(__linux__)
|
||||||
|
# ifdef WANT_RFKILL
|
||||||
if (ref == "bluetooth") {
|
if (ref == "bluetooth") {
|
||||||
return new waybar::modules::Bluetooth(id, config_[name]);
|
return new waybar::modules::Bluetooth(id, config_[name]);
|
||||||
}
|
}
|
||||||
|
# endif
|
||||||
#endif
|
#endif
|
||||||
if (ref.compare(0, 7, "custom/") == 0 && ref.size() > 7) {
|
if (ref.compare(0, 7, "custom/") == 0 && ref.size() > 7) {
|
||||||
return new waybar::modules::Custom(ref.substr(7), id, config_[name]);
|
return new waybar::modules::Custom(ref.substr(7), id, config_[name]);
|
||||||
|
|
66
src/main.cpp
66
src/main.cpp
|
@ -1,7 +1,72 @@
|
||||||
#include <csignal>
|
#include <csignal>
|
||||||
|
#include <list>
|
||||||
|
#include <mutex>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
#include "client.hpp"
|
#include "client.hpp"
|
||||||
|
|
||||||
|
std::mutex reap_mtx;
|
||||||
|
std::list<pid_t> reap;
|
||||||
|
|
||||||
|
void* signalThread(void* args) {
|
||||||
|
int err, signum;
|
||||||
|
sigset_t mask;
|
||||||
|
sigemptyset(&mask);
|
||||||
|
sigaddset(&mask, SIGCHLD);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
err = sigwait(&mask, &signum);
|
||||||
|
if (err != 0) {
|
||||||
|
spdlog::error("sigwait failed: {}", strerror(errno));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (signum) {
|
||||||
|
case SIGCHLD:
|
||||||
|
spdlog::debug("Received SIGCHLD in signalThread");
|
||||||
|
if (!reap.empty()) {
|
||||||
|
reap_mtx.lock();
|
||||||
|
for (auto it = reap.begin(); it != reap.end(); ++it) {
|
||||||
|
if (waitpid(*it, nullptr, WNOHANG) == *it) {
|
||||||
|
spdlog::debug("Reaped child with PID: {}", *it);
|
||||||
|
it = reap.erase(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reap_mtx.unlock();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
spdlog::debug("Received signal with number {}, but not handling",
|
||||||
|
signum);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void startSignalThread(void) {
|
||||||
|
int err;
|
||||||
|
sigset_t mask;
|
||||||
|
sigemptyset(&mask);
|
||||||
|
sigaddset(&mask, SIGCHLD);
|
||||||
|
|
||||||
|
// Block SIGCHLD so it can be handled by the signal thread
|
||||||
|
// Any threads created by this one (the main thread) should not
|
||||||
|
// modify their signal mask to unblock SIGCHLD
|
||||||
|
err = pthread_sigmask(SIG_BLOCK, &mask, nullptr);
|
||||||
|
if (err != 0) {
|
||||||
|
spdlog::error("pthread_sigmask failed in startSignalThread: {}", strerror(err));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_t thread_id;
|
||||||
|
err = pthread_create(&thread_id, nullptr, signalThread, nullptr);
|
||||||
|
if (err != 0) {
|
||||||
|
spdlog::error("pthread_create failed in startSignalThread: {}", strerror(err));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
try {
|
try {
|
||||||
auto client = waybar::Client::inst();
|
auto client = waybar::Client::inst();
|
||||||
|
@ -18,6 +83,7 @@ int main(int argc, char* argv[]) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
startSignalThread();
|
||||||
|
|
||||||
auto ret = client->main(argc, argv);
|
auto ret = client->main(argc, argv);
|
||||||
delete client;
|
delete client;
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
using waybar::modules::waybar_time;
|
using waybar::modules::waybar_time;
|
||||||
|
|
||||||
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, "clock", id, "{:%H:%M}", 60), fixed_time_zone_(false) {
|
: ALabel(config, "clock", id, "{:%H:%M}", 60, false, false, true), fixed_time_zone_(false) {
|
||||||
if (config_["timezone"].isString()) {
|
if (config_["timezone"].isString()) {
|
||||||
spdlog::warn("As using a timezone, some format args may be missing as the date library havn't got a release since 2018.");
|
spdlog::warn("As using a timezone, some format args may be missing as the date library havn't got a release since 2018.");
|
||||||
time_zone_ = date::locate_zone(config_["timezone"].asString());
|
time_zone_ = date::locate_zone(config_["timezone"].asString());
|
||||||
|
@ -71,6 +71,40 @@ auto waybar::modules::Clock::update() -> void {
|
||||||
ALabel::update();
|
ALabel::update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool waybar::modules::Clock::handleScroll(GdkEventScroll *e) {
|
||||||
|
// defer to user commands if set
|
||||||
|
if (config_["on-scroll-up"].isString() || config_["on-scroll-down"].isString()) {
|
||||||
|
return AModule::handleScroll(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto dir = AModule::getScrollDir(e);
|
||||||
|
if (dir != SCROLL_DIR::UP && dir != SCROLL_DIR::DOWN) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!config_["timezones"].isArray() || config_["timezones"].empty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
auto nr_zones = config_["timezones"].size();
|
||||||
|
int new_idx = time_zone_idx_ + ((dir == SCROLL_DIR::UP) ? 1 : -1);
|
||||||
|
if (new_idx < 0) {
|
||||||
|
time_zone_idx_ = nr_zones - 1;
|
||||||
|
} else if (new_idx >= nr_zones) {
|
||||||
|
time_zone_idx_ = 0;
|
||||||
|
} else {
|
||||||
|
time_zone_idx_ = new_idx;
|
||||||
|
}
|
||||||
|
auto zone_name = config_["timezones"][time_zone_idx_];
|
||||||
|
if (!zone_name.isString() || zone_name.empty()) {
|
||||||
|
fixed_time_zone_ = false;
|
||||||
|
} else {
|
||||||
|
time_zone_ = date::locate_zone(zone_name.asString());
|
||||||
|
fixed_time_zone_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
update();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
auto waybar::modules::Clock::calendar_text(const waybar_time& wtime) -> std::string {
|
auto waybar::modules::Clock::calendar_text(const waybar_time& wtime) -> std::string {
|
||||||
const auto daypoint = date::floor<date::days>(wtime.ztime.get_local_time());
|
const auto daypoint = date::floor<date::days>(wtime.ztime.get_local_time());
|
||||||
const auto ymd = date::year_month_day(daypoint);
|
const auto ymd = date::year_month_day(daypoint);
|
||||||
|
@ -99,7 +133,12 @@ auto waybar::modules::Clock::calendar_text(const waybar_time& wtime) -> std::str
|
||||||
os << '\n';
|
os << '\n';
|
||||||
}
|
}
|
||||||
if (d == curr_day) {
|
if (d == curr_day) {
|
||||||
|
if (config_["today-format"].isString()) {
|
||||||
|
auto today_format = config_["today-format"].asString();
|
||||||
|
os << fmt::format(today_format, date::format("%e", d));
|
||||||
|
} else {
|
||||||
os << "<b><u>" << date::format("%e", d) << "</u></b>";
|
os << "<b><u>" << date::format("%e", d) << "</u></b>";
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
os << date::format("%e", d);
|
os << date::format("%e", d);
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,7 +50,6 @@ void waybar::modules::Custom::continuousWorker() {
|
||||||
thread_ = [this, cmd] {
|
thread_ = [this, cmd] {
|
||||||
char* buff = nullptr;
|
char* buff = nullptr;
|
||||||
size_t len = 0;
|
size_t len = 0;
|
||||||
bool restart = false;
|
|
||||||
if (getline(&buff, &len, fp_) == -1) {
|
if (getline(&buff, &len, fp_) == -1) {
|
||||||
int exit_code = 1;
|
int exit_code = 1;
|
||||||
if (fp_) {
|
if (fp_) {
|
||||||
|
@ -63,8 +62,8 @@ void waybar::modules::Custom::continuousWorker() {
|
||||||
spdlog::error("{} stopped unexpectedly, is it endless?", name_);
|
spdlog::error("{} stopped unexpectedly, is it endless?", name_);
|
||||||
}
|
}
|
||||||
if (config_["restart-interval"].isUInt()) {
|
if (config_["restart-interval"].isUInt()) {
|
||||||
restart = true;
|
|
||||||
pid_ = -1;
|
pid_ = -1;
|
||||||
|
thread_.sleep_for(std::chrono::seconds(config_["restart-interval"].asUInt()));
|
||||||
fp_ = util::command::open(cmd, pid_);
|
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);
|
||||||
|
@ -83,9 +82,6 @@ void waybar::modules::Custom::continuousWorker() {
|
||||||
output_ = {0, output};
|
output_ = {0, output};
|
||||||
dp.emit();
|
dp.emit();
|
||||||
}
|
}
|
||||||
if (restart) {
|
|
||||||
thread_.sleep_for(std::chrono::seconds(config_["restart-interval"].asUInt()));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,15 +91,21 @@ void waybar::modules::Custom::refresh(int sig) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void waybar::modules::Custom::handleEvent() {
|
||||||
|
if (!config_["exec-on-event"].isBool() || config_["exec-on-event"].asBool()) {
|
||||||
|
thread_.wake_up();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool waybar::modules::Custom::handleScroll(GdkEventScroll* e) {
|
bool waybar::modules::Custom::handleScroll(GdkEventScroll* e) {
|
||||||
auto ret = ALabel::handleScroll(e);
|
auto ret = ALabel::handleScroll(e);
|
||||||
thread_.wake_up();
|
handleEvent();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool waybar::modules::Custom::handleToggle(GdkEventButton* const& e) {
|
bool waybar::modules::Custom::handleToggle(GdkEventButton* const& e) {
|
||||||
auto ret = ALabel::handleToggle(e);
|
auto ret = ALabel::handleToggle(e);
|
||||||
thread_.wake_up();
|
handleEvent();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,13 +47,14 @@ auto waybar::modules::Disk::update() -> void {
|
||||||
auto free = pow_format(stats.f_bavail * stats.f_frsize, "B", true);
|
auto free = pow_format(stats.f_bavail * stats.f_frsize, "B", true);
|
||||||
auto used = pow_format((stats.f_blocks - stats.f_bavail) * stats.f_frsize, "B", true);
|
auto used = pow_format((stats.f_blocks - stats.f_bavail) * stats.f_frsize, "B", true);
|
||||||
auto total = pow_format(stats.f_blocks * stats.f_frsize, "B", true);
|
auto total = pow_format(stats.f_blocks * stats.f_frsize, "B", true);
|
||||||
|
auto percentage_used = (stats.f_blocks - stats.f_bavail) * 100 / stats.f_blocks;
|
||||||
|
|
||||||
label_.set_markup(fmt::format(format_
|
label_.set_markup(fmt::format(format_
|
||||||
, stats.f_bavail * 100 / stats.f_blocks
|
, stats.f_bavail * 100 / stats.f_blocks
|
||||||
, fmt::arg("free", free)
|
, fmt::arg("free", free)
|
||||||
, fmt::arg("percentage_free", stats.f_bavail * 100 / stats.f_blocks)
|
, fmt::arg("percentage_free", stats.f_bavail * 100 / stats.f_blocks)
|
||||||
, fmt::arg("used", used)
|
, fmt::arg("used", used)
|
||||||
, fmt::arg("percentage_used", (stats.f_blocks - stats.f_bavail) * 100 / stats.f_blocks)
|
, fmt::arg("percentage_used", percentage_used)
|
||||||
, fmt::arg("total", total)
|
, fmt::arg("total", total)
|
||||||
, fmt::arg("path", path_)
|
, fmt::arg("path", path_)
|
||||||
));
|
));
|
||||||
|
@ -67,12 +68,13 @@ auto waybar::modules::Disk::update() -> void {
|
||||||
, fmt::arg("free", free)
|
, fmt::arg("free", free)
|
||||||
, fmt::arg("percentage_free", stats.f_bavail * 100 / stats.f_blocks)
|
, fmt::arg("percentage_free", stats.f_bavail * 100 / stats.f_blocks)
|
||||||
, fmt::arg("used", used)
|
, fmt::arg("used", used)
|
||||||
, fmt::arg("percentage_used", (stats.f_blocks - stats.f_bavail) * 100 / stats.f_blocks)
|
, fmt::arg("percentage_used", percentage_used)
|
||||||
, fmt::arg("total", total)
|
, fmt::arg("total", total)
|
||||||
, fmt::arg("path", path_)
|
, fmt::arg("path", path_)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
event_box_.show();
|
event_box_.show();
|
||||||
|
getState(percentage_used);
|
||||||
// Call parent update
|
// Call parent update
|
||||||
ALabel::update();
|
ALabel::update();
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,17 @@ auto waybar::modules::Memory::update() -> void {
|
||||||
fmt::arg("used", used_ram_gigabytes),
|
fmt::arg("used", used_ram_gigabytes),
|
||||||
fmt::arg("avail", available_ram_gigabytes)));
|
fmt::arg("avail", available_ram_gigabytes)));
|
||||||
if (tooltipEnabled()) {
|
if (tooltipEnabled()) {
|
||||||
label_.set_tooltip_text(fmt::format("{:.{}f}Gb used", used_ram_gigabytes, 1));
|
if (config_["tooltip-format"].isString()) {
|
||||||
|
auto tooltip_format = config_["tooltip-format"].asString();
|
||||||
|
label_.set_tooltip_text(fmt::format(tooltip_format,
|
||||||
|
used_ram_percentage,
|
||||||
|
fmt::arg("total", total_ram_gigabytes),
|
||||||
|
fmt::arg("percentage", used_ram_percentage),
|
||||||
|
fmt::arg("used", used_ram_gigabytes),
|
||||||
|
fmt::arg("avail", available_ram_gigabytes)));
|
||||||
|
} else {
|
||||||
|
label_.set_tooltip_text(fmt::format("{:.{}f}GiB used", used_ram_gigabytes, 1));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
event_box_.show();
|
event_box_.show();
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -63,6 +63,7 @@ auto waybar::modules::MPD::update() -> void {
|
||||||
|
|
||||||
std::thread waybar::modules::MPD::event_listener() {
|
std::thread waybar::modules::MPD::event_listener() {
|
||||||
return std::thread([this] {
|
return std::thread([this] {
|
||||||
|
while (true) {
|
||||||
try {
|
try {
|
||||||
if (connection_ == nullptr) {
|
if (connection_ == nullptr) {
|
||||||
// Retry periodically if no connection
|
// Retry periodically if no connection
|
||||||
|
@ -79,6 +80,7 @@ std::thread waybar::modules::MPD::event_listener() {
|
||||||
spdlog::warn("{}: {}", module_name_, e.what());
|
spdlog::warn("{}: {}", module_name_, e.what());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,6 +133,7 @@ void waybar::modules::MPD::setLabel() {
|
||||||
auto format = format_;
|
auto format = format_;
|
||||||
|
|
||||||
std::string artist, album_artist, album, title, date;
|
std::string artist, album_artist, album, title, date;
|
||||||
|
int song_pos, queue_length;
|
||||||
std::chrono::seconds elapsedTime, totalTime;
|
std::chrono::seconds elapsedTime, totalTime;
|
||||||
|
|
||||||
std::string stateIcon = "";
|
std::string stateIcon = "";
|
||||||
|
@ -159,6 +162,8 @@ void waybar::modules::MPD::setLabel() {
|
||||||
album = getTag(MPD_TAG_ALBUM);
|
album = getTag(MPD_TAG_ALBUM);
|
||||||
title = getTag(MPD_TAG_TITLE);
|
title = getTag(MPD_TAG_TITLE);
|
||||||
date = getTag(MPD_TAG_DATE);
|
date = getTag(MPD_TAG_DATE);
|
||||||
|
song_pos = mpd_status_get_song_pos(status_.get());
|
||||||
|
queue_length = mpd_status_get_queue_length(status_.get());
|
||||||
elapsedTime = std::chrono::seconds(mpd_status_get_elapsed_time(status_.get()));
|
elapsedTime = std::chrono::seconds(mpd_status_get_elapsed_time(status_.get()));
|
||||||
totalTime = std::chrono::seconds(mpd_status_get_total_time(status_.get()));
|
totalTime = std::chrono::seconds(mpd_status_get_total_time(status_.get()));
|
||||||
}
|
}
|
||||||
|
@ -182,6 +187,8 @@ void waybar::modules::MPD::setLabel() {
|
||||||
fmt::arg("date", Glib::Markup::escape_text(date).raw()),
|
fmt::arg("date", Glib::Markup::escape_text(date).raw()),
|
||||||
fmt::arg("elapsedTime", elapsedTime),
|
fmt::arg("elapsedTime", elapsedTime),
|
||||||
fmt::arg("totalTime", totalTime),
|
fmt::arg("totalTime", totalTime),
|
||||||
|
fmt::arg("songPosition", song_pos),
|
||||||
|
fmt::arg("queueLength", queue_length),
|
||||||
fmt::arg("stateIcon", stateIcon),
|
fmt::arg("stateIcon", stateIcon),
|
||||||
fmt::arg("consumeIcon", consumeIcon),
|
fmt::arg("consumeIcon", consumeIcon),
|
||||||
fmt::arg("randomIcon", randomIcon),
|
fmt::arg("randomIcon", randomIcon),
|
||||||
|
@ -198,6 +205,8 @@ void waybar::modules::MPD::setLabel() {
|
||||||
fmt::arg("album", album),
|
fmt::arg("album", album),
|
||||||
fmt::arg("title", title),
|
fmt::arg("title", title),
|
||||||
fmt::arg("date", date),
|
fmt::arg("date", date),
|
||||||
|
fmt::arg("songPosition", song_pos),
|
||||||
|
fmt::arg("queueLength", queue_length),
|
||||||
fmt::arg("stateIcon", stateIcon),
|
fmt::arg("stateIcon", stateIcon),
|
||||||
fmt::arg("consumeIcon", consumeIcon),
|
fmt::arg("consumeIcon", consumeIcon),
|
||||||
fmt::arg("randomIcon", randomIcon),
|
fmt::arg("randomIcon", randomIcon),
|
||||||
|
|
|
@ -4,7 +4,9 @@
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include "util/format.hpp"
|
#include "util/format.hpp"
|
||||||
|
#ifdef WANT_RFKILL
|
||||||
#include "util/rfkill.hpp"
|
#include "util/rfkill.hpp"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
@ -81,11 +83,15 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf
|
||||||
: ALabel(config, "network", id, "{ifname}", 60),
|
: ALabel(config, "network", id, "{ifname}", 60),
|
||||||
ifid_(-1),
|
ifid_(-1),
|
||||||
family_(config["family"] == "ipv6" ? AF_INET6 : AF_INET),
|
family_(config["family"] == "ipv6" ? AF_INET6 : AF_INET),
|
||||||
|
efd_(-1),
|
||||||
|
ev_fd_(-1),
|
||||||
cidr_(-1),
|
cidr_(-1),
|
||||||
signal_strength_dbm_(0),
|
signal_strength_dbm_(0),
|
||||||
signal_strength_(0),
|
signal_strength_(0),
|
||||||
frequency_(0),
|
#ifdef WANT_RFKILL
|
||||||
rfkill_{RFKILL_TYPE_WLAN} {
|
rfkill_{RFKILL_TYPE_WLAN},
|
||||||
|
#endif
|
||||||
|
frequency_(0) {
|
||||||
auto down_octets = read_netstat(BANDWIDTH_CATEGORY, BANDWIDTH_DOWN_TOTAL_KEY);
|
auto down_octets = read_netstat(BANDWIDTH_CATEGORY, BANDWIDTH_DOWN_TOTAL_KEY);
|
||||||
auto up_octets = read_netstat(BANDWIDTH_CATEGORY, BANDWIDTH_UP_TOTAL_KEY);
|
auto up_octets = read_netstat(BANDWIDTH_CATEGORY, BANDWIDTH_UP_TOTAL_KEY);
|
||||||
if (down_octets) {
|
if (down_octets) {
|
||||||
|
@ -115,6 +121,12 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf
|
||||||
}
|
}
|
||||||
|
|
||||||
waybar::modules::Network::~Network() {
|
waybar::modules::Network::~Network() {
|
||||||
|
if (ev_fd_ > -1) {
|
||||||
|
close(ev_fd_);
|
||||||
|
}
|
||||||
|
if (efd_ > -1) {
|
||||||
|
close(efd_);
|
||||||
|
}
|
||||||
if (ev_sock_ != nullptr) {
|
if (ev_sock_ != nullptr) {
|
||||||
nl_socket_drop_membership(ev_sock_, RTNLGRP_LINK);
|
nl_socket_drop_membership(ev_sock_, RTNLGRP_LINK);
|
||||||
if (family_ == AF_INET) {
|
if (family_ == AF_INET) {
|
||||||
|
@ -146,6 +158,30 @@ void waybar::modules::Network::createEventSocket() {
|
||||||
} else {
|
} else {
|
||||||
nl_socket_add_membership(ev_sock_, RTNLGRP_IPV6_IFADDR);
|
nl_socket_add_membership(ev_sock_, RTNLGRP_IPV6_IFADDR);
|
||||||
}
|
}
|
||||||
|
efd_ = epoll_create1(EPOLL_CLOEXEC);
|
||||||
|
if (efd_ < 0) {
|
||||||
|
throw std::runtime_error("Can't create epoll");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
ev_fd_ = eventfd(0, EFD_NONBLOCK);
|
||||||
|
struct epoll_event event;
|
||||||
|
memset(&event, 0, sizeof(event));
|
||||||
|
event.events = EPOLLIN | EPOLLET;
|
||||||
|
event.data.fd = ev_fd_;
|
||||||
|
if (epoll_ctl(efd_, EPOLL_CTL_ADD, ev_fd_, &event) == -1) {
|
||||||
|
throw std::runtime_error("Can't add epoll event");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto fd = nl_socket_get_fd(ev_sock_);
|
||||||
|
struct epoll_event event;
|
||||||
|
memset(&event, 0, sizeof(event));
|
||||||
|
event.events = EPOLLIN | EPOLLET | EPOLLRDHUP;
|
||||||
|
event.data.fd = fd;
|
||||||
|
if (epoll_ctl(efd_, EPOLL_CTL_ADD, fd, &event) == -1) {
|
||||||
|
throw std::runtime_error("Can't add epoll event");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void waybar::modules::Network::createInfoSocket() {
|
void waybar::modules::Network::createInfoSocket() {
|
||||||
|
@ -174,6 +210,7 @@ void waybar::modules::Network::worker() {
|
||||||
}
|
}
|
||||||
thread_timer_.sleep_for(interval_);
|
thread_timer_.sleep_for(interval_);
|
||||||
};
|
};
|
||||||
|
#ifdef WANT_RFKILL
|
||||||
thread_rfkill_ = [this] {
|
thread_rfkill_ = [this] {
|
||||||
rfkill_.waitForEvent();
|
rfkill_.waitForEvent();
|
||||||
{
|
{
|
||||||
|
@ -184,12 +221,30 @@ void waybar::modules::Network::worker() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
#else
|
||||||
|
spdlog::warn("Waybar has been built without rfkill support.");
|
||||||
|
#endif
|
||||||
|
thread_ = [this] {
|
||||||
|
std::array<struct epoll_event, EPOLL_MAX> events{};
|
||||||
|
|
||||||
|
int ec = epoll_wait(efd_, events.data(), EPOLL_MAX, -1);
|
||||||
|
if (ec > 0) {
|
||||||
|
for (auto i = 0; i < ec; i++) {
|
||||||
|
if (events[i].data.fd != nl_socket_get_fd(ev_sock_) || nl_recvmsgs_default(ev_sock_) < 0) {
|
||||||
|
thread_.stop();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string waybar::modules::Network::getNetworkState() const {
|
const std::string waybar::modules::Network::getNetworkState() const {
|
||||||
if (ifid_ == -1) {
|
if (ifid_ == -1) {
|
||||||
|
#ifdef WANT_RFKILL
|
||||||
if (rfkill_.getState())
|
if (rfkill_.getState())
|
||||||
return "disabled";
|
return "disabled";
|
||||||
|
#endif
|
||||||
return "disconnected";
|
return "disconnected";
|
||||||
}
|
}
|
||||||
if (ipaddr_.empty()) return "linked";
|
if (ipaddr_.empty()) return "linked";
|
||||||
|
@ -277,7 +332,7 @@ auto waybar::modules::Network::update() -> void {
|
||||||
fmt::arg("bandwidthUpBits", pow_format(bandwidth_up * 8ull / interval_.count(), "b/s")),
|
fmt::arg("bandwidthUpBits", pow_format(bandwidth_up * 8ull / interval_.count(), "b/s")),
|
||||||
fmt::arg("bandwidthDownOctets", pow_format(bandwidth_down / interval_.count(), "o/s")),
|
fmt::arg("bandwidthDownOctets", pow_format(bandwidth_down / interval_.count(), "o/s")),
|
||||||
fmt::arg("bandwidthUpOctets", pow_format(bandwidth_up / interval_.count(), "o/s")));
|
fmt::arg("bandwidthUpOctets", pow_format(bandwidth_up / interval_.count(), "o/s")));
|
||||||
if (label_.get_tooltip_text() != text) {
|
if (label_.get_tooltip_text() != tooltip_text) {
|
||||||
label_.set_tooltip_text(tooltip_text);
|
label_.set_tooltip_text(tooltip_text);
|
||||||
}
|
}
|
||||||
} else if (label_.get_tooltip_text() != text) {
|
} else if (label_.get_tooltip_text() != text) {
|
||||||
|
|
|
@ -0,0 +1,201 @@
|
||||||
|
#include "modules/sndio.hpp"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <fmt/format.h>
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
|
namespace waybar::modules {
|
||||||
|
|
||||||
|
void ondesc(void *arg, struct sioctl_desc *d, int curval) {
|
||||||
|
auto self = static_cast<Sndio*>(arg);
|
||||||
|
if (d == NULL) {
|
||||||
|
// d is NULL when the list is done
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self->set_desc(d, curval);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onval(void *arg, unsigned int addr, unsigned int val) {
|
||||||
|
auto self = static_cast<Sndio*>(arg);
|
||||||
|
self->put_val(addr, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Sndio::connect_to_sndio() -> void {
|
||||||
|
hdl_ = sioctl_open(SIO_DEVANY, SIOCTL_READ | SIOCTL_WRITE, 0);
|
||||||
|
if (hdl_ == nullptr) {
|
||||||
|
throw std::runtime_error("sioctl_open() failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sioctl_ondesc(hdl_, ondesc, this) == 0) {
|
||||||
|
throw std::runtime_error("sioctl_ondesc() failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sioctl_onval(hdl_, onval, this) == 0) {
|
||||||
|
throw std::runtime_error("sioctl_onval() failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
pfds_.reserve(sioctl_nfds(hdl_));
|
||||||
|
}
|
||||||
|
|
||||||
|
Sndio::Sndio(const std::string &id, const Json::Value &config)
|
||||||
|
: ALabel(config, "sndio", id, "{volume}%", 1),
|
||||||
|
hdl_(nullptr),
|
||||||
|
pfds_(0),
|
||||||
|
addr_(0),
|
||||||
|
volume_(0),
|
||||||
|
old_volume_(0),
|
||||||
|
maxval_(0),
|
||||||
|
muted_(false) {
|
||||||
|
connect_to_sndio();
|
||||||
|
|
||||||
|
event_box_.show();
|
||||||
|
|
||||||
|
event_box_.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK | Gdk::BUTTON_PRESS_MASK);
|
||||||
|
event_box_.signal_scroll_event().connect(
|
||||||
|
sigc::mem_fun(*this, &Sndio::handleScroll));
|
||||||
|
event_box_.signal_button_press_event().connect(
|
||||||
|
sigc::mem_fun(*this, &Sndio::handleToggle));
|
||||||
|
|
||||||
|
thread_ = [this] {
|
||||||
|
dp.emit();
|
||||||
|
|
||||||
|
int nfds = sioctl_pollfd(hdl_, pfds_.data(), POLLIN);
|
||||||
|
if (nfds == 0) {
|
||||||
|
throw std::runtime_error("sioctl_pollfd() failed.");
|
||||||
|
}
|
||||||
|
while (poll(pfds_.data(), nfds, -1) < 0) {
|
||||||
|
if (errno != EINTR) {
|
||||||
|
throw std::runtime_error("poll() failed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int revents = sioctl_revents(hdl_, pfds_.data());
|
||||||
|
if (revents & POLLHUP) {
|
||||||
|
spdlog::warn("sndio disconnected!");
|
||||||
|
sioctl_close(hdl_);
|
||||||
|
hdl_ = nullptr;
|
||||||
|
|
||||||
|
// reconnection loop
|
||||||
|
while (thread_.isRunning()) {
|
||||||
|
try {
|
||||||
|
connect_to_sndio();
|
||||||
|
} catch(std::runtime_error const& e) {
|
||||||
|
// avoid leaking hdl_
|
||||||
|
if (hdl_) {
|
||||||
|
sioctl_close(hdl_);
|
||||||
|
hdl_ = nullptr;
|
||||||
|
}
|
||||||
|
// rate limiting for the retries
|
||||||
|
thread_.sleep_for(interval_);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
spdlog::warn("sndio reconnected!");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Sndio::~Sndio() {
|
||||||
|
sioctl_close(hdl_);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Sndio::update() -> void {
|
||||||
|
auto format = format_;
|
||||||
|
unsigned int vol = 100. * static_cast<double>(volume_) / static_cast<double>(maxval_);
|
||||||
|
|
||||||
|
if (volume_ == 0) {
|
||||||
|
label_.get_style_context()->add_class("muted");
|
||||||
|
} else {
|
||||||
|
label_.get_style_context()->remove_class("muted");
|
||||||
|
}
|
||||||
|
|
||||||
|
label_.set_markup(fmt::format(format,
|
||||||
|
fmt::arg("volume", vol),
|
||||||
|
fmt::arg("raw_value", volume_)));
|
||||||
|
|
||||||
|
ALabel::update();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Sndio::set_desc(struct sioctl_desc *d, unsigned int val) -> void {
|
||||||
|
std::string name{d->func};
|
||||||
|
std::string node_name{d->node0.name};
|
||||||
|
|
||||||
|
if (name == "level" && node_name == "output" && d->type == SIOCTL_NUM) {
|
||||||
|
// store addr for output.level value, used in put_val
|
||||||
|
addr_ = d->addr;
|
||||||
|
maxval_ = d->maxval;
|
||||||
|
volume_ = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Sndio::put_val(unsigned int addr, unsigned int val) -> void {
|
||||||
|
if (addr == addr_) {
|
||||||
|
volume_ = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sndio::handleScroll(GdkEventScroll *e) {
|
||||||
|
// change the volume only when no user provided
|
||||||
|
// events are configured
|
||||||
|
if (config_["on-scroll-up"].isString() || config_["on-scroll-down"].isString()) {
|
||||||
|
return AModule::handleScroll(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// only try to talk to sndio if connected
|
||||||
|
if (hdl_ == nullptr) return true;
|
||||||
|
|
||||||
|
auto dir = AModule::getScrollDir(e);
|
||||||
|
if (dir == SCROLL_DIR::NONE) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int step = 5;
|
||||||
|
if (config_["scroll-step"].isInt()) {
|
||||||
|
step = config_["scroll-step"].asInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
int new_volume = volume_;
|
||||||
|
if (muted_) {
|
||||||
|
new_volume = old_volume_;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dir == SCROLL_DIR::UP) {
|
||||||
|
new_volume += step;
|
||||||
|
} else if (dir == SCROLL_DIR::DOWN) {
|
||||||
|
new_volume -= step;
|
||||||
|
}
|
||||||
|
new_volume = std::clamp(new_volume, 0, static_cast<int>(maxval_));
|
||||||
|
|
||||||
|
// quits muted mode if volume changes
|
||||||
|
muted_ = false;
|
||||||
|
|
||||||
|
sioctl_setval(hdl_, addr_, new_volume);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sndio::handleToggle(GdkEventButton* const& e) {
|
||||||
|
// toggle mute only when no user provided events are configured
|
||||||
|
if (config_["on-click"].isString()) {
|
||||||
|
return AModule::handleToggle(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// only try to talk to sndio if connected
|
||||||
|
if (hdl_ == nullptr) return true;
|
||||||
|
|
||||||
|
muted_ = !muted_;
|
||||||
|
if (muted_) {
|
||||||
|
// store old volume to be able to restore it later
|
||||||
|
old_volume_ = volume_;
|
||||||
|
sioctl_setval(hdl_, addr_, 0);
|
||||||
|
} else {
|
||||||
|
sioctl_setval(hdl_, addr_, old_volume_);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} /* namespace waybar::modules */
|
|
@ -248,8 +248,10 @@ Gtk::Button &Workspaces::addButton(const Json::Value &node) {
|
||||||
auto pair = buttons_.emplace(node["name"].asString(), node["name"].asString());
|
auto pair = buttons_.emplace(node["name"].asString(), node["name"].asString());
|
||||||
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_name("sway-workspace-" + node["name"].asString());
|
||||||
button.set_relief(Gtk::RELIEF_NONE);
|
button.set_relief(Gtk::RELIEF_NONE);
|
||||||
button.signal_clicked().connect([this, node] {
|
if (!config_["disable-click"].asBool()) {
|
||||||
|
button.signal_pressed().connect([this, node] {
|
||||||
try {
|
try {
|
||||||
if (node["target_output"].isString()) {
|
if (node["target_output"].isString()) {
|
||||||
ipc_.sendCmd(
|
ipc_.sendCmd(
|
||||||
|
@ -265,6 +267,7 @@ Gtk::Button &Workspaces::addButton(const Json::Value &node) {
|
||||||
spdlog::error("Workspaces: {}", e.what());
|
spdlog::error("Workspaces: {}", e.what());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
return button;
|
return button;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -280,6 +283,8 @@ std::string Workspaces::getIcon(const std::string &name, const Json::Value &node
|
||||||
return config_["format-icons"]["persistent"].asString();
|
return config_["format-icons"]["persistent"].asString();
|
||||||
} else if (config_["format-icons"][key].isString()) {
|
} else if (config_["format-icons"][key].isString()) {
|
||||||
return config_["format-icons"][key].asString();
|
return config_["format-icons"][key].asString();
|
||||||
|
} else if (config_["format-icons"][trimWorkspaceName(key)].isString()) {
|
||||||
|
return config_["format-icons"][trimWorkspaceName(key)].asString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return name;
|
return name;
|
||||||
|
|
|
@ -460,38 +460,51 @@ bool Task::operator!=(const Task &o) const
|
||||||
|
|
||||||
void Task::update()
|
void Task::update()
|
||||||
{
|
{
|
||||||
|
bool markup = config_["markup"].isBool() ? config_["markup"].asBool() : false;
|
||||||
|
std::string title = title_;
|
||||||
|
std::string app_id = app_id_;
|
||||||
|
if (markup) {
|
||||||
|
title = Glib::Markup::escape_text(title);
|
||||||
|
app_id = Glib::Markup::escape_text(app_id);
|
||||||
|
}
|
||||||
if (!format_before_.empty()) {
|
if (!format_before_.empty()) {
|
||||||
text_before_.set_label(
|
auto txt = fmt::format(format_before_,
|
||||||
fmt::format(format_before_,
|
fmt::arg("title", title),
|
||||||
fmt::arg("title", title_),
|
fmt::arg("app_id", app_id),
|
||||||
fmt::arg("app_id", app_id_),
|
|
||||||
fmt::arg("state", state_string()),
|
fmt::arg("state", state_string()),
|
||||||
fmt::arg("short_state", state_string(true))
|
fmt::arg("short_state", state_string(true))
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
if (markup)
|
||||||
|
text_before_.set_markup(txt);
|
||||||
|
else
|
||||||
|
text_before_.set_label(txt);
|
||||||
text_before_.show();
|
text_before_.show();
|
||||||
}
|
}
|
||||||
if (!format_after_.empty()) {
|
if (!format_after_.empty()) {
|
||||||
text_after_.set_label(
|
auto txt = fmt::format(format_after_,
|
||||||
fmt::format(format_after_,
|
fmt::arg("title", title),
|
||||||
fmt::arg("title", title_),
|
fmt::arg("app_id", app_id),
|
||||||
fmt::arg("app_id", app_id_),
|
|
||||||
fmt::arg("state", state_string()),
|
fmt::arg("state", state_string()),
|
||||||
fmt::arg("short_state", state_string(true))
|
fmt::arg("short_state", state_string(true))
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
if (markup)
|
||||||
|
text_after_.set_markup(txt);
|
||||||
|
else
|
||||||
|
text_after_.set_label(txt);
|
||||||
text_after_.show();
|
text_after_.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!format_tooltip_.empty()) {
|
if (!format_tooltip_.empty()) {
|
||||||
button_.set_tooltip_markup(
|
auto txt = fmt::format(format_tooltip_,
|
||||||
fmt::format(format_tooltip_,
|
fmt::arg("title", title),
|
||||||
fmt::arg("title", title_),
|
fmt::arg("app_id", app_id),
|
||||||
fmt::arg("app_id", app_id_),
|
|
||||||
fmt::arg("state", state_string()),
|
fmt::arg("state", state_string()),
|
||||||
fmt::arg("short_state", state_string(true))
|
fmt::arg("short_state", state_string(true))
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
if (markup)
|
||||||
|
button_.set_tooltip_markup(txt);
|
||||||
|
else
|
||||||
|
button_.set_tooltip_text(txt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -709,7 +722,7 @@ bool Taskbar::show_output(struct wl_output *output) const
|
||||||
|
|
||||||
bool Taskbar::all_outputs() const
|
bool Taskbar::all_outputs() const
|
||||||
{
|
{
|
||||||
return config_["all_outputs"].isBool() && config_["all_outputs"].asBool();
|
return config_["all-outputs"].isBool() && config_["all-outputs"].asBool();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Glib::RefPtr<Gtk::IconTheme>> Taskbar::icon_themes() const
|
std::vector<Glib::RefPtr<Gtk::IconTheme>> Taskbar::icon_themes() const
|
||||||
|
|
|
@ -20,8 +20,8 @@
|
||||||
|
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <linux/rfkill.h>
|
#include <linux/rfkill.h>
|
||||||
|
#include <poll.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <sys/poll.h>
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
[wrap-file]
|
[wrap-file]
|
||||||
source_url=https://github.com/HowardHinnant/date/archive/v2.4.1.tar.gz
|
source_url=https://github.com/HowardHinnant/date/archive/v3.0.0.tar.gz
|
||||||
source_filename=date-2.4.1.tar.gz
|
source_filename=date-3.0.0.tar.gz
|
||||||
source_hash=98907d243397483bd7ad889bf6c66746db0d7d2a39cc9aacc041834c40b65b98
|
source_hash=87bba2eaf0ebc7ec539e5e62fc317cb80671a337c1fb1b84cb9e4d42c6dbebe3
|
||||||
directory=date-2.4.1
|
directory=date-3.0.0
|
||||||
|
|
||||||
patch_url = https://github.com/mesonbuild/hinnant-date/releases/download/2.4.1-1/hinnant-date.zip
|
patch_url = https://github.com/mesonbuild/hinnant-date/releases/download/3.0.0-1/hinnant-date.zip
|
||||||
patch_filename = hinnant-date-2.4.1-1-wrap.zip
|
patch_filename = hinnant-date-3.0.0-1-wrap.zip
|
||||||
patch_hash = 2061673a6f8e6d63c3a40df4da58fa2b3de2835fd9b3e74649e8279599f3a8f6
|
patch_hash = 6ccaf70732d8bdbd1b6d5fdf3e1b935c23bf269bda12fdfd0e561276f63432fe
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[wrap-file]
|
[wrap-file]
|
||||||
directory = gtk-layer-shell-0.1.0
|
directory = gtk-layer-shell-0.3.0
|
||||||
source_filename = gtk-layer-shell-0.1.0.tar.gz
|
source_filename = gtk-layer-shell-0.3.0.tar.gz
|
||||||
source_hash = f7569e27ae30b1a94c3ad6c955cf56240d6bc272b760d9d266ce2ccdb94a5cf0
|
source_hash = edd5e31279d494df66da9e9190c219fa295da547f5538207685e98468dbc134d
|
||||||
source_url = https://github.com/wmww/gtk-layer-shell/archive/v0.1.0/gtk-layer-shell-0.1.0.tar.gz
|
source_url = https://github.com/wmww/gtk-layer-shell/archive/v0.3.0/gtk-layer-shell-0.3.0.tar.gz
|
||||||
|
|
Loading…
Reference in New Issue