Merge branch 'master' into config-reloading
commit
45f7f9b07a
|
@ -29,7 +29,9 @@ jobs:
|
|||
compiler: clang
|
||||
env:
|
||||
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
|
||||
script:
|
||||
- meson build -Dman-pages=enabled
|
||||
|
|
|
@ -2,4 +2,4 @@
|
|||
|
||||
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
|
||||
|
|
|
@ -67,6 +67,7 @@ libnl [Network module]
|
|||
libappindicator-gtk3 [Tray module]
|
||||
libdbusmenu-gtk3 [Tray module]
|
||||
libmpdclient [MPD module]
|
||||
libsndio [sndio module]
|
||||
```
|
||||
|
||||
**Build dependencies**
|
||||
|
|
|
@ -39,6 +39,9 @@
|
|||
#ifdef HAVE_LIBMPDCLIENT
|
||||
#include "modules/mpd.hpp"
|
||||
#endif
|
||||
#ifdef HAVE_LIBSNDIO
|
||||
#include "modules/sndio.hpp"
|
||||
#endif
|
||||
#include "bar.hpp"
|
||||
#include "modules/custom.hpp"
|
||||
#include "modules/temperature.hpp"
|
||||
|
|
|
@ -22,6 +22,7 @@ class Custom : public ALabel {
|
|||
void continuousWorker();
|
||||
void parseOutputRaw();
|
||||
void parseOutputJson();
|
||||
void handleEvent();
|
||||
bool handleScroll(GdkEventScroll* e);
|
||||
bool handleToggle(GdkEventButton* const& e);
|
||||
|
||||
|
|
|
@ -54,6 +54,8 @@ class Network : public ALabel {
|
|||
struct sockaddr_nl nladdr_ = {0};
|
||||
struct nl_sock* sock_ = nullptr;
|
||||
struct nl_sock* ev_sock_ = nullptr;
|
||||
int efd_;
|
||||
int ev_fd_;
|
||||
int nl80211_id_;
|
||||
std::mutex mutex_;
|
||||
|
||||
|
|
|
@ -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
|
|
@ -22,6 +22,12 @@ Addressed by *custom/<name>*
|
|||
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-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*: ++
|
||||
typeof: string ++
|
||||
See *return-type*
|
||||
|
|
|
@ -31,6 +31,10 @@ Addressed by *disk*
|
|||
typeof: integer ++
|
||||
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*: ++
|
||||
typeof: integer ++
|
||||
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)
|
||||
|
||||
*{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)
|
||||
|
||||
*{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*
|
|
@ -32,6 +32,11 @@ Addressed by *wlr/taskbar*
|
|||
default: 16 ++
|
||||
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*: ++
|
||||
typeof: bool ++
|
||||
default: true ++
|
||||
|
|
|
@ -14,6 +14,7 @@ Valid locations for this file are:
|
|||
- *~/.config/waybar/config*
|
||||
- *~/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
|
||||
Also a minimal example configuration can be found on the at the bottom of this man page.
|
49
meson.build
49
meson.build
|
@ -1,6 +1,6 @@
|
|||
project(
|
||||
'waybar', 'cpp', 'c',
|
||||
version: '0.9.3',
|
||||
version: '0.9.4',
|
||||
license: 'MIT',
|
||||
default_options : [
|
||||
'cpp_std=c++17',
|
||||
|
@ -9,6 +9,8 @@ project(
|
|||
],
|
||||
)
|
||||
|
||||
fs = import('fs')
|
||||
|
||||
compiler = meson.get_compiler('cpp')
|
||||
|
||||
cpp_args = []
|
||||
|
@ -94,6 +96,19 @@ 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'))
|
||||
|
||||
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',
|
||||
required: get_option('gtk-layer-shell'),
|
||||
fallback : ['gtk-layer-shell', 'gtk_layer_shell_dep'])
|
||||
|
@ -205,6 +220,11 @@ if gtk_layer_shell.found()
|
|||
add_project_arguments('-DHAVE_GTK_LAYER_SHELL', language: 'cpp')
|
||||
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')
|
||||
|
@ -239,6 +259,7 @@ executable(
|
|||
libepoll,
|
||||
libmpdclient,
|
||||
gtk_layer_shell,
|
||||
libsndio,
|
||||
tz_dep
|
||||
],
|
||||
include_directories: [include_directories('include')],
|
||||
|
@ -256,9 +277,20 @@ scdoc = dependency('scdoc', version: '>=1.9.2', native: true, required: get_opti
|
|||
if scdoc.found()
|
||||
scdoc_prog = find_program(scdoc.get_pkgconfig_variable('scdoc'), 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')
|
||||
man_files = [
|
||||
'waybar.5.scd',
|
||||
main_manpage_path,
|
||||
'waybar-backlight.5.scd',
|
||||
'waybar-battery.5.scd',
|
||||
'waybar-clock.5.scd',
|
||||
|
@ -279,16 +311,21 @@ if scdoc.found()
|
|||
'waybar-states.5.scd',
|
||||
'waybar-wlr-taskbar.5.scd',
|
||||
'waybar-bluetooth.5.scd',
|
||||
'waybar-sndio.5.scd',
|
||||
]
|
||||
|
||||
foreach filename : man_files
|
||||
topic = filename.split('.')[-3].split('/')[-1]
|
||||
section = filename.split('.')[-2]
|
||||
foreach file : man_files
|
||||
path = '@0@'.format(file)
|
||||
basename = fs.name(path)
|
||||
|
||||
topic = basename.split('.')[-3].split('/')[-1]
|
||||
section = basename.split('.')[-2]
|
||||
output = '@0@.@1@'.format(topic, section)
|
||||
|
||||
custom_target(
|
||||
output,
|
||||
input: 'man/@0@'.format(filename),
|
||||
# drops the 'man' if `path` is an absolute path
|
||||
input: join_paths('man', path),
|
||||
output: output,
|
||||
command: [
|
||||
sh, '-c', '@0@ < @INPUT@ > @1@'.format(scdoc_prog.path(), output)
|
||||
|
|
|
@ -8,3 +8,4 @@ option('man-pages', type: 'feature', value: 'auto', description: 'Generate and i
|
|||
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('rfkill', type: 'feature', value: 'auto', description: 'Enable support for RFKILL')
|
||||
option('sndio', type: 'feature', value: 'auto', description: 'Enable support for sndio')
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
"format": "<span style=\"italic\">{}</span>"
|
||||
},
|
||||
"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-stopped": "{consumeIcon}{randomIcon}{repeatIcon}{singleIcon}Stopped ",
|
||||
"unknown-tag": "N/A",
|
||||
|
|
|
@ -41,19 +41,19 @@ window#waybar.chromium {
|
|||
padding: 0 5px;
|
||||
background-color: transparent;
|
||||
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 */
|
||||
#workspaces button:hover {
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
box-shadow: inherit;
|
||||
border-bottom: 3px solid #ffffff;
|
||||
box-shadow: inset 0 -3px #ffffff;
|
||||
}
|
||||
|
||||
#workspaces button.focused {
|
||||
background-color: #64727D;
|
||||
border-bottom: 3px solid #ffffff;
|
||||
box-shadow: inset 0 -3px #ffffff;
|
||||
}
|
||||
|
||||
#workspaces button.urgent {
|
||||
|
|
|
@ -44,9 +44,9 @@ bool AModule::handleToggle(GdkEventButton* const& e) {
|
|||
format = config_["on-click-middle"].asString();
|
||||
} else if (config_["on-click-right"].isString() && e->button == 3) {
|
||||
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();
|
||||
} 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();
|
||||
}
|
||||
if (!format.empty()) {
|
||||
|
|
|
@ -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(config["name"].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") {
|
||||
height_ = 0;
|
||||
|
|
|
@ -162,6 +162,7 @@ std::tuple<const std::string, const std::string> waybar::Client::getConfigs(
|
|||
"$XDG_CONFIG_HOME/waybar/config",
|
||||
"$HOME/.config/waybar/config",
|
||||
"$HOME/waybar/config",
|
||||
"/etc/xdg/waybar/config",
|
||||
SYSCONFDIR "/xdg/waybar/config",
|
||||
"./resources/config",
|
||||
})
|
||||
|
@ -170,6 +171,7 @@ std::tuple<const std::string, const std::string> waybar::Client::getConfigs(
|
|||
"$XDG_CONFIG_HOME/waybar/style.css",
|
||||
"$HOME/.config/waybar/style.css",
|
||||
"$HOME/waybar/style.css",
|
||||
"/etc/xdg/waybar/style.css",
|
||||
SYSCONFDIR "/xdg/waybar/style.css",
|
||||
"./resources/style.css",
|
||||
})
|
||||
|
|
|
@ -76,6 +76,11 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const {
|
|||
if (ref == "mpd") {
|
||||
return new waybar::modules::MPD(id, config_[name]);
|
||||
}
|
||||
#endif
|
||||
#ifdef HAVE_LIBSNDIO
|
||||
if (ref == "sndio") {
|
||||
return new waybar::modules::Sndio(id, config_[name]);
|
||||
}
|
||||
#endif
|
||||
if (ref == "temperature") {
|
||||
return new waybar::modules::Temperature(id, config_[name]);
|
||||
|
|
|
@ -50,7 +50,6 @@ void waybar::modules::Custom::continuousWorker() {
|
|||
thread_ = [this, cmd] {
|
||||
char* buff = nullptr;
|
||||
size_t len = 0;
|
||||
bool restart = false;
|
||||
if (getline(&buff, &len, fp_) == -1) {
|
||||
int exit_code = 1;
|
||||
if (fp_) {
|
||||
|
@ -63,8 +62,8 @@ void waybar::modules::Custom::continuousWorker() {
|
|||
spdlog::error("{} stopped unexpectedly, is it endless?", name_);
|
||||
}
|
||||
if (config_["restart-interval"].isUInt()) {
|
||||
restart = true;
|
||||
pid_ = -1;
|
||||
thread_.sleep_for(std::chrono::seconds(config_["restart-interval"].asUInt()));
|
||||
fp_ = util::command::open(cmd, pid_);
|
||||
if (!fp_) {
|
||||
throw std::runtime_error("Unable to open " + cmd);
|
||||
|
@ -83,9 +82,6 @@ void waybar::modules::Custom::continuousWorker() {
|
|||
output_ = {0, output};
|
||||
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) {
|
||||
auto ret = ALabel::handleScroll(e);
|
||||
thread_.wake_up();
|
||||
handleEvent();
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool waybar::modules::Custom::handleToggle(GdkEventButton* const& e) {
|
||||
auto ret = ALabel::handleToggle(e);
|
||||
thread_.wake_up();
|
||||
handleEvent();
|
||||
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 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 percentage_used = (stats.f_blocks - stats.f_bavail) * 100 / stats.f_blocks;
|
||||
|
||||
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("percentage_used", percentage_used)
|
||||
, fmt::arg("total", total)
|
||||
, fmt::arg("path", path_)
|
||||
));
|
||||
|
@ -67,12 +68,13 @@ auto waybar::modules::Disk::update() -> void {
|
|||
, 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("percentage_used", percentage_used)
|
||||
, fmt::arg("total", total)
|
||||
, fmt::arg("path", path_)
|
||||
));
|
||||
}
|
||||
event_box_.show();
|
||||
getState(percentage_used);
|
||||
// Call parent update
|
||||
ALabel::update();
|
||||
}
|
||||
|
|
|
@ -36,7 +36,17 @@ auto waybar::modules::Memory::update() -> void {
|
|||
fmt::arg("used", used_ram_gigabytes),
|
||||
fmt::arg("avail", available_ram_gigabytes)));
|
||||
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();
|
||||
} else {
|
||||
|
|
|
@ -133,6 +133,7 @@ void waybar::modules::MPD::setLabel() {
|
|||
auto format = format_;
|
||||
|
||||
std::string artist, album_artist, album, title, date;
|
||||
int song_pos, queue_length;
|
||||
std::chrono::seconds elapsedTime, totalTime;
|
||||
|
||||
std::string stateIcon = "";
|
||||
|
@ -161,6 +162,8 @@ void waybar::modules::MPD::setLabel() {
|
|||
album = getTag(MPD_TAG_ALBUM);
|
||||
title = getTag(MPD_TAG_TITLE);
|
||||
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()));
|
||||
totalTime = std::chrono::seconds(mpd_status_get_total_time(status_.get()));
|
||||
}
|
||||
|
@ -184,6 +187,8 @@ void waybar::modules::MPD::setLabel() {
|
|||
fmt::arg("date", Glib::Markup::escape_text(date).raw()),
|
||||
fmt::arg("elapsedTime", elapsedTime),
|
||||
fmt::arg("totalTime", totalTime),
|
||||
fmt::arg("songPosition", song_pos),
|
||||
fmt::arg("queueLength", queue_length),
|
||||
fmt::arg("stateIcon", stateIcon),
|
||||
fmt::arg("consumeIcon", consumeIcon),
|
||||
fmt::arg("randomIcon", randomIcon),
|
||||
|
@ -200,6 +205,8 @@ void waybar::modules::MPD::setLabel() {
|
|||
fmt::arg("album", album),
|
||||
fmt::arg("title", title),
|
||||
fmt::arg("date", date),
|
||||
fmt::arg("songPosition", song_pos),
|
||||
fmt::arg("queueLength", queue_length),
|
||||
fmt::arg("stateIcon", stateIcon),
|
||||
fmt::arg("consumeIcon", consumeIcon),
|
||||
fmt::arg("randomIcon", randomIcon),
|
||||
|
|
|
@ -83,6 +83,8 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf
|
|||
: ALabel(config, "network", id, "{ifname}", 60),
|
||||
ifid_(-1),
|
||||
family_(config["family"] == "ipv6" ? AF_INET6 : AF_INET),
|
||||
efd_(-1),
|
||||
ev_fd_(-1),
|
||||
cidr_(-1),
|
||||
signal_strength_dbm_(0),
|
||||
signal_strength_(0),
|
||||
|
@ -119,6 +121,12 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf
|
|||
}
|
||||
|
||||
waybar::modules::Network::~Network() {
|
||||
if (ev_fd_ > -1) {
|
||||
close(ev_fd_);
|
||||
}
|
||||
if (efd_ > -1) {
|
||||
close(efd_);
|
||||
}
|
||||
if (ev_sock_ != nullptr) {
|
||||
nl_socket_drop_membership(ev_sock_, RTNLGRP_LINK);
|
||||
if (family_ == AF_INET) {
|
||||
|
@ -150,6 +158,30 @@ void waybar::modules::Network::createEventSocket() {
|
|||
} else {
|
||||
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() {
|
||||
|
@ -192,6 +224,19 @@ 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 {
|
||||
|
|
|
@ -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 */
|
|
@ -283,6 +283,8 @@ std::string Workspaces::getIcon(const std::string &name, const Json::Value &node
|
|||
return config_["format-icons"]["persistent"].asString();
|
||||
} else if (config_["format-icons"][key].isString()) {
|
||||
return config_["format-icons"][key].asString();
|
||||
} else if (config_["format-icons"][trimWorkspaceName(key)].isString()) {
|
||||
return config_["format-icons"][trimWorkspaceName(key)].asString();
|
||||
}
|
||||
}
|
||||
return name;
|
||||
|
|
|
@ -306,7 +306,7 @@ std::string Task::state_string(bool shortened) const
|
|||
|
||||
void Task::handle_title(const char *title)
|
||||
{
|
||||
title_ = Glib::Markup::escape_text(title);
|
||||
title_ = title;
|
||||
}
|
||||
|
||||
void Task::handle_app_id(const char *app_id)
|
||||
|
@ -460,38 +460,51 @@ bool Task::operator!=(const Task &o) const
|
|||
|
||||
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()) {
|
||||
text_before_.set_label(
|
||||
fmt::format(format_before_,
|
||||
fmt::arg("title", title_),
|
||||
fmt::arg("app_id", app_id_),
|
||||
auto txt = fmt::format(format_before_,
|
||||
fmt::arg("title", title),
|
||||
fmt::arg("app_id", app_id),
|
||||
fmt::arg("state", state_string()),
|
||||
fmt::arg("short_state", state_string(true))
|
||||
)
|
||||
);
|
||||
if (markup)
|
||||
text_before_.set_markup(txt);
|
||||
else
|
||||
text_before_.set_label(txt);
|
||||
text_before_.show();
|
||||
}
|
||||
if (!format_after_.empty()) {
|
||||
text_after_.set_label(
|
||||
fmt::format(format_after_,
|
||||
fmt::arg("title", title_),
|
||||
fmt::arg("app_id", app_id_),
|
||||
auto txt = fmt::format(format_after_,
|
||||
fmt::arg("title", title),
|
||||
fmt::arg("app_id", app_id),
|
||||
fmt::arg("state", state_string()),
|
||||
fmt::arg("short_state", state_string(true))
|
||||
)
|
||||
);
|
||||
if (markup)
|
||||
text_after_.set_markup(txt);
|
||||
else
|
||||
text_after_.set_label(txt);
|
||||
text_after_.show();
|
||||
}
|
||||
|
||||
if (!format_tooltip_.empty()) {
|
||||
button_.set_tooltip_markup(
|
||||
fmt::format(format_tooltip_,
|
||||
fmt::arg("title", title_),
|
||||
fmt::arg("app_id", app_id_),
|
||||
auto txt = fmt::format(format_tooltip_,
|
||||
fmt::arg("title", title),
|
||||
fmt::arg("app_id", app_id),
|
||||
fmt::arg("state", state_string()),
|
||||
fmt::arg("short_state", state_string(true))
|
||||
)
|
||||
);
|
||||
if (markup)
|
||||
button_.set_tooltip_markup(txt);
|
||||
else
|
||||
button_.set_tooltip_text(txt);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,8 +20,8 @@
|
|||
|
||||
#include <fcntl.h>
|
||||
#include <linux/rfkill.h>
|
||||
#include <poll.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/poll.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <cerrno>
|
||||
|
|
Loading…
Reference in New Issue