Merge branch 'master' into list_of_times
commit
b6655e475b
|
@ -11,7 +11,7 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Test in FreeBSD VM
|
- name: Test in FreeBSD VM
|
||||||
uses: vmactions/freebsd-vm@v0.1.5 # aka FreeBSD 13.0
|
uses: vmactions/freebsd-vm@v0.1.6 # aka FreeBSD 13.0
|
||||||
with:
|
with:
|
||||||
mem: 2048
|
mem: 2048
|
||||||
usesh: true
|
usesh: true
|
||||||
|
@ -21,7 +21,7 @@ jobs:
|
||||||
pkg install -y git # subprojects/date
|
pkg install -y git # subprojects/date
|
||||||
pkg install -y catch evdev-proto gtk-layer-shell gtkmm30 jsoncpp \
|
pkg install -y catch evdev-proto gtk-layer-shell gtkmm30 jsoncpp \
|
||||||
libdbusmenu libevdev libfmt libmpdclient libudev-devd meson \
|
libdbusmenu libevdev libfmt libmpdclient libudev-devd meson \
|
||||||
pkgconf pulseaudio scdoc sndio spdlog
|
pkgconf pulseaudio scdoc sndio spdlog wayland-protocols
|
||||||
run: |
|
run: |
|
||||||
meson build -Dman-pages=enabled
|
meson build -Dman-pages=enabled
|
||||||
ninja -C build
|
ninja -C build
|
||||||
|
|
|
@ -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 sndio-dev scdoc libxkbcommon
|
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 libxkbcommon tzdata
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
> Highly customizable Wayland bar for Sway and Wlroots based compositors.<br>
|
> Highly customizable Wayland bar for Sway and Wlroots based compositors.<br>
|
||||||
> Available in Arch [community](https://www.archlinux.org/packages/community/x86_64/waybar/) or
|
> Available in Arch [community](https://www.archlinux.org/packages/community/x86_64/waybar/) or
|
||||||
[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/), [Gentoo](https://packages.gentoo.org/packages/gui-apps/waybar), [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
|
||||||
|
@ -69,6 +69,7 @@ libdbusmenu-gtk3 [Tray module]
|
||||||
libmpdclient [MPD module]
|
libmpdclient [MPD module]
|
||||||
libsndio [sndio module]
|
libsndio [sndio module]
|
||||||
libevdev [KeyboardState module]
|
libevdev [KeyboardState module]
|
||||||
|
xkbregistry
|
||||||
```
|
```
|
||||||
|
|
||||||
**Build dependencies**
|
**Build dependencies**
|
||||||
|
@ -101,7 +102,8 @@ sudo apt install \
|
||||||
libsigc++-2.0-dev \
|
libsigc++-2.0-dev \
|
||||||
libspdlog-dev \
|
libspdlog-dev \
|
||||||
libwayland-dev \
|
libwayland-dev \
|
||||||
scdoc
|
scdoc \
|
||||||
|
libxkbregistry-dev
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <gtkmm/box.h>
|
||||||
|
#include <gtkmm/image.h>
|
||||||
|
|
||||||
|
#include "ALabel.hpp"
|
||||||
|
|
||||||
|
namespace waybar {
|
||||||
|
|
||||||
|
class AIconLabel : public ALabel {
|
||||||
|
public:
|
||||||
|
AIconLabel(const Json::Value &config, const std::string &name, const std::string &id,
|
||||||
|
const std::string &format, uint16_t interval = 0, bool ellipsize = false,
|
||||||
|
bool enable_click = false, bool enable_scroll = false);
|
||||||
|
virtual ~AIconLabel() = default;
|
||||||
|
virtual auto update() -> void;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Gtk::Image image_;
|
||||||
|
Gtk::Box box_;
|
||||||
|
|
||||||
|
bool iconEnabled() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace waybar
|
|
@ -112,14 +112,14 @@ class Bar {
|
||||||
Gtk::Box center_;
|
Gtk::Box center_;
|
||||||
Gtk::Box right_;
|
Gtk::Box right_;
|
||||||
Gtk::Box box_;
|
Gtk::Box box_;
|
||||||
std::vector<std::unique_ptr<waybar::AModule>> modules_left_;
|
std::vector<std::shared_ptr<waybar::AModule>> modules_left_;
|
||||||
std::vector<std::unique_ptr<waybar::AModule>> modules_center_;
|
std::vector<std::shared_ptr<waybar::AModule>> modules_center_;
|
||||||
std::vector<std::unique_ptr<waybar::AModule>> modules_right_;
|
std::vector<std::shared_ptr<waybar::AModule>> modules_right_;
|
||||||
#ifdef HAVE_SWAY
|
#ifdef HAVE_SWAY
|
||||||
using BarIpcClient = modules::sway::BarIpcClient;
|
using BarIpcClient = modules::sway::BarIpcClient;
|
||||||
std::unique_ptr<BarIpcClient> _ipc_client;
|
std::unique_ptr<BarIpcClient> _ipc_client;
|
||||||
#endif
|
#endif
|
||||||
std::vector<std::unique_ptr<waybar::AModule>> modules_all_;
|
std::vector<std::shared_ptr<waybar::AModule>> modules_all_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace waybar
|
} // namespace waybar
|
||||||
|
|
|
@ -51,6 +51,9 @@
|
||||||
#ifdef HAVE_LIBSNDIO
|
#ifdef HAVE_LIBSNDIO
|
||||||
#include "modules/sndio.hpp"
|
#include "modules/sndio.hpp"
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef HAVE_GIO_UNIX
|
||||||
|
#include "modules/inhibitor.hpp"
|
||||||
|
#endif
|
||||||
#include "bar.hpp"
|
#include "bar.hpp"
|
||||||
#include "modules/custom.hpp"
|
#include "modules/custom.hpp"
|
||||||
#include "modules/temperature.hpp"
|
#include "modules/temperature.hpp"
|
||||||
|
|
|
@ -1,21 +1,14 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <fmt/format.h>
|
|
||||||
#if FMT_VERSION < 60000
|
|
||||||
#include <fmt/time.h>
|
|
||||||
#else
|
|
||||||
#include <fmt/chrono.h>
|
|
||||||
#endif
|
|
||||||
#include <date/tz.h>
|
#include <date/tz.h>
|
||||||
#include "ALabel.hpp"
|
#include "ALabel.hpp"
|
||||||
#include "util/sleeper_thread.hpp"
|
#include "util/sleeper_thread.hpp"
|
||||||
|
|
||||||
namespace waybar::modules {
|
namespace waybar {
|
||||||
|
|
||||||
struct waybar_time {
|
struct waybar_time;
|
||||||
std::locale locale;
|
|
||||||
date::zoned_seconds ztime;
|
namespace modules {
|
||||||
};
|
|
||||||
|
|
||||||
const std::string kCalendarPlaceholder = "calendar";
|
const std::string kCalendarPlaceholder = "calendar";
|
||||||
const std::string KTimezonedTimeListPlaceholder = "timezoned_time_list";
|
const std::string KTimezonedTimeListPlaceholder = "timezoned_time_list";
|
||||||
|
@ -46,4 +39,5 @@ class Clock : public ALabel {
|
||||||
auto timezones_text(std::chrono::_V2::system_clock::time_point *now) -> std::string;
|
auto timezones_text(std::chrono::_V2::system_clock::time_point *now) -> std::string;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace waybar::modules
|
} // namespace modules
|
||||||
|
} // namespace waybar
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include <gio/gio.h>
|
||||||
|
|
||||||
|
#include "ALabel.hpp"
|
||||||
|
#include "bar.hpp"
|
||||||
|
|
||||||
|
namespace waybar::modules {
|
||||||
|
|
||||||
|
class Inhibitor : public ALabel {
|
||||||
|
public:
|
||||||
|
Inhibitor(const std::string&, const waybar::Bar&, const Json::Value&);
|
||||||
|
~Inhibitor() override;
|
||||||
|
auto update() -> void;
|
||||||
|
auto activated() -> bool;
|
||||||
|
|
||||||
|
private:
|
||||||
|
auto handleToggle(::GdkEventButton* const& e) -> bool;
|
||||||
|
|
||||||
|
const std::unique_ptr<::GDBusConnection, void(*)(::GDBusConnection*)> dbus_;
|
||||||
|
const std::string inhibitors_;
|
||||||
|
int handle_ = -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace waybar::modules
|
|
@ -24,8 +24,6 @@ class KeyboardState : public AModule {
|
||||||
auto update() -> void;
|
auto update() -> void;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static auto openDevice(const std::string&) -> std::pair<int, libevdev*>;
|
|
||||||
|
|
||||||
Gtk::Box box_;
|
Gtk::Box box_;
|
||||||
Gtk::Label numlock_label_;
|
Gtk::Label numlock_label_;
|
||||||
Gtk::Label capslock_label_;
|
Gtk::Label capslock_label_;
|
||||||
|
|
|
@ -73,7 +73,8 @@ class Network : public ALabel {
|
||||||
int cidr_;
|
int cidr_;
|
||||||
int32_t signal_strength_dbm_;
|
int32_t signal_strength_dbm_;
|
||||||
uint8_t signal_strength_;
|
uint8_t signal_strength_;
|
||||||
uint32_t frequency_;
|
std::string signal_strength_app_;
|
||||||
|
float frequency_;
|
||||||
uint32_t route_priority;
|
uint32_t route_priority;
|
||||||
|
|
||||||
util::SleeperThread thread_;
|
util::SleeperThread thread_;
|
||||||
|
|
|
@ -2,7 +2,8 @@
|
||||||
|
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
#include "ALabel.hpp"
|
|
||||||
|
#include "AIconLabel.hpp"
|
||||||
#include "bar.hpp"
|
#include "bar.hpp"
|
||||||
#include "client.hpp"
|
#include "client.hpp"
|
||||||
#include "modules/sway/ipc/client.hpp"
|
#include "modules/sway/ipc/client.hpp"
|
||||||
|
@ -10,7 +11,7 @@
|
||||||
|
|
||||||
namespace waybar::modules::sway {
|
namespace waybar::modules::sway {
|
||||||
|
|
||||||
class Window : public ALabel, public sigc::trackable {
|
class Window : public AIconLabel, public sigc::trackable {
|
||||||
public:
|
public:
|
||||||
Window(const std::string&, const waybar::Bar&, const Json::Value&);
|
Window(const std::string&, const waybar::Bar&, const Json::Value&);
|
||||||
~Window() = default;
|
~Window() = default;
|
||||||
|
@ -23,6 +24,7 @@ class Window : public ALabel, public sigc::trackable {
|
||||||
std::string& output);
|
std::string& output);
|
||||||
void getTree();
|
void getTree();
|
||||||
std::string rewriteTitle(const std::string& title);
|
std::string rewriteTitle(const std::string& title);
|
||||||
|
void updateAppIcon();
|
||||||
|
|
||||||
const Bar& bar_;
|
const Bar& bar_;
|
||||||
std::string window_;
|
std::string window_;
|
||||||
|
|
|
@ -3,11 +3,13 @@
|
||||||
#include "AModule.hpp"
|
#include "AModule.hpp"
|
||||||
#include "bar.hpp"
|
#include "bar.hpp"
|
||||||
#include "client.hpp"
|
#include "client.hpp"
|
||||||
|
#include "giomm/desktopappinfo.h"
|
||||||
#include "util/json.hpp"
|
#include "util/json.hpp"
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
|
|
||||||
#include <gdk/gdk.h>
|
#include <gdk/gdk.h>
|
||||||
|
@ -61,15 +63,18 @@ class Task
|
||||||
Gtk::Image icon_;
|
Gtk::Image icon_;
|
||||||
Gtk::Label text_before_;
|
Gtk::Label text_before_;
|
||||||
Gtk::Label text_after_;
|
Gtk::Label text_after_;
|
||||||
|
Glib::RefPtr<Gio::DesktopAppInfo> app_info_;
|
||||||
bool button_visible_ = false;
|
bool button_visible_ = false;
|
||||||
bool ignored_ = false;
|
bool ignored_ = false;
|
||||||
|
|
||||||
bool with_icon_;
|
bool with_icon_ = false;
|
||||||
|
bool with_name_ = false;
|
||||||
std::string format_before_;
|
std::string format_before_;
|
||||||
std::string format_after_;
|
std::string format_after_;
|
||||||
|
|
||||||
std::string format_tooltip_;
|
std::string format_tooltip_;
|
||||||
|
|
||||||
|
std::string name_;
|
||||||
std::string title_;
|
std::string title_;
|
||||||
std::string app_id_;
|
std::string app_id_;
|
||||||
uint32_t state_ = 0;
|
uint32_t state_ = 0;
|
||||||
|
@ -77,6 +82,8 @@ class Task
|
||||||
private:
|
private:
|
||||||
std::string repr() const;
|
std::string repr() const;
|
||||||
std::string state_string(bool = false) const;
|
std::string state_string(bool = false) const;
|
||||||
|
void set_app_info_from_app_id_list(const std::string& app_id_list);
|
||||||
|
bool image_load_icon(Gtk::Image& image, const Glib::RefPtr<Gtk::IconTheme>& icon_theme, Glib::RefPtr<Gio::DesktopAppInfo> app_info, int size);
|
||||||
void hide_if_ignored();
|
void hide_if_ignored();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -136,6 +143,7 @@ class Taskbar : public waybar::AModule
|
||||||
|
|
||||||
std::vector<Glib::RefPtr<Gtk::IconTheme>> icon_themes_;
|
std::vector<Glib::RefPtr<Gtk::IconTheme>> icon_themes_;
|
||||||
std::unordered_set<std::string> ignore_list_;
|
std::unordered_set<std::string> ignore_list_;
|
||||||
|
std::map<std::string, std::string> app_ids_replace_map_;
|
||||||
|
|
||||||
struct zwlr_foreign_toplevel_manager_v1 *manager_;
|
struct zwlr_foreign_toplevel_manager_v1 *manager_;
|
||||||
struct wl_seat *seat_;
|
struct wl_seat *seat_;
|
||||||
|
@ -158,8 +166,9 @@ class Taskbar : public waybar::AModule
|
||||||
bool show_output(struct wl_output *) const;
|
bool show_output(struct wl_output *) const;
|
||||||
bool all_outputs() const;
|
bool all_outputs() const;
|
||||||
|
|
||||||
std::vector<Glib::RefPtr<Gtk::IconTheme>> icon_themes() const;
|
const std::vector<Glib::RefPtr<Gtk::IconTheme>>& icon_themes() const;
|
||||||
const std::unordered_set<std::string>& ignore_list() const;
|
const std::unordered_set<std::string>& ignore_list() const;
|
||||||
|
const std::map<std::string, std::string>& app_ids_replace_map() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
} /* namespace waybar::modules::wlr */
|
} /* namespace waybar::modules::wlr */
|
||||||
|
|
|
@ -68,7 +68,10 @@ inline int close(FILE* fp, pid_t pid) {
|
||||||
inline FILE* open(const std::string& cmd, int& pid) {
|
inline FILE* open(const std::string& cmd, int& pid) {
|
||||||
if (cmd == "") return nullptr;
|
if (cmd == "") return nullptr;
|
||||||
int fd[2];
|
int fd[2];
|
||||||
pipe(fd);
|
if (pipe(fd) != 0){
|
||||||
|
spdlog::error("Unable to pipe fd");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
pid_t child_pid = fork();
|
pid_t child_pid = fork();
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
|
#include <glibmm/ustring.h>
|
||||||
|
|
||||||
class pow_format {
|
class pow_format {
|
||||||
public:
|
public:
|
||||||
|
@ -84,5 +85,15 @@ namespace fmt {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Glib ustirng support
|
||||||
|
template <>
|
||||||
|
struct formatter<Glib::ustring> : formatter<std::string> {
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(const Glib::ustring& value, FormatContext& ctx) {
|
||||||
|
return formatter<std::string>::format(value, ctx);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
const std::string WHITESPACE = " \n\r\t\f\v";
|
const std::string WHITESPACE = " \n\r\t\f\v";
|
||||||
|
|
||||||
std::string ltrim(const std::string s) {
|
inline std::string ltrim(const std::string& s) {
|
||||||
size_t begin = s.find_first_not_of(WHITESPACE);
|
size_t begin = s.find_first_not_of(WHITESPACE);
|
||||||
return (begin == std::string::npos) ? "" : s.substr(begin);
|
return (begin == std::string::npos) ? "" : s.substr(begin);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string rtrim(const std::string s) {
|
inline std::string rtrim(const std::string& s) {
|
||||||
size_t end = s.find_last_not_of(WHITESPACE);
|
size_t end = s.find_last_not_of(WHITESPACE);
|
||||||
return (end == std::string::npos) ? "" : s.substr(0, end + 1);
|
return (end == std::string::npos) ? "" : s.substr(0, end + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string trim(const std::string& s) { return rtrim(ltrim(s)); }
|
inline std::string trim(const std::string& s) { return rtrim(ltrim(s)); }
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <date/tz.h>
|
||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
|
namespace waybar {
|
||||||
|
|
||||||
|
struct waybar_time {
|
||||||
|
std::locale locale;
|
||||||
|
date::zoned_seconds ztime;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace waybar
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct fmt::formatter<waybar::waybar_time> {
|
||||||
|
std::string_view specs;
|
||||||
|
|
||||||
|
template <typename ParseContext>
|
||||||
|
constexpr auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||||
|
auto it = ctx.begin();
|
||||||
|
if (it != ctx.end() && *it == ':') {
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
auto end = it;
|
||||||
|
while (end != ctx.end() && *end != '}') {
|
||||||
|
++end;
|
||||||
|
}
|
||||||
|
if (end != it) {
|
||||||
|
specs = {it, std::string_view::size_type(end - it)};
|
||||||
|
}
|
||||||
|
return end;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(const waybar::waybar_time& t, FormatContext& ctx) {
|
||||||
|
return format_to(ctx.out(), "{}", date::format(t.locale, fmt::to_string(specs), t.ztime));
|
||||||
|
}
|
||||||
|
};
|
|
@ -86,6 +86,11 @@ The *clock* module displays the current date and time.
|
||||||
typeof: double ++
|
typeof: double ++
|
||||||
Threshold to be used when scrolling.
|
Threshold to be used when scrolling.
|
||||||
|
|
||||||
|
*tooltip*: ++
|
||||||
|
typeof: bool ++
|
||||||
|
default: true ++
|
||||||
|
Option to disable tooltip on hover.
|
||||||
|
|
||||||
View all valid format options in *strftime(3)*.
|
View all valid format options in *strftime(3)*.
|
||||||
|
|
||||||
# FORMAT REPLACEMENTS
|
# FORMAT REPLACEMENTS
|
||||||
|
|
|
@ -151,7 +151,8 @@ $text\\n$tooltip\\n$class*
|
||||||
"max-length": 40,
|
"max-length": 40,
|
||||||
"interval": 30, // Remove this if your script is endless and write in loop
|
"interval": 30, // Remove this if your script is endless and write in loop
|
||||||
"exec": "$HOME/.config/waybar/mediaplayer.sh 2> /dev/null", // Script in resources folder
|
"exec": "$HOME/.config/waybar/mediaplayer.sh 2> /dev/null", // Script in resources folder
|
||||||
"exec-if": "pgrep spotify"
|
"exec-if": "pgrep spotify",
|
||||||
|
"return-type": "json"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
waybar-inhibitor(5)
|
||||||
|
|
||||||
|
# NAME
|
||||||
|
|
||||||
|
waybar - inhibitor module
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
|
||||||
|
The *inhibitor* module allows to take an inhibitor lock that logind provides.
|
||||||
|
See *systemd-inhibit*(1) for more information.
|
||||||
|
|
||||||
|
# CONFIGURATION
|
||||||
|
|
||||||
|
*what*: ++
|
||||||
|
typeof: string or array ++
|
||||||
|
The inhibitor lock or locks that should be taken when active. The available inhibitor locks are *idle*, *shutdown*, *sleep*, *handle-power-key*, *handle-suspend-key*, *handle-hibernate-key* and *handle-lid-switch*.
|
||||||
|
|
||||||
|
*format*: ++
|
||||||
|
typeof: string ++
|
||||||
|
The format, how the state should be displayed.
|
||||||
|
|
||||||
|
*format-icons*: ++
|
||||||
|
typeof: array ++
|
||||||
|
Based on the current state, the corresponding icon gets selected.
|
||||||
|
|
||||||
|
*rotate*: ++
|
||||||
|
typeof: integer ++
|
||||||
|
Positive value to rotate the text label.
|
||||||
|
|
||||||
|
*max-length*: ++
|
||||||
|
typeof: integer ++
|
||||||
|
The maximum length in character the module should display.
|
||||||
|
|
||||||
|
*min-length*: ++
|
||||||
|
typeof: integer ++
|
||||||
|
The minimum length in characters the module should take up.
|
||||||
|
|
||||||
|
*align*: ++
|
||||||
|
typeof: float ++
|
||||||
|
The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text.
|
||||||
|
|
||||||
|
*on-click*: ++
|
||||||
|
typeof: string ++
|
||||||
|
Command to execute when clicked on the module. A click also toggles the state
|
||||||
|
|
||||||
|
*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.
|
||||||
|
|
||||||
|
*on-scroll-down*: ++
|
||||||
|
typeof: string ++
|
||||||
|
Command to execute when scrolling down on the module.
|
||||||
|
|
||||||
|
*smooth-scrolling-threshold*: ++
|
||||||
|
typeof: double ++
|
||||||
|
Threshold to be used when scrolling.
|
||||||
|
|
||||||
|
*tooltip*: ++
|
||||||
|
typeof: bool ++
|
||||||
|
default: true ++
|
||||||
|
Option to disable tooltip on hover.
|
||||||
|
|
||||||
|
# FORMAT REPLACEMENTS
|
||||||
|
|
||||||
|
*{status}*: status (*activated* or *deactivated*)
|
||||||
|
|
||||||
|
*{icon}*: Icon, as defined in *format-icons*
|
||||||
|
|
||||||
|
# EXAMPLES
|
||||||
|
|
||||||
|
```
|
||||||
|
"inhibitor": {
|
||||||
|
"what": "handle-lid-switch",
|
||||||
|
"format": "{icon}",
|
||||||
|
"format-icons": {
|
||||||
|
"activated": "",
|
||||||
|
"deactivated": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
|
@ -8,6 +8,8 @@ waybar - keyboard-state module
|
||||||
|
|
||||||
The *keyboard-state* module displays the state of number lock, caps lock, and scroll lock.
|
The *keyboard-state* module displays the state of number lock, caps lock, and scroll lock.
|
||||||
|
|
||||||
|
You must be a member of the input group to use this module.
|
||||||
|
|
||||||
# CONFIGURATION
|
# CONFIGURATION
|
||||||
|
|
||||||
*interval*: ++
|
*interval*: ++
|
||||||
|
|
|
@ -84,12 +84,20 @@ Addressed by *memory*
|
||||||
|
|
||||||
*{percentage}*: Percentage of memory in use.
|
*{percentage}*: Percentage of memory in use.
|
||||||
|
|
||||||
|
*{swapPercentage}*: Percentage of swap in use.
|
||||||
|
|
||||||
*{total}*: Amount of total memory available in GiB.
|
*{total}*: Amount of total memory available in GiB.
|
||||||
|
|
||||||
|
*{swapTotal}*: Amount of total swap available in GiB.
|
||||||
|
|
||||||
*{used}*: Amount of used memory in GiB.
|
*{used}*: Amount of used memory in GiB.
|
||||||
|
|
||||||
|
*{swapUsed}*: Amount of used swap in GiB.
|
||||||
|
|
||||||
*{avail}*: Amount of available memory in GiB.
|
*{avail}*: Amount of available memory in GiB.
|
||||||
|
|
||||||
|
*{swapAvail}*: Amount of available swap in GiB.
|
||||||
|
|
||||||
# EXAMPLES
|
# EXAMPLES
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
|
@ -69,7 +69,7 @@ Addressed by *sway/mode*
|
||||||
# EXAMPLES
|
# EXAMPLES
|
||||||
|
|
||||||
```
|
```
|
||||||
"sway/window": {
|
"sway/mode": {
|
||||||
"format": " {}",
|
"format": " {}",
|
||||||
"max-length": 50
|
"max-length": 50
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,6 +70,11 @@ Addressed by *sway/window*
|
||||||
typeof: object ++
|
typeof: object ++
|
||||||
Rules to rewrite window title. See *rewrite rules*.
|
Rules to rewrite window title. See *rewrite rules*.
|
||||||
|
|
||||||
|
*icon*: ++
|
||||||
|
typeof: bool ++
|
||||||
|
default: true ++
|
||||||
|
Option to hide the application icon.
|
||||||
|
|
||||||
# REWRITE RULES
|
# REWRITE RULES
|
||||||
|
|
||||||
*rewrite* is an object where keys are regular expressions and values are
|
*rewrite* is an object where keys are regular expressions and values are
|
||||||
|
|
|
@ -69,10 +69,6 @@ Addressed by *sway/workspaces*
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
Command to execute when the module is updated.
|
Command to execute when the module is updated.
|
||||||
|
|
||||||
*numeric-first*: ++
|
|
||||||
typeof: bool ++
|
|
||||||
Whether to put workspaces starting with numbers before workspaces that do not start with a number.
|
|
||||||
|
|
||||||
*disable-auto-back-and-forth*: ++
|
*disable-auto-back-and-forth*: ++
|
||||||
typeof: bool ++
|
typeof: bool ++
|
||||||
Whether to disable *workspace_auto_back_and_forth* when clicking on workspaces. If this is set to *true*, clicking on a workspace you are already on won't do anything, even if *workspace_auto_back_and_forth* is enabled in the Sway configuration.
|
Whether to disable *workspace_auto_back_and_forth* when clicking on workspaces. If this is set to *true*, clicking on a workspace you are already on won't do anything, even if *workspace_auto_back_and_forth* is enabled in the Sway configuration.
|
||||||
|
@ -120,7 +116,6 @@ n.b.: the list of outputs can be obtained from command line using *swaymsg -t ge
|
||||||
"sway/workspaces": {
|
"sway/workspaces": {
|
||||||
"disable-scroll": true,
|
"disable-scroll": true,
|
||||||
"all-outputs": true,
|
"all-outputs": true,
|
||||||
"numeric-first": false,
|
|
||||||
"format": "{name}: {icon}",
|
"format": "{name}: {icon}",
|
||||||
"format-icons": {
|
"format-icons": {
|
||||||
"1": "",
|
"1": "",
|
||||||
|
|
|
@ -29,6 +29,10 @@ Addressed by *tray*
|
||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
Defines the spacing between the tray icons.
|
Defines the spacing between the tray icons.
|
||||||
|
|
||||||
|
*reverse-direction*: ++
|
||||||
|
typeof: bool ++
|
||||||
|
Defines if new app icons should be added in a reverse order
|
||||||
|
|
||||||
*on-update*: ++
|
*on-update*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
Command to execute when the module is updated.
|
Command to execute when the module is updated.
|
||||||
|
|
|
@ -72,10 +72,16 @@ Addressed by *wlr/taskbar*
|
||||||
typeof: array ++
|
typeof: array ++
|
||||||
List of app_id/titles to be invisible.
|
List of app_id/titles to be invisible.
|
||||||
|
|
||||||
|
*app_ids-mapping*: ++
|
||||||
|
typeof: object ++
|
||||||
|
Dictionary of app_id to be replaced with
|
||||||
|
|
||||||
# FORMAT REPLACEMENTS
|
# FORMAT REPLACEMENTS
|
||||||
|
|
||||||
*{icon}*: The icon of the application.
|
*{icon}*: The icon of the application.
|
||||||
|
|
||||||
|
*{title}*: The application name as in desktop file if appropriate desktop fils found, otherwise same as {app_id}
|
||||||
|
|
||||||
*{title}*: The title of the application.
|
*{title}*: The title of the application.
|
||||||
|
|
||||||
*{app_id}*: The app_id (== application name) of the application.
|
*{app_id}*: The app_id (== application name) of the application.
|
||||||
|
@ -87,10 +93,15 @@ Addressed by *wlr/taskbar*
|
||||||
# CLICK ACTIONS
|
# CLICK ACTIONS
|
||||||
|
|
||||||
*activate*: Bring the application into foreground.
|
*activate*: Bring the application into foreground.
|
||||||
|
|
||||||
*minimize*: Toggle application's minimized state.
|
*minimize*: Toggle application's minimized state.
|
||||||
|
|
||||||
*minimize-raise*: Bring the application into foreground or toggle its minimized state.
|
*minimize-raise*: Bring the application into foreground or toggle its minimized state.
|
||||||
|
|
||||||
*maximize*: Toggle application's maximized state.
|
*maximize*: Toggle application's maximized state.
|
||||||
|
|
||||||
*fullscreen*: Toggle application's fullscreen state.
|
*fullscreen*: Toggle application's fullscreen state.
|
||||||
|
|
||||||
*close*: Close the application.
|
*close*: Close the application.
|
||||||
|
|
||||||
# EXAMPLES
|
# EXAMPLES
|
||||||
|
@ -105,7 +116,10 @@ Addressed by *wlr/taskbar*
|
||||||
"on-click-middle": "close",
|
"on-click-middle": "close",
|
||||||
"ignore-list": [
|
"ignore-list": [
|
||||||
"Alacritty"
|
"Alacritty"
|
||||||
]
|
],
|
||||||
|
"app_ids-mapping": {
|
||||||
|
"firefoxdeveloperedition": "firefox-developer-edition"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -52,6 +52,7 @@ Addressed by *wlr/workspaces*
|
||||||
# CLICK ACTIONS
|
# CLICK ACTIONS
|
||||||
|
|
||||||
*activate*: Switch to workspace.
|
*activate*: Switch to workspace.
|
||||||
|
|
||||||
*close*: Close the workspace.
|
*close*: Close the workspace.
|
||||||
|
|
||||||
# ICONS
|
# ICONS
|
||||||
|
|
|
@ -82,6 +82,12 @@ Also a minimal example configuration can be found on the at the bottom of this m
|
||||||
default: *true* ++
|
default: *true* ++
|
||||||
Option to request an exclusive zone from the compositor. Disable this to allow drawing application windows underneath or on top of the bar.
|
Option to request an exclusive zone from the compositor. Disable this to allow drawing application windows underneath or on top of the bar.
|
||||||
|
|
||||||
|
*fixed-center* ++
|
||||||
|
typeof: bool ++
|
||||||
|
default: *true*
|
||||||
|
Prefer fixed center position for the `modules-center` block. The center block will stay in the middle of the bar whenever possible. It can still be pushed around if other blocks need more space.
|
||||||
|
When false, the center block is centered in the space between the left and right block.
|
||||||
|
|
||||||
*passthrough* ++
|
*passthrough* ++
|
||||||
typeof: bool ++
|
typeof: bool ++
|
||||||
default: *false* ++
|
default: *false* ++
|
||||||
|
|
16
meson.build
16
meson.build
|
@ -1,6 +1,6 @@
|
||||||
project(
|
project(
|
||||||
'waybar', 'cpp', 'c',
|
'waybar', 'cpp', 'c',
|
||||||
version: '0.9.8',
|
version: '0.9.10',
|
||||||
license: 'MIT',
|
license: 'MIT',
|
||||||
meson_version: '>= 0.49.0',
|
meson_version: '>= 0.49.0',
|
||||||
default_options : [
|
default_options : [
|
||||||
|
@ -79,14 +79,14 @@ is_netbsd = host_machine.system() == 'netbsd'
|
||||||
is_openbsd = host_machine.system() == 'openbsd'
|
is_openbsd = host_machine.system() == 'openbsd'
|
||||||
|
|
||||||
thread_dep = dependency('threads')
|
thread_dep = dependency('threads')
|
||||||
fmt = dependency('fmt', version : ['>=5.3.0'], fallback : ['fmt', 'fmt_dep'])
|
fmt = dependency('fmt', version : ['>=7.0.0'], fallback : ['fmt', 'fmt_dep'])
|
||||||
spdlog = dependency('spdlog', version : ['>=1.8.5'], fallback : ['spdlog', 'spdlog_dep'], default_options : ['external_fmt=true'])
|
spdlog = dependency('spdlog', version : ['>=1.8.5'], fallback : ['spdlog', 'spdlog_dep'], default_options : ['external_fmt=true'])
|
||||||
wayland_client = dependency('wayland-client')
|
wayland_client = dependency('wayland-client')
|
||||||
wayland_cursor = dependency('wayland-cursor')
|
wayland_cursor = dependency('wayland-cursor')
|
||||||
wayland_protos = dependency('wayland-protocols')
|
wayland_protos = dependency('wayland-protocols')
|
||||||
gtkmm = dependency('gtkmm-3.0', version : ['>=3.22.0'])
|
gtkmm = dependency('gtkmm-3.0', version : ['>=3.22.0'])
|
||||||
dbusmenu_gtk = dependency('dbusmenu-gtk3-0.4', required: get_option('dbusmenu-gtk'))
|
dbusmenu_gtk = dependency('dbusmenu-gtk3-0.4', required: get_option('dbusmenu-gtk'))
|
||||||
giounix = dependency('gio-unix-2.0', required: get_option('dbusmenu-gtk'))
|
giounix = dependency('gio-unix-2.0', required: (get_option('dbusmenu-gtk').enabled() or get_option('logind').enabled()))
|
||||||
jsoncpp = dependency('jsoncpp')
|
jsoncpp = dependency('jsoncpp')
|
||||||
sigcpp = dependency('sigc++-2.0')
|
sigcpp = dependency('sigc++-2.0')
|
||||||
libepoll = dependency('epoll-shim', required: false)
|
libepoll = dependency('epoll-shim', required: false)
|
||||||
|
@ -142,6 +142,7 @@ src_files = files(
|
||||||
'src/factory.cpp',
|
'src/factory.cpp',
|
||||||
'src/AModule.cpp',
|
'src/AModule.cpp',
|
||||||
'src/ALabel.cpp',
|
'src/ALabel.cpp',
|
||||||
|
'src/AIconLabel.cpp',
|
||||||
'src/modules/custom.cpp',
|
'src/modules/custom.cpp',
|
||||||
'src/modules/disk.cpp',
|
'src/modules/disk.cpp',
|
||||||
'src/modules/idle_inhibitor.cpp',
|
'src/modules/idle_inhibitor.cpp',
|
||||||
|
@ -242,6 +243,11 @@ if libsndio.found()
|
||||||
src_files += 'src/modules/sndio.cpp'
|
src_files += 'src/modules/sndio.cpp'
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
if (giounix.found() and not get_option('logind').disabled())
|
||||||
|
add_project_arguments('-DHAVE_GIO_UNIX', language: 'cpp')
|
||||||
|
src_files += 'src/modules/inhibitor.cpp'
|
||||||
|
endif
|
||||||
|
|
||||||
if get_option('rfkill').enabled()
|
if get_option('rfkill').enabled()
|
||||||
if is_linux
|
if is_linux
|
||||||
add_project_arguments('-DWANT_RFKILL', language: 'cpp')
|
add_project_arguments('-DWANT_RFKILL', language: 'cpp')
|
||||||
|
@ -347,6 +353,10 @@ if scdoc.found()
|
||||||
'waybar-sndio.5.scd',
|
'waybar-sndio.5.scd',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
if (giounix.found() and not get_option('logind').disabled())
|
||||||
|
man_files += 'waybar-inhibitor.5.scd'
|
||||||
|
endif
|
||||||
|
|
||||||
foreach file : man_files
|
foreach file : man_files
|
||||||
path = '@0@'.format(file)
|
path = '@0@'.format(file)
|
||||||
basename = path.split('/')[-1]
|
basename = path.split('/')[-1]
|
||||||
|
|
|
@ -10,5 +10,6 @@ option('mpd', type: 'feature', value: 'auto', description: 'Enable support for t
|
||||||
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('rfkill', type: 'feature', value: 'auto', description: 'Enable support for RFKILL')
|
||||||
option('sndio', type: 'feature', value: 'auto', description: 'Enable support for sndio')
|
option('sndio', type: 'feature', value: 'auto', description: 'Enable support for sndio')
|
||||||
|
option('logind', type: 'feature', value: 'auto', description: 'Enable support for logind')
|
||||||
option('tests', type: 'feature', value: 'auto', description: 'Enable tests')
|
option('tests', type: 'feature', value: 'auto', description: 'Enable tests')
|
||||||
option('experimental', type : 'boolean', value : false, description: 'Enable experimental features')
|
option('experimental', type : 'boolean', value : false, description: 'Enable experimental features')
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
* {
|
* {
|
||||||
border: none;
|
|
||||||
border-radius: 0;
|
|
||||||
/* `otf-font-awesome` is required to be installed for icons */
|
/* `otf-font-awesome` is required to be installed for icons */
|
||||||
font-family: Roboto, Helvetica, Arial, sans-serif;
|
font-family: FontAwesome, Roboto, Helvetica, Arial, sans-serif;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
min-height: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
window#waybar {
|
window#waybar {
|
||||||
|
@ -43,6 +40,9 @@ window#waybar.chromium {
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
/* Use box-shadow instead of border so the text isn't offset */
|
/* Use box-shadow instead of border so the text isn't offset */
|
||||||
box-shadow: inset 0 -3px transparent;
|
box-shadow: inset 0 -3px transparent;
|
||||||
|
/* Avoid rounded borders under each workspace name */
|
||||||
|
border: none;
|
||||||
|
border-radius: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 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 */
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
#include "AIconLabel.hpp"
|
||||||
|
|
||||||
|
#include <gdkmm/pixbuf.h>
|
||||||
|
|
||||||
|
namespace waybar {
|
||||||
|
|
||||||
|
AIconLabel::AIconLabel(const Json::Value &config, const std::string &name, const std::string &id,
|
||||||
|
const std::string &format, uint16_t interval, bool ellipsize,
|
||||||
|
bool enable_click, bool enable_scroll)
|
||||||
|
: ALabel(config, name, id, format, interval, ellipsize, enable_click, enable_scroll) {
|
||||||
|
event_box_.remove();
|
||||||
|
box_.set_orientation(Gtk::Orientation::ORIENTATION_HORIZONTAL);
|
||||||
|
box_.set_spacing(8);
|
||||||
|
box_.add(image_);
|
||||||
|
box_.add(label_);
|
||||||
|
event_box_.add(box_);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto AIconLabel::update() -> void {
|
||||||
|
image_.set_visible(image_.get_visible() && iconEnabled());
|
||||||
|
ALabel::update();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AIconLabel::iconEnabled() const {
|
||||||
|
return config_["icon"].isBool() ? config_["icon"].asBool() : true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace waybar
|
|
@ -6,7 +6,9 @@ namespace waybar {
|
||||||
|
|
||||||
AModule::AModule(const Json::Value& config, const std::string& name, const std::string& id,
|
AModule::AModule(const Json::Value& config, const std::string& name, const std::string& id,
|
||||||
bool enable_click, bool enable_scroll)
|
bool enable_click, bool enable_scroll)
|
||||||
: name_(std::move(name)), config_(std::move(config)) {
|
: name_(std::move(name)), config_(std::move(config))
|
||||||
|
, distance_scrolled_y_(0.0)
|
||||||
|
, distance_scrolled_x_(0.0) {
|
||||||
// configure events' user commands
|
// configure events' user commands
|
||||||
if (config_["on-click"].isString() || config_["on-click-middle"].isString() ||
|
if (config_["on-click"].isString() || config_["on-click-middle"].isString() ||
|
||||||
config_["on-click-backward"].isString() || config_["on-click-forward"].isString() ||
|
config_["on-click-backward"].isString() || config_["on-click-forward"].isString() ||
|
||||||
|
|
15
src/bar.cpp
15
src/bar.cpp
|
@ -735,21 +735,22 @@ void waybar::Bar::getModules(const Factory& factory, const std::string& pos, Gtk
|
||||||
module = factory.makeModule(ref);
|
module = factory.makeModule(ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
modules_all_.emplace_back(module);
|
std::shared_ptr<AModule> module_sp(module);
|
||||||
|
modules_all_.emplace_back(module_sp);
|
||||||
if (group) {
|
if (group) {
|
||||||
group->pack_start(*module, false, false);
|
group->pack_start(*module, false, false);
|
||||||
} else {
|
} else {
|
||||||
if (pos == "modules-left") {
|
if (pos == "modules-left") {
|
||||||
modules_left_.emplace_back(module);
|
modules_left_.emplace_back(module_sp);
|
||||||
}
|
}
|
||||||
if (pos == "modules-center") {
|
if (pos == "modules-center") {
|
||||||
modules_center_.emplace_back(module);
|
modules_center_.emplace_back(module_sp);
|
||||||
}
|
}
|
||||||
if (pos == "modules-right") {
|
if (pos == "modules-right") {
|
||||||
modules_right_.emplace_back(module);
|
modules_right_.emplace_back(module_sp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
module->dp.connect([module, &name] {
|
module->dp.connect([module, name] {
|
||||||
try {
|
try {
|
||||||
module->update();
|
module->update();
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
|
@ -766,7 +767,11 @@ void waybar::Bar::getModules(const Factory& factory, const std::string& pos, Gtk
|
||||||
auto waybar::Bar::setupWidgets() -> void {
|
auto waybar::Bar::setupWidgets() -> void {
|
||||||
window.add(box_);
|
window.add(box_);
|
||||||
box_.pack_start(left_, false, false);
|
box_.pack_start(left_, false, false);
|
||||||
|
if (config["fixed-center"].isBool() ? config["fixed-center"].asBool() : true) {
|
||||||
box_.set_center_widget(center_);
|
box_.set_center_widget(center_);
|
||||||
|
} else {
|
||||||
|
box_.pack_start(center_, true, false);
|
||||||
|
}
|
||||||
box_.pack_end(right_, false, false);
|
box_.pack_end(right_, false, false);
|
||||||
|
|
||||||
// Convert to button code for every module that is used.
|
// Convert to button code for every module that is used.
|
||||||
|
|
|
@ -94,6 +94,11 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const {
|
||||||
if (ref == "sndio") {
|
if (ref == "sndio") {
|
||||||
return new waybar::modules::Sndio(id, config_[name]);
|
return new waybar::modules::Sndio(id, config_[name]);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_GIO_UNIX
|
||||||
|
if (ref == "inhibitor") {
|
||||||
|
return new waybar::modules::Inhibitor(id, bar_, 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]);
|
||||||
|
|
|
@ -161,7 +161,7 @@ const std::tuple<uint8_t, float, std::string, float> waybar::modules::Battery::g
|
||||||
uint32_t energy_now;
|
uint32_t energy_now;
|
||||||
uint32_t energy_full_design;
|
uint32_t energy_full_design;
|
||||||
std::string _status;
|
std::string _status;
|
||||||
std::ifstream(bat / "status") >> _status;
|
std::getline(std::ifstream(bat / "status"), _status);
|
||||||
|
|
||||||
// Some battery will report current and charge in μA/μAh.
|
// Some battery will report current and charge in μA/μAh.
|
||||||
// Scale these by the voltage to get μW/μWh.
|
// Scale these by the voltage to get μW/μWh.
|
||||||
|
|
|
@ -1,17 +1,24 @@
|
||||||
#include "modules/clock.hpp"
|
#include "modules/clock.hpp"
|
||||||
|
|
||||||
#include <time.h>
|
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
|
#if FMT_VERSION < 60000
|
||||||
|
#include <fmt/time.h>
|
||||||
|
#else
|
||||||
|
#include <fmt/chrono.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <ctime>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
#include "util/ustring_clen.hpp"
|
#include "util/ustring_clen.hpp"
|
||||||
|
#include "util/waybar_time.hpp"
|
||||||
#ifdef HAVE_LANGINFO_1STDAY
|
#ifdef HAVE_LANGINFO_1STDAY
|
||||||
#include <langinfo.h>
|
#include <langinfo.h>
|
||||||
#include <locale.h>
|
#include <locale.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
using waybar::modules::waybar_time;
|
using waybar::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, false, false, true),
|
: ALabel(config, "clock", id, "{:%H:%M}", 60, false, false, true),
|
||||||
|
@ -96,7 +103,7 @@ auto waybar::modules::Clock::update() -> void {
|
||||||
// As date dep is not fully compatible, prefer fmt
|
// As date dep is not fully compatible, prefer fmt
|
||||||
tzset();
|
tzset();
|
||||||
auto localtime = fmt::localtime(std::chrono::system_clock::to_time_t(now));
|
auto localtime = fmt::localtime(std::chrono::system_clock::to_time_t(now));
|
||||||
text = fmt::format(format_, localtime);
|
text = fmt::format(locale_, format_, localtime);
|
||||||
} else {
|
} else {
|
||||||
text = fmt::format(format_, wtime);
|
text = fmt::format(format_, wtime);
|
||||||
}
|
}
|
||||||
|
@ -114,10 +121,10 @@ auto waybar::modules::Clock::update() -> void {
|
||||||
}
|
}
|
||||||
auto tooltip_format = config_["tooltip-format"].asString();
|
auto tooltip_format = config_["tooltip-format"].asString();
|
||||||
text = fmt::format(tooltip_format, wtime, fmt::arg(kCalendarPlaceholder.c_str(), calendar_lines), fmt::arg(KTimezonedTimeListPlaceholder.c_str(), timezoned_time_lines));
|
text = fmt::format(tooltip_format, wtime, fmt::arg(kCalendarPlaceholder.c_str(), calendar_lines), fmt::arg(KTimezonedTimeListPlaceholder.c_str(), timezoned_time_lines));
|
||||||
|
label_.set_tooltip_markup(text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
label_.set_tooltip_markup(text);
|
|
||||||
// Call parent update
|
// Call parent update
|
||||||
ALabel::update();
|
ALabel::update();
|
||||||
}
|
}
|
||||||
|
@ -255,14 +262,3 @@ auto waybar::modules::Clock::first_day_of_week() -> date::weekday {
|
||||||
#endif
|
#endif
|
||||||
return date::Sunday;
|
return date::Sunday;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
|
||||||
struct fmt::formatter<waybar_time> : fmt::formatter<std::tm> {
|
|
||||||
template <typename FormatContext>
|
|
||||||
auto format(const waybar_time& t, FormatContext& ctx) {
|
|
||||||
#if FMT_VERSION >= 80000
|
|
||||||
auto& tm_format = specs;
|
|
||||||
#endif
|
|
||||||
return format_to(ctx.out(), "{}", date::format(t.locale, fmt::to_string(tm_format), t.ztime));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
|
@ -62,7 +62,7 @@ auto waybar::modules::Cpu::update() -> void {
|
||||||
double waybar::modules::Cpu::getCpuLoad() {
|
double waybar::modules::Cpu::getCpuLoad() {
|
||||||
double load[1];
|
double load[1];
|
||||||
if (getloadavg(load, 1) != -1) {
|
if (getloadavg(load, 1) != -1) {
|
||||||
return load[0];
|
return std::ceil(load[0] * 100.0) / 100.0;
|
||||||
}
|
}
|
||||||
throw std::runtime_error("Can't get Cpu load");
|
throw std::runtime_error("Can't get Cpu load");
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,175 @@
|
||||||
|
#include "modules/inhibitor.hpp"
|
||||||
|
|
||||||
|
#include <gio/gio.h>
|
||||||
|
#include <gio/gunixfdlist.h>
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
using DBus = std::unique_ptr<GDBusConnection, void(*)(GDBusConnection*)>;
|
||||||
|
|
||||||
|
auto dbus() -> DBus {
|
||||||
|
GError *error = nullptr;
|
||||||
|
GDBusConnection* connection =
|
||||||
|
g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
spdlog::error("g_bus_get_sync() failed: {}", error->message);
|
||||||
|
g_error_free(error);
|
||||||
|
connection = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto destructor = [](GDBusConnection* connection) {
|
||||||
|
GError *error = nullptr;
|
||||||
|
g_dbus_connection_close_sync(connection, nullptr, &error);
|
||||||
|
if (error) {
|
||||||
|
spdlog::error(
|
||||||
|
"g_bus_connection_close_sync failed(): {}",
|
||||||
|
error->message);
|
||||||
|
g_error_free(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return DBus{connection, destructor};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto getLocks(const DBus& bus, const std::string& inhibitors) -> int {
|
||||||
|
GError *error = nullptr;
|
||||||
|
GUnixFDList* fd_list;
|
||||||
|
int handle;
|
||||||
|
|
||||||
|
auto reply = g_dbus_connection_call_with_unix_fd_list_sync(bus.get(),
|
||||||
|
"org.freedesktop.login1",
|
||||||
|
"/org/freedesktop/login1",
|
||||||
|
"org.freedesktop.login1.Manager",
|
||||||
|
"Inhibit",
|
||||||
|
g_variant_new(
|
||||||
|
"(ssss)",
|
||||||
|
inhibitors.c_str(),
|
||||||
|
"waybar",
|
||||||
|
"Asked by user",
|
||||||
|
"block"),
|
||||||
|
G_VARIANT_TYPE("(h)"),
|
||||||
|
G_DBUS_CALL_FLAGS_NONE,
|
||||||
|
-1,
|
||||||
|
nullptr,
|
||||||
|
&fd_list,
|
||||||
|
nullptr,
|
||||||
|
&error);
|
||||||
|
if (error) {
|
||||||
|
spdlog::error(
|
||||||
|
"g_dbus_connection_call_with_unix_fd_list_sync() failed: {}",
|
||||||
|
error->message);
|
||||||
|
g_error_free(error);
|
||||||
|
handle = -1;
|
||||||
|
} else {
|
||||||
|
gint index;
|
||||||
|
g_variant_get(reply, "(h)", &index);
|
||||||
|
g_variant_unref(reply);
|
||||||
|
handle = g_unix_fd_list_get(fd_list, index, nullptr);
|
||||||
|
g_object_unref(fd_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto checkInhibitor(const std::string& inhibitor) -> const std::string& {
|
||||||
|
static const auto inhibitors = std::array{
|
||||||
|
"idle",
|
||||||
|
"shutdown",
|
||||||
|
"sleep",
|
||||||
|
"handle-power-key",
|
||||||
|
"handle-suspend-key",
|
||||||
|
"handle-hibernate-key",
|
||||||
|
"handle-lid-switch"
|
||||||
|
};
|
||||||
|
|
||||||
|
if (std::find(inhibitors.begin(), inhibitors.end(), inhibitor)
|
||||||
|
== inhibitors.end()) {
|
||||||
|
throw std::runtime_error("invalid logind inhibitor " + inhibitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
return inhibitor;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto getInhibitors(const Json::Value& config) -> std::string {
|
||||||
|
std::string inhibitors = "idle";
|
||||||
|
|
||||||
|
if (config["what"].empty()) {
|
||||||
|
return inhibitors;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config["what"].isString()) {
|
||||||
|
return checkInhibitor(config["what"].asString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config["what"].isArray()) {
|
||||||
|
inhibitors = checkInhibitor(config["what"][0].asString());
|
||||||
|
for (decltype(config["what"].size()) i = 1; i < config["what"].size(); ++i) {
|
||||||
|
inhibitors += ":" + checkInhibitor(config["what"][i].asString());
|
||||||
|
}
|
||||||
|
return inhibitors;
|
||||||
|
}
|
||||||
|
|
||||||
|
return inhibitors;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace waybar::modules {
|
||||||
|
|
||||||
|
Inhibitor::Inhibitor(const std::string& id, const Bar& bar,
|
||||||
|
const Json::Value& config)
|
||||||
|
: ALabel(config, "inhibitor", id, "{status}", true),
|
||||||
|
dbus_(::dbus()),
|
||||||
|
inhibitors_(::getInhibitors(config)) {
|
||||||
|
event_box_.add_events(Gdk::BUTTON_PRESS_MASK);
|
||||||
|
event_box_.signal_button_press_event().connect(
|
||||||
|
sigc::mem_fun(*this, &Inhibitor::handleToggle));
|
||||||
|
dp.emit();
|
||||||
|
}
|
||||||
|
|
||||||
|
Inhibitor::~Inhibitor() {
|
||||||
|
if (handle_ != -1) {
|
||||||
|
::close(handle_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Inhibitor::activated() -> bool {
|
||||||
|
return handle_ != -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Inhibitor::update() -> void {
|
||||||
|
std::string status_text = activated() ? "activated" : "deactivated";
|
||||||
|
|
||||||
|
label_.get_style_context()->remove_class(
|
||||||
|
activated() ? "deactivated" : "activated");
|
||||||
|
label_.set_markup(
|
||||||
|
fmt::format(format_, fmt::arg("status", status_text),
|
||||||
|
fmt::arg("icon", getIcon(0, status_text))));
|
||||||
|
label_.get_style_context()->add_class(status_text);
|
||||||
|
|
||||||
|
if (tooltipEnabled()) {
|
||||||
|
label_.set_tooltip_text(status_text);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ALabel::update();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Inhibitor::handleToggle(GdkEventButton* const& e) -> bool {
|
||||||
|
if (e->button == 1) {
|
||||||
|
if (activated()) {
|
||||||
|
::close(handle_);
|
||||||
|
handle_ = -1;
|
||||||
|
} else {
|
||||||
|
handle_ = ::getLocks(dbus_, inhibitors_);
|
||||||
|
if (handle_ == -1) {
|
||||||
|
spdlog::error("cannot get inhibitor locks");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ALabel::handleToggle(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // waybar::modules
|
|
@ -1,6 +1,8 @@
|
||||||
#include "modules/keyboard_state.hpp"
|
#include "modules/keyboard_state.hpp"
|
||||||
|
#include <errno.h>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
@ -8,6 +10,69 @@ extern "C" {
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class errno_error : public std::runtime_error {
|
||||||
|
public:
|
||||||
|
int code;
|
||||||
|
errno_error(int code, const std::string& msg)
|
||||||
|
: std::runtime_error(getErrorMsg(code, msg.c_str())),
|
||||||
|
code(code) {}
|
||||||
|
errno_error(int code, const char* msg)
|
||||||
|
: std::runtime_error(getErrorMsg(code, msg)),
|
||||||
|
code(code) {}
|
||||||
|
private:
|
||||||
|
static auto getErrorMsg(int err, const char* msg) -> std::string {
|
||||||
|
std::string error_msg{msg};
|
||||||
|
error_msg += ": ";
|
||||||
|
|
||||||
|
#if (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 32)
|
||||||
|
// strerrorname_np gets the error code's name; it's nice to have, but it's a recent GNU extension
|
||||||
|
const auto errno_name = strerrorname_np(err);
|
||||||
|
error_msg += errno_name;
|
||||||
|
error_msg += " ";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const auto errno_str = strerror(err);
|
||||||
|
error_msg += errno_str;
|
||||||
|
|
||||||
|
return error_msg;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto openFile(const std::string& path, int flags) -> int {
|
||||||
|
int fd = open(path.c_str(), flags);
|
||||||
|
if (fd < 0) {
|
||||||
|
if (errno == EACCES) {
|
||||||
|
throw errno_error(errno, "Can't open " + path + " (are you in the input group?)");
|
||||||
|
} else {
|
||||||
|
throw errno_error(errno, "Can't open " + path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto closeFile(int fd) -> void {
|
||||||
|
int res = close(fd);
|
||||||
|
if (res < 0) {
|
||||||
|
throw errno_error(errno, "Can't close file");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto openDevice(int fd) -> libevdev* {
|
||||||
|
libevdev* dev;
|
||||||
|
int err = libevdev_new_from_fd(fd, &dev);
|
||||||
|
if (err < 0) {
|
||||||
|
throw errno_error(-err, "Can't create libevdev device");
|
||||||
|
}
|
||||||
|
return dev;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto supportsLockStates(const libevdev* dev) -> bool {
|
||||||
|
return libevdev_has_event_type(dev, EV_LED)
|
||||||
|
&& libevdev_has_event_code(dev, EV_LED, LED_NUML)
|
||||||
|
&& libevdev_has_event_code(dev, EV_LED, LED_CAPSL)
|
||||||
|
&& libevdev_has_event_code(dev, EV_LED, LED_SCROLLL);
|
||||||
|
}
|
||||||
|
|
||||||
waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Bar& bar, const Json::Value& config)
|
waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Bar& bar, const Json::Value& config)
|
||||||
: AModule(config, "keyboard-state", id, false, !config["disable-scroll"].asBool()),
|
: AModule(config, "keyboard-state", id, false, !config["disable-scroll"].asBool()),
|
||||||
box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0),
|
box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0),
|
||||||
|
@ -48,26 +113,36 @@ waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Bar&
|
||||||
|
|
||||||
if (config_["device-path"].isString()) {
|
if (config_["device-path"].isString()) {
|
||||||
std::string dev_path = config_["device-path"].asString();
|
std::string dev_path = config_["device-path"].asString();
|
||||||
std::tie(fd_, dev_) = openDevice(dev_path);
|
fd_ = openFile(dev_path, O_NONBLOCK | O_CLOEXEC | O_RDONLY);
|
||||||
|
dev_ = openDevice(fd_);
|
||||||
} else {
|
} else {
|
||||||
DIR* dev_dir = opendir("/dev/input");
|
DIR* dev_dir = opendir("/dev/input");
|
||||||
if (dev_dir == nullptr) {
|
if (dev_dir == nullptr) {
|
||||||
throw std::runtime_error("Failed to open /dev/input");
|
throw errno_error(errno, "Failed to open /dev/input");
|
||||||
}
|
}
|
||||||
dirent *ep;
|
dirent *ep;
|
||||||
while ((ep = readdir(dev_dir))) {
|
while ((ep = readdir(dev_dir))) {
|
||||||
if (ep->d_type != DT_CHR) continue;
|
if (ep->d_type != DT_CHR) continue;
|
||||||
std::string dev_path = std::string("/dev/input/") + ep->d_name;
|
std::string dev_path = std::string("/dev/input/") + ep->d_name;
|
||||||
|
int fd = openFile(dev_path.c_str(), O_NONBLOCK | O_CLOEXEC | O_RDONLY);
|
||||||
try {
|
try {
|
||||||
std::tie(fd_, dev_) = openDevice(dev_path);
|
auto dev = openDevice(fd);
|
||||||
spdlog::info("Found device {} at '{}'", libevdev_get_name(dev_), dev_path);
|
if (supportsLockStates(dev)) {
|
||||||
|
spdlog::info("Found device {} at '{}'", libevdev_get_name(dev), dev_path);
|
||||||
|
fd_ = fd;
|
||||||
|
dev_ = dev;
|
||||||
break;
|
break;
|
||||||
} catch (const std::runtime_error& e) {
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
} catch (const errno_error& e) {
|
||||||
|
// ENOTTY just means the device isn't an evdev device, skip it
|
||||||
|
if (e.code != ENOTTY) {
|
||||||
|
spdlog::warn(e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closeFile(fd);
|
||||||
}
|
}
|
||||||
if (dev_ == nullptr) {
|
if (dev_ == nullptr) {
|
||||||
throw std::runtime_error("Failed to find keyboard device");
|
throw errno_error(errno, "Failed to find keyboard device");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,35 +154,13 @@ waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Bar&
|
||||||
|
|
||||||
waybar::modules::KeyboardState::~KeyboardState() {
|
waybar::modules::KeyboardState::~KeyboardState() {
|
||||||
libevdev_free(dev_);
|
libevdev_free(dev_);
|
||||||
int err = close(fd_);
|
try {
|
||||||
if (err < 0) {
|
closeFile(fd_);
|
||||||
// Not much we can do, so ignore it.
|
} catch (const std::runtime_error& e) {
|
||||||
|
spdlog::warn(e.what());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto waybar::modules::KeyboardState::openDevice(const std::string& path) -> std::pair<int, libevdev*> {
|
|
||||||
int fd = open(path.c_str(), O_NONBLOCK | O_CLOEXEC | O_RDONLY);
|
|
||||||
if (fd < 0) {
|
|
||||||
throw std::runtime_error("Can't open " + path);
|
|
||||||
}
|
|
||||||
|
|
||||||
libevdev* dev;
|
|
||||||
int err = libevdev_new_from_fd(fd, &dev);
|
|
||||||
if (err < 0) {
|
|
||||||
throw std::runtime_error("Can't create libevdev device");
|
|
||||||
}
|
|
||||||
if (!libevdev_has_event_type(dev, EV_LED)) {
|
|
||||||
throw std::runtime_error("Device doesn't support LED events");
|
|
||||||
}
|
|
||||||
if (!libevdev_has_event_code(dev, EV_LED, LED_NUML)
|
|
||||||
|| !libevdev_has_event_code(dev, EV_LED, LED_CAPSL)
|
|
||||||
|| !libevdev_has_event_code(dev, EV_LED, LED_SCROLLL)) {
|
|
||||||
throw std::runtime_error("Device doesn't support num lock, caps lock, or scroll lock events");
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::make_pair(fd, dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto waybar::modules::KeyboardState::update() -> void {
|
auto waybar::modules::KeyboardState::update() -> void {
|
||||||
int err = LIBEVDEV_READ_STATUS_SUCCESS;
|
int err = LIBEVDEV_READ_STATUS_SUCCESS;
|
||||||
while (err == LIBEVDEV_READ_STATUS_SUCCESS) {
|
while (err == LIBEVDEV_READ_STATUS_SUCCESS) {
|
||||||
|
@ -117,8 +170,8 @@ auto waybar::modules::KeyboardState::update() -> void {
|
||||||
err = libevdev_next_event(dev_, LIBEVDEV_READ_FLAG_SYNC, &ev);
|
err = libevdev_next_event(dev_, LIBEVDEV_READ_FLAG_SYNC, &ev);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (err != -EAGAIN) {
|
if (-err != EAGAIN) {
|
||||||
throw std::runtime_error("Failed to sync evdev device");
|
throw errno_error(-err, "Failed to sync evdev device");
|
||||||
}
|
}
|
||||||
|
|
||||||
int numl = libevdev_get_event_value(dev_, EV_LED, LED_NUML);
|
int numl = libevdev_get_event_value(dev_, EV_LED, LED_NUML);
|
||||||
|
|
|
@ -12,7 +12,15 @@ auto waybar::modules::Memory::update() -> void {
|
||||||
parseMeminfo();
|
parseMeminfo();
|
||||||
|
|
||||||
unsigned long memtotal = meminfo_["MemTotal"];
|
unsigned long memtotal = meminfo_["MemTotal"];
|
||||||
|
unsigned long swaptotal = 0;
|
||||||
|
if (meminfo_.count("SwapTotal")) {
|
||||||
|
swaptotal = meminfo_["SwapTotal"];
|
||||||
|
}
|
||||||
unsigned long memfree;
|
unsigned long memfree;
|
||||||
|
unsigned long swapfree = 0;
|
||||||
|
if (meminfo_.count("SwapFree")) {
|
||||||
|
swapfree = meminfo_["SwapFree"];
|
||||||
|
}
|
||||||
if (meminfo_.count("MemAvailable")) {
|
if (meminfo_.count("MemAvailable")) {
|
||||||
// New kernels (3.4+) have an accurate available memory field.
|
// New kernels (3.4+) have an accurate available memory field.
|
||||||
memfree = meminfo_["MemAvailable"] + meminfo_["zfs_size"];
|
memfree = meminfo_["MemAvailable"] + meminfo_["zfs_size"];
|
||||||
|
@ -24,9 +32,16 @@ auto waybar::modules::Memory::update() -> void {
|
||||||
|
|
||||||
if (memtotal > 0 && memfree >= 0) {
|
if (memtotal > 0 && memfree >= 0) {
|
||||||
auto total_ram_gigabytes = memtotal / std::pow(1024, 2);
|
auto total_ram_gigabytes = memtotal / std::pow(1024, 2);
|
||||||
|
auto total_swap_gigabytes = swaptotal / std::pow(1024, 2);
|
||||||
int used_ram_percentage = 100 * (memtotal - memfree) / memtotal;
|
int used_ram_percentage = 100 * (memtotal - memfree) / memtotal;
|
||||||
|
int used_swap_percentage = 0;
|
||||||
|
if (swaptotal && swapfree) {
|
||||||
|
used_swap_percentage = 100 * (swaptotal - swapfree) / swaptotal;
|
||||||
|
}
|
||||||
auto used_ram_gigabytes = (memtotal - memfree) / std::pow(1024, 2);
|
auto used_ram_gigabytes = (memtotal - memfree) / std::pow(1024, 2);
|
||||||
|
auto used_swap_gigabytes = (swaptotal - swapfree) / std::pow(1024, 2);
|
||||||
auto available_ram_gigabytes = memfree / std::pow(1024, 2);
|
auto available_ram_gigabytes = memfree / std::pow(1024, 2);
|
||||||
|
auto available_swap_gigabytes = swapfree / std::pow(1024, 2);
|
||||||
|
|
||||||
auto format = format_;
|
auto format = format_;
|
||||||
auto state = getState(used_ram_percentage);
|
auto state = getState(used_ram_percentage);
|
||||||
|
@ -43,9 +58,13 @@ auto waybar::modules::Memory::update() -> void {
|
||||||
used_ram_percentage,
|
used_ram_percentage,
|
||||||
fmt::arg("icon", getIcon(used_ram_percentage, icons)),
|
fmt::arg("icon", getIcon(used_ram_percentage, icons)),
|
||||||
fmt::arg("total", total_ram_gigabytes),
|
fmt::arg("total", total_ram_gigabytes),
|
||||||
|
fmt::arg("swapTotal", total_swap_gigabytes),
|
||||||
fmt::arg("percentage", used_ram_percentage),
|
fmt::arg("percentage", used_ram_percentage),
|
||||||
|
fmt::arg("swapPercentage", used_swap_percentage),
|
||||||
fmt::arg("used", used_ram_gigabytes),
|
fmt::arg("used", used_ram_gigabytes),
|
||||||
fmt::arg("avail", available_ram_gigabytes)));
|
fmt::arg("swapUsed", used_swap_gigabytes),
|
||||||
|
fmt::arg("avail", available_ram_gigabytes),
|
||||||
|
fmt::arg("swapAvail", available_swap_gigabytes)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tooltipEnabled()) {
|
if (tooltipEnabled()) {
|
||||||
|
@ -54,9 +73,13 @@ auto waybar::modules::Memory::update() -> void {
|
||||||
label_.set_tooltip_text(fmt::format(tooltip_format,
|
label_.set_tooltip_text(fmt::format(tooltip_format,
|
||||||
used_ram_percentage,
|
used_ram_percentage,
|
||||||
fmt::arg("total", total_ram_gigabytes),
|
fmt::arg("total", total_ram_gigabytes),
|
||||||
|
fmt::arg("swapTotal", total_swap_gigabytes),
|
||||||
fmt::arg("percentage", used_ram_percentage),
|
fmt::arg("percentage", used_ram_percentage),
|
||||||
|
fmt::arg("swapPercentage", used_swap_percentage),
|
||||||
fmt::arg("used", used_ram_gigabytes),
|
fmt::arg("used", used_ram_gigabytes),
|
||||||
fmt::arg("avail", available_ram_gigabytes)));
|
fmt::arg("swapUsed", used_swap_gigabytes),
|
||||||
|
fmt::arg("avail", available_ram_gigabytes),
|
||||||
|
fmt::arg("swapAvail", available_swap_gigabytes)));
|
||||||
} else {
|
} else {
|
||||||
label_.set_tooltip_text(fmt::format("{:.{}f}GiB used", used_ram_gigabytes, 1));
|
label_.set_tooltip_text(fmt::format("{:.{}f}GiB used", used_ram_gigabytes, 1));
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,7 +129,7 @@ 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());
|
song_pos = mpd_status_get_song_pos(status_.get()) + 1;
|
||||||
volume = mpd_status_get_volume(status_.get());
|
volume = mpd_status_get_volume(status_.get());
|
||||||
if (volume < 0) {
|
if (volume < 0) {
|
||||||
volume = 0;
|
volume = 0;
|
||||||
|
|
|
@ -78,7 +78,7 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf
|
||||||
family_(config["family"] == "ipv6" ? AF_INET6 : AF_INET),
|
family_(config["family"] == "ipv6" ? AF_INET6 : AF_INET),
|
||||||
efd_(-1),
|
efd_(-1),
|
||||||
ev_fd_(-1),
|
ev_fd_(-1),
|
||||||
want_route_dump_(false),
|
want_route_dump_(true),
|
||||||
want_link_dump_(false),
|
want_link_dump_(false),
|
||||||
want_addr_dump_(false),
|
want_addr_dump_(false),
|
||||||
dump_in_progress_(false),
|
dump_in_progress_(false),
|
||||||
|
@ -88,7 +88,7 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf
|
||||||
#ifdef WANT_RFKILL
|
#ifdef WANT_RFKILL
|
||||||
rfkill_{RFKILL_TYPE_WLAN},
|
rfkill_{RFKILL_TYPE_WLAN},
|
||||||
#endif
|
#endif
|
||||||
frequency_(0) {
|
frequency_(0.0) {
|
||||||
|
|
||||||
// Start with some "text" in the module's label_, update() will then
|
// Start with some "text" in the module's label_, update() will then
|
||||||
// update it. Since the text should be different, update() will be able
|
// update it. Since the text should be different, update() will be able
|
||||||
|
@ -106,7 +106,7 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!config_["interface"].isString()) {
|
if (!config_["interface"].isString()) {
|
||||||
// "interface" isn't configure, then try to guess the external
|
// "interface" isn't configured, then try to guess the external
|
||||||
// interface currently used for internet.
|
// interface currently used for internet.
|
||||||
want_route_dump_ = true;
|
want_route_dump_ = true;
|
||||||
} else {
|
} else {
|
||||||
|
@ -331,12 +331,13 @@ auto waybar::modules::Network::update() -> void {
|
||||||
fmt::arg("essid", essid_),
|
fmt::arg("essid", essid_),
|
||||||
fmt::arg("signaldBm", signal_strength_dbm_),
|
fmt::arg("signaldBm", signal_strength_dbm_),
|
||||||
fmt::arg("signalStrength", signal_strength_),
|
fmt::arg("signalStrength", signal_strength_),
|
||||||
|
fmt::arg("signalStrengthApp", signal_strength_app_),
|
||||||
fmt::arg("ifname", ifname_),
|
fmt::arg("ifname", ifname_),
|
||||||
fmt::arg("netmask", netmask_),
|
fmt::arg("netmask", netmask_),
|
||||||
fmt::arg("ipaddr", ipaddr_),
|
fmt::arg("ipaddr", ipaddr_),
|
||||||
fmt::arg("gwaddr", gwaddr_),
|
fmt::arg("gwaddr", gwaddr_),
|
||||||
fmt::arg("cidr", cidr_),
|
fmt::arg("cidr", cidr_),
|
||||||
fmt::arg("frequency", frequency_),
|
fmt::arg("frequency", fmt::format("{:.1f}", frequency_)),
|
||||||
fmt::arg("icon", getIcon(signal_strength_, state_)),
|
fmt::arg("icon", getIcon(signal_strength_, state_)),
|
||||||
fmt::arg("bandwidthDownBits", pow_format(bandwidth_down * 8ull / interval_.count(), "b/s")),
|
fmt::arg("bandwidthDownBits", pow_format(bandwidth_down * 8ull / interval_.count(), "b/s")),
|
||||||
fmt::arg("bandwidthUpBits", pow_format(bandwidth_up * 8ull / interval_.count(), "b/s")),
|
fmt::arg("bandwidthUpBits", pow_format(bandwidth_up * 8ull / interval_.count(), "b/s")),
|
||||||
|
@ -360,12 +361,13 @@ auto waybar::modules::Network::update() -> void {
|
||||||
fmt::arg("essid", essid_),
|
fmt::arg("essid", essid_),
|
||||||
fmt::arg("signaldBm", signal_strength_dbm_),
|
fmt::arg("signaldBm", signal_strength_dbm_),
|
||||||
fmt::arg("signalStrength", signal_strength_),
|
fmt::arg("signalStrength", signal_strength_),
|
||||||
|
fmt::arg("signalStrengthApp", signal_strength_app_),
|
||||||
fmt::arg("ifname", ifname_),
|
fmt::arg("ifname", ifname_),
|
||||||
fmt::arg("netmask", netmask_),
|
fmt::arg("netmask", netmask_),
|
||||||
fmt::arg("ipaddr", ipaddr_),
|
fmt::arg("ipaddr", ipaddr_),
|
||||||
fmt::arg("gwaddr", gwaddr_),
|
fmt::arg("gwaddr", gwaddr_),
|
||||||
fmt::arg("cidr", cidr_),
|
fmt::arg("cidr", cidr_),
|
||||||
fmt::arg("frequency", frequency_),
|
fmt::arg("frequency", fmt::format("{:.1f}", frequency_)),
|
||||||
fmt::arg("icon", getIcon(signal_strength_, state_)),
|
fmt::arg("icon", getIcon(signal_strength_, state_)),
|
||||||
fmt::arg("bandwidthDownBits",
|
fmt::arg("bandwidthDownBits",
|
||||||
pow_format(bandwidth_down * 8ull / interval_.count(), "b/s")),
|
pow_format(bandwidth_down * 8ull / interval_.count(), "b/s")),
|
||||||
|
@ -403,7 +405,8 @@ void waybar::modules::Network::clearIface() {
|
||||||
cidr_ = 0;
|
cidr_ = 0;
|
||||||
signal_strength_dbm_ = 0;
|
signal_strength_dbm_ = 0;
|
||||||
signal_strength_ = 0;
|
signal_strength_ = 0;
|
||||||
frequency_ = 0;
|
signal_strength_app_.clear();
|
||||||
|
frequency_ = 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) {
|
int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) {
|
||||||
|
@ -470,7 +473,8 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) {
|
||||||
net->essid_.clear();
|
net->essid_.clear();
|
||||||
net->signal_strength_dbm_ = 0;
|
net->signal_strength_dbm_ = 0;
|
||||||
net->signal_strength_ = 0;
|
net->signal_strength_ = 0;
|
||||||
net->frequency_ = 0;
|
net->signal_strength_app_.clear();
|
||||||
|
net->frequency_ = 0.0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
net->carrier_ = carrier.value();
|
net->carrier_ = carrier.value();
|
||||||
|
@ -788,13 +792,30 @@ void waybar::modules::Network::parseSignal(struct nlattr **bss) {
|
||||||
if (bss[NL80211_BSS_SIGNAL_MBM] != nullptr) {
|
if (bss[NL80211_BSS_SIGNAL_MBM] != nullptr) {
|
||||||
// signalstrength in dBm from mBm
|
// signalstrength in dBm from mBm
|
||||||
signal_strength_dbm_ = nla_get_s32(bss[NL80211_BSS_SIGNAL_MBM]) / 100;
|
signal_strength_dbm_ = nla_get_s32(bss[NL80211_BSS_SIGNAL_MBM]) / 100;
|
||||||
|
// WiFi-hardware usually operates in the range -90 to -30dBm.
|
||||||
|
|
||||||
// WiFi-hardware usually operates in the range -90 to -20dBm.
|
// If a signal is too strong, it can overwhelm receiving circuity that is designed
|
||||||
const int hardwareMax = -20;
|
// to pick up and process a certain signal level. The following percentage is scaled to
|
||||||
|
// punish signals that are too strong (>= -45dBm) or too weak (<= -45 dBm).
|
||||||
|
const int hardwareOptimum = -45;
|
||||||
const int hardwareMin = -90;
|
const int hardwareMin = -90;
|
||||||
const int strength =
|
const int strength =
|
||||||
((signal_strength_dbm_ - hardwareMin) / double{hardwareMax - hardwareMin}) * 100;
|
100 - ((abs(signal_strength_dbm_ - hardwareOptimum) / double{hardwareOptimum - hardwareMin}) * 100);
|
||||||
signal_strength_ = std::clamp(strength, 0, 100);
|
signal_strength_ = std::clamp(strength, 0, 100);
|
||||||
|
|
||||||
|
if (signal_strength_dbm_ >= -50) {
|
||||||
|
signal_strength_app_ = "Great Connectivity";
|
||||||
|
} else if (signal_strength_dbm_ >= -60) {
|
||||||
|
signal_strength_app_ = "Good Connectivity";
|
||||||
|
} else if (signal_strength_dbm_ >= -67) {
|
||||||
|
signal_strength_app_ = "Streaming";
|
||||||
|
} else if (signal_strength_dbm_ >= -70) {
|
||||||
|
signal_strength_app_ = "Web Surfing";
|
||||||
|
} else if (signal_strength_dbm_ >= -80) {
|
||||||
|
signal_strength_app_ = "Basic Connectivity";
|
||||||
|
} else {
|
||||||
|
signal_strength_app_ = "Poor Connectivity";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (bss[NL80211_BSS_SIGNAL_UNSPEC] != nullptr) {
|
if (bss[NL80211_BSS_SIGNAL_UNSPEC] != nullptr) {
|
||||||
signal_strength_ = nla_get_u8(bss[NL80211_BSS_SIGNAL_UNSPEC]);
|
signal_strength_ = nla_get_u8(bss[NL80211_BSS_SIGNAL_UNSPEC]);
|
||||||
|
@ -803,8 +824,8 @@ void waybar::modules::Network::parseSignal(struct nlattr **bss) {
|
||||||
|
|
||||||
void waybar::modules::Network::parseFreq(struct nlattr **bss) {
|
void waybar::modules::Network::parseFreq(struct nlattr **bss) {
|
||||||
if (bss[NL80211_BSS_FREQUENCY] != nullptr) {
|
if (bss[NL80211_BSS_FREQUENCY] != nullptr) {
|
||||||
// in MHz
|
// in GHz
|
||||||
frequency_ = nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
|
frequency_ = (double) nla_get_u32(bss[NL80211_BSS_FREQUENCY]) / 1000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -54,7 +54,9 @@ void waybar::modules::Pulseaudio::contextStateCb(pa_context *c, void *data) {
|
||||||
c,
|
c,
|
||||||
static_cast<enum pa_subscription_mask>(static_cast<int>(PA_SUBSCRIPTION_MASK_SERVER) |
|
static_cast<enum pa_subscription_mask>(static_cast<int>(PA_SUBSCRIPTION_MASK_SERVER) |
|
||||||
static_cast<int>(PA_SUBSCRIPTION_MASK_SINK) |
|
static_cast<int>(PA_SUBSCRIPTION_MASK_SINK) |
|
||||||
static_cast<int>(PA_SUBSCRIPTION_MASK_SOURCE)),
|
static_cast<int>(PA_SUBSCRIPTION_MASK_SINK_INPUT) |
|
||||||
|
static_cast<int>(PA_SUBSCRIPTION_MASK_SOURCE) |
|
||||||
|
static_cast<int>(PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT)),
|
||||||
nullptr,
|
nullptr,
|
||||||
nullptr);
|
nullptr);
|
||||||
break;
|
break;
|
||||||
|
@ -79,6 +81,13 @@ bool waybar::modules::Pulseaudio::handleScroll(GdkEventScroll *e) {
|
||||||
if (dir == SCROLL_DIR::NONE) {
|
if (dir == SCROLL_DIR::NONE) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (config_["reverse-scrolling"].asInt() == 1){
|
||||||
|
if (dir == SCROLL_DIR::UP) {
|
||||||
|
dir = SCROLL_DIR::DOWN;
|
||||||
|
} else if (dir == SCROLL_DIR::DOWN) {
|
||||||
|
dir = SCROLL_DIR::UP;
|
||||||
|
}
|
||||||
|
}
|
||||||
double volume_tick = static_cast<double>(PA_VOLUME_NORM) / 100;
|
double volume_tick = static_cast<double>(PA_VOLUME_NORM) / 100;
|
||||||
pa_volume_t change = volume_tick;
|
pa_volume_t change = volume_tick;
|
||||||
pa_cvolume pa_volume = pa_volume_;
|
pa_cvolume pa_volume = pa_volume_;
|
||||||
|
@ -114,8 +123,12 @@ void waybar::modules::Pulseaudio::subscribeCb(pa_context * conte
|
||||||
pa_context_get_server_info(context, serverInfoCb, data);
|
pa_context_get_server_info(context, serverInfoCb, data);
|
||||||
} else if (facility == PA_SUBSCRIPTION_EVENT_SINK) {
|
} else if (facility == PA_SUBSCRIPTION_EVENT_SINK) {
|
||||||
pa_context_get_sink_info_by_index(context, idx, sinkInfoCb, data);
|
pa_context_get_sink_info_by_index(context, idx, sinkInfoCb, data);
|
||||||
|
} else if (facility == PA_SUBSCRIPTION_EVENT_SINK_INPUT) {
|
||||||
|
pa_context_get_sink_info_list(context, sinkInfoCb, data);
|
||||||
} else if (facility == PA_SUBSCRIPTION_EVENT_SOURCE) {
|
} else if (facility == PA_SUBSCRIPTION_EVENT_SOURCE) {
|
||||||
pa_context_get_source_info_by_index(context, idx, sourceInfoCb, data);
|
pa_context_get_source_info_by_index(context, idx, sourceInfoCb, data);
|
||||||
|
} else if (facility == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT) {
|
||||||
|
pa_context_get_source_info_list(context, sourceInfoCb, data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,13 +8,7 @@
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
template <>
|
#include "util/format.hpp"
|
||||||
struct fmt::formatter<Glib::ustring> : formatter<std::string> {
|
|
||||||
template <typename FormatContext>
|
|
||||||
auto format(const Glib::ustring& value, FormatContext& ctx) {
|
|
||||||
return formatter<std::string>::format(value, ctx);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct fmt::formatter<Glib::VariantBase> : formatter<std::string> {
|
struct fmt::formatter<Glib::VariantBase> : formatter<std::string> {
|
||||||
|
|
|
@ -25,7 +25,11 @@ Tray::Tray(const std::string& id, const Bar& bar, const Json::Value& config)
|
||||||
}
|
}
|
||||||
|
|
||||||
void Tray::onAdd(std::unique_ptr<Item>& item) {
|
void Tray::onAdd(std::unique_ptr<Item>& item) {
|
||||||
|
if (config_["reverse-direction"].isBool() && config_["reverse-direction"].asBool()) {
|
||||||
|
box_.pack_end(item->event_box);
|
||||||
|
} else {
|
||||||
box_.pack_start(item->event_box);
|
box_.pack_start(item->event_box);
|
||||||
|
}
|
||||||
dp.emit();
|
dp.emit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -154,7 +154,10 @@ auto Language::init_layouts_map(const std::vector<std::string>& used_layouts) ->
|
||||||
|
|
||||||
std::map<std::string, int> short_name_to_number_map;
|
std::map<std::string, int> short_name_to_number_map;
|
||||||
for (const auto& used_layout_name : used_layouts) {
|
for (const auto& used_layout_name : used_layouts) {
|
||||||
auto used_layout = &layouts_map_.find(used_layout_name)->second;
|
auto found = layouts_map_.find(used_layout_name);
|
||||||
|
if (found == layouts_map_.end())
|
||||||
|
continue;
|
||||||
|
auto used_layout = &found->second;
|
||||||
auto layouts_with_same_name_list = found_by_short_names[used_layout->short_name];
|
auto layouts_with_same_name_list = found_by_short_names[used_layout->short_name];
|
||||||
if (layouts_with_same_name_list.size() < 2) {
|
if (layouts_with_same_name_list.size() < 2) {
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -1,11 +1,20 @@
|
||||||
#include "modules/sway/window.hpp"
|
#include "modules/sway/window.hpp"
|
||||||
|
|
||||||
|
#include <gdkmm/pixbuf.h>
|
||||||
|
#include <glibmm/fileutils.h>
|
||||||
|
#include <glibmm/keyfile.h>
|
||||||
|
#include <glibmm/miscutils.h>
|
||||||
|
#include <gtkmm/enums.h>
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
namespace waybar::modules::sway {
|
namespace waybar::modules::sway {
|
||||||
|
|
||||||
Window::Window(const std::string& id, const Bar& bar, const Json::Value& config)
|
Window::Window(const std::string& id, const Bar& bar, const Json::Value& config)
|
||||||
: ALabel(config, "window", id, "{}", 0, true), bar_(bar), windowId_(-1) {
|
: AIconLabel(config, "window", id, "{}", 0, true), bar_(bar), windowId_(-1) {
|
||||||
ipc_.subscribe(R"(["window","workspace"])");
|
ipc_.subscribe(R"(["window","workspace"])");
|
||||||
ipc_.signal_event.connect(sigc::mem_fun(*this, &Window::onEvent));
|
ipc_.signal_event.connect(sigc::mem_fun(*this, &Window::onEvent));
|
||||||
ipc_.signal_cmd.connect(sigc::mem_fun(*this, &Window::onCmd));
|
ipc_.signal_cmd.connect(sigc::mem_fun(*this, &Window::onCmd));
|
||||||
|
@ -29,12 +38,60 @@ void Window::onCmd(const struct Ipc::ipc_response& res) {
|
||||||
auto payload = parser_.parse(res.payload);
|
auto payload = parser_.parse(res.payload);
|
||||||
auto output = payload["output"].isString() ? payload["output"].asString() : "";
|
auto output = payload["output"].isString() ? payload["output"].asString() : "";
|
||||||
std::tie(app_nb_, windowId_, window_, app_id_) = getFocusedNode(payload["nodes"], output);
|
std::tie(app_nb_, windowId_, window_, app_id_) = getFocusedNode(payload["nodes"], output);
|
||||||
|
updateAppIcon();
|
||||||
dp.emit();
|
dp.emit();
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
spdlog::error("Window: {}", e.what());
|
spdlog::error("Window: {}", e.what());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> getDesktopFilePath(const std::string& app_id) {
|
||||||
|
const auto data_dirs = Glib::get_system_data_dirs();
|
||||||
|
for (const auto& data_dir : data_dirs) {
|
||||||
|
const auto desktop_file_path = data_dir + "applications/" + app_id + ".desktop";
|
||||||
|
if (std::filesystem::exists(desktop_file_path)) {
|
||||||
|
return desktop_file_path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<Glib::ustring> getIconName(const std::string& app_id) {
|
||||||
|
const auto desktop_file_path = getDesktopFilePath(app_id);
|
||||||
|
if (!desktop_file_path.has_value()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Glib::KeyFile desktop_file;
|
||||||
|
desktop_file.load_from_file(desktop_file_path.value());
|
||||||
|
const auto icon_name = desktop_file.get_string("Desktop Entry", "Icon");
|
||||||
|
if (icon_name.empty()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return icon_name;
|
||||||
|
} catch (Glib::FileError& error) {
|
||||||
|
spdlog::warn(
|
||||||
|
"Error while loading desktop file {}: {}", desktop_file_path.value(), error.what().c_str());
|
||||||
|
} catch (Glib::KeyFileError& error) {
|
||||||
|
spdlog::warn(
|
||||||
|
"Error while loading desktop file {}: {}", desktop_file_path.value(), error.what().c_str());
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void Window::updateAppIcon() {
|
||||||
|
if (!iconEnabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto icon_name = getIconName(app_id_);
|
||||||
|
if (icon_name.has_value()) {
|
||||||
|
image_.set_from_icon_name(icon_name.value(), Gtk::ICON_SIZE_LARGE_TOOLBAR);
|
||||||
|
image_.set_visible(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
image_.set_visible(false);
|
||||||
|
}
|
||||||
|
|
||||||
auto Window::update() -> void {
|
auto Window::update() -> void {
|
||||||
if (!old_app_id_.empty()) {
|
if (!old_app_id_.empty()) {
|
||||||
bar_.window.get_style_context()->remove_class(old_app_id_);
|
bar_.window.get_style_context()->remove_class(old_app_id_);
|
||||||
|
@ -63,7 +120,7 @@ auto Window::update() -> void {
|
||||||
label_.set_tooltip_text(window_);
|
label_.set_tooltip_text(window_);
|
||||||
}
|
}
|
||||||
// Call parent update
|
// Call parent update
|
||||||
ALabel::update();
|
AIconLabel::update();
|
||||||
}
|
}
|
||||||
|
|
||||||
int leafNodesInWorkspace(const Json::Value& node) {
|
int leafNodesInWorkspace(const Json::Value& node) {
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
@ -98,6 +99,7 @@ void Workspaces::onCmd(const struct Ipc::ipc_response &res) {
|
||||||
Json::Value v;
|
Json::Value v;
|
||||||
v["name"] = p_w_name;
|
v["name"] = p_w_name;
|
||||||
v["target_output"] = bar_.output->name;
|
v["target_output"] = bar_.output->name;
|
||||||
|
v["num"] = convertWorkspaceNameToNum(p_w_name);
|
||||||
workspaces_.emplace_back(std::move(v));
|
workspaces_.emplace_back(std::move(v));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -107,57 +109,59 @@ void Workspaces::onCmd(const struct Ipc::ipc_response &res) {
|
||||||
Json::Value v;
|
Json::Value v;
|
||||||
v["name"] = p_w_name;
|
v["name"] = p_w_name;
|
||||||
v["target_output"] = "";
|
v["target_output"] = "";
|
||||||
|
v["num"] = convertWorkspaceNameToNum(p_w_name);
|
||||||
workspaces_.emplace_back(std::move(v));
|
workspaces_.emplace_back(std::move(v));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// config option to sort numeric workspace names before others
|
// sway has a defined ordering of workspaces that should be preserved in
|
||||||
bool config_numeric_first = config_["numeric-first"].asBool();
|
// the representation displayed by waybar to ensure that commands such
|
||||||
|
// as "workspace prev" or "workspace next" make sense when looking at
|
||||||
|
// the workspace representation in the bar.
|
||||||
|
// Due to waybar's own feature of persistent workspaces unknown to sway,
|
||||||
|
// custom sorting logic is necessary to make these workspaces appear
|
||||||
|
// naturally in the list of workspaces without messing up sway's
|
||||||
|
// sorting. For this purpose, a custom numbering property is created
|
||||||
|
// that preserves the order provided by sway while inserting numbered
|
||||||
|
// persistent workspaces at their natural positions.
|
||||||
|
//
|
||||||
|
// All of this code assumes that sway provides numbered workspaces first
|
||||||
|
// and other workspaces are sorted by their creation time.
|
||||||
|
//
|
||||||
|
// In a first pass, the maximum "num" value is computed to enqueue
|
||||||
|
// unnumbered workspaces behind numbered ones when computing the sort
|
||||||
|
// attribute.
|
||||||
|
int max_num = -1;
|
||||||
|
for (auto & workspace : workspaces_) {
|
||||||
|
max_num = std::max(workspace["num"].asInt(), max_num);
|
||||||
|
}
|
||||||
|
for (auto & workspace : workspaces_) {
|
||||||
|
auto workspace_num = workspace["num"].asInt();
|
||||||
|
if (workspace_num > -1) {
|
||||||
|
workspace["sort"] = workspace_num;
|
||||||
|
} else {
|
||||||
|
workspace["sort"] = ++max_num;
|
||||||
|
}
|
||||||
|
}
|
||||||
std::sort(workspaces_.begin(),
|
std::sort(workspaces_.begin(),
|
||||||
workspaces_.end(),
|
workspaces_.end(),
|
||||||
[config_numeric_first](const Json::Value &lhs, const Json::Value &rhs) {
|
[](const Json::Value &lhs, const Json::Value &rhs) {
|
||||||
// the "num" property (integer type):
|
|
||||||
// The workspace number or -1 for workspaces that do
|
|
||||||
// not start with a number.
|
|
||||||
// We could rely on sway providing this property:
|
|
||||||
//
|
|
||||||
// auto l = lhs["num"].asInt();
|
|
||||||
// auto r = rhs["num"].asInt();
|
|
||||||
//
|
|
||||||
// We cannot rely on the "num" property as provided by sway
|
|
||||||
// via IPC, because persistent workspace might not exist in
|
|
||||||
// sway's view. However, we need this property also for
|
|
||||||
// not-yet created persistent workspace. As such, we simply
|
|
||||||
// duplicate sway's logic of assigning the "num" property
|
|
||||||
// into waybar (see convertWorkspaceNameToNum). This way the
|
|
||||||
// sorting should work out even when we include workspaces
|
|
||||||
// that do not currently exist.
|
|
||||||
auto lname = lhs["name"].asString();
|
auto lname = lhs["name"].asString();
|
||||||
auto rname = rhs["name"].asString();
|
auto rname = rhs["name"].asString();
|
||||||
int l = convertWorkspaceNameToNum(lname);
|
int l = lhs["sort"].asInt();
|
||||||
int r = convertWorkspaceNameToNum(rname);
|
int r = rhs["sort"].asInt();
|
||||||
|
|
||||||
if (l == r) {
|
if (l == r) {
|
||||||
// in case both integers are the same, lexicographical
|
// In case both integers are the same, lexicographical
|
||||||
// sort. This also covers the case when both don't have a
|
// sort. The code above already ensure that this will only
|
||||||
// number (i.e., l == r == -1).
|
// happend in case of explicitly numbered workspaces.
|
||||||
return lname < rname;
|
return lname < rname;
|
||||||
}
|
}
|
||||||
|
|
||||||
// one of the workspaces doesn't begin with a number, so
|
|
||||||
// num is -1.
|
|
||||||
if (l < 0 || r < 0) {
|
|
||||||
if (config_numeric_first) {
|
|
||||||
return r < 0;
|
|
||||||
}
|
|
||||||
return l < 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// both workspaces have a "num" so let's just compare those
|
|
||||||
return l < r;
|
return l < r;
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
dp.emit();
|
dp.emit();
|
||||||
} catch (const std::exception &e) {
|
} catch (const std::exception &e) {
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "glibmm/fileutils.h"
|
#include "glibmm/fileutils.h"
|
||||||
#include "glibmm/refptr.h"
|
#include "glibmm/refptr.h"
|
||||||
#include "util/format.hpp"
|
#include "util/format.hpp"
|
||||||
|
#include "util/string.hpp"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
|
@ -11,7 +12,9 @@
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include <fmt/core.h>
|
||||||
#include <gdkmm/monitor.h>
|
#include <gdkmm/monitor.h>
|
||||||
|
|
||||||
#include <gtkmm/icontheme.h>
|
#include <gtkmm/icontheme.h>
|
||||||
|
@ -24,27 +27,6 @@
|
||||||
|
|
||||||
namespace waybar::modules::wlr {
|
namespace waybar::modules::wlr {
|
||||||
|
|
||||||
/* String manipulation methods */
|
|
||||||
const std::string WHITESPACE = " \n\r\t\f\v";
|
|
||||||
|
|
||||||
static std::string ltrim(const std::string& s)
|
|
||||||
{
|
|
||||||
size_t start = s.find_first_not_of(WHITESPACE);
|
|
||||||
return (start == std::string::npos) ? "" : s.substr(start);
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::string rtrim(const std::string& s)
|
|
||||||
{
|
|
||||||
size_t end = s.find_last_not_of(WHITESPACE);
|
|
||||||
return (end == std::string::npos) ? "" : s.substr(0, end + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::string trim(const std::string& s)
|
|
||||||
{
|
|
||||||
return rtrim(ltrim(s));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Icon loading functions */
|
/* Icon loading functions */
|
||||||
static std::vector<std::string> search_prefix()
|
static std::vector<std::string> search_prefix()
|
||||||
{
|
{
|
||||||
|
@ -86,8 +68,7 @@ static Glib::RefPtr<Gdk::Pixbuf> load_icon_from_file(std::string icon_path, int
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Method 1 - get the correct icon name from the desktop file */
|
static Glib::RefPtr<Gio::DesktopAppInfo> get_app_info_by_name(const std::string& app_id)
|
||||||
static std::string get_from_desktop_app_info(const std::string &app_id)
|
|
||||||
{
|
{
|
||||||
static std::vector<std::string> prefixes = search_prefix();
|
static std::vector<std::string> prefixes = search_prefix();
|
||||||
|
|
||||||
|
@ -103,33 +84,29 @@ static std::string get_from_desktop_app_info(const std::string &app_id)
|
||||||
".desktop"
|
".desktop"
|
||||||
};
|
};
|
||||||
|
|
||||||
Glib::RefPtr<Gio::DesktopAppInfo> app_info;
|
for (auto& prefix : prefixes) {
|
||||||
|
for (auto& folder : app_folders) {
|
||||||
for (auto& prefix : prefixes)
|
for (auto& suffix : suffixes) {
|
||||||
for (auto& folder : app_folders)
|
auto app_info_ = Gio::DesktopAppInfo::create_from_filename(prefix + folder + app_id + suffix);
|
||||||
for (auto& suffix : suffixes)
|
if (!app_info_) {
|
||||||
if (!app_info)
|
continue;
|
||||||
app_info = Gio::DesktopAppInfo::create_from_filename(prefix + folder + app_id + suffix);
|
|
||||||
|
|
||||||
if (app_info && app_info->get_icon())
|
|
||||||
return app_info->get_icon()->to_string();
|
|
||||||
|
|
||||||
return "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Method 2 - use the app_id and check whether there is an icon with this name in the icon theme */
|
return app_info_;
|
||||||
static std::string get_from_icon_theme(const Glib::RefPtr<Gtk::IconTheme>& icon_theme,
|
}
|
||||||
const std::string &app_id)
|
}
|
||||||
{
|
|
||||||
if (icon_theme->lookup_icon(app_id, 24))
|
|
||||||
return app_id;
|
|
||||||
|
|
||||||
return "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Method 3 - as last resort perform a search for most appropriate desktop info file */
|
return {};
|
||||||
static std::string get_from_desktop_app_info_search(const std::string &app_id)
|
}
|
||||||
|
|
||||||
|
Glib::RefPtr<Gio::DesktopAppInfo> get_desktop_app_info(const std::string &app_id)
|
||||||
{
|
{
|
||||||
|
auto app_info = get_app_info_by_name(app_id);
|
||||||
|
if (app_info) {
|
||||||
|
return app_info;
|
||||||
|
}
|
||||||
|
|
||||||
std::string desktop_file = "";
|
std::string desktop_file = "";
|
||||||
|
|
||||||
gchar*** desktop_list = g_desktop_app_info_search(app_id.c_str());
|
gchar*** desktop_list = g_desktop_app_info_search(app_id.c_str());
|
||||||
|
@ -151,65 +128,84 @@ static std::string get_from_desktop_app_info_search(const std::string &app_id)
|
||||||
}
|
}
|
||||||
g_free(desktop_list);
|
g_free(desktop_list);
|
||||||
|
|
||||||
return get_from_desktop_app_info(desktop_file);
|
return get_app_info_by_name(desktop_file);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool image_load_icon(Gtk::Image& image, const Glib::RefPtr<Gtk::IconTheme>& icon_theme,
|
void Task::set_app_info_from_app_id_list(const std::string& app_id_list) {
|
||||||
const std::string &app_id_list, int size)
|
|
||||||
{
|
|
||||||
std::string app_id;
|
std::string app_id;
|
||||||
std::istringstream stream(app_id_list);
|
std::istringstream stream(app_id_list);
|
||||||
bool found = false;
|
|
||||||
|
|
||||||
/* Wayfire sends a list of app-id's in space separated format, other compositors
|
/* Wayfire sends a list of app-id's in space separated format, other compositors
|
||||||
* send a single app-id, but in any case this works fine */
|
* send a single app-id, but in any case this works fine */
|
||||||
while (stream >> app_id)
|
while (stream >> app_id)
|
||||||
{
|
{
|
||||||
size_t start = 0, end = app_id.size();
|
app_info_ = get_desktop_app_info(app_id);
|
||||||
start = app_id.rfind(".", end);
|
if (app_info_) {
|
||||||
std::string app_name = app_id.substr(start+1, app_id.size());
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
auto lower_app_id = app_id;
|
auto lower_app_id = app_id;
|
||||||
std::transform(lower_app_id.begin(), lower_app_id.end(), lower_app_id.begin(),
|
std::transform(lower_app_id.begin(), lower_app_id.end(), lower_app_id.begin(),
|
||||||
[](char c){ return std::tolower(c); });
|
[](char c){ return std::tolower(c); });
|
||||||
|
app_info_ = get_desktop_app_info(lower_app_id);
|
||||||
|
if (app_info_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::string icon_name = get_from_icon_theme(icon_theme, app_id);
|
size_t start = 0, end = app_id.size();
|
||||||
|
start = app_id.rfind(".", end);
|
||||||
|
std::string app_name = app_id.substr(start+1, app_id.size());
|
||||||
|
app_info_ = get_desktop_app_info(app_name);
|
||||||
|
if (app_info_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (icon_name.empty())
|
start = app_id.find("-");
|
||||||
icon_name = get_from_icon_theme(icon_theme, lower_app_id);
|
app_name = app_id.substr(0, start);
|
||||||
if (icon_name.empty())
|
app_info_ = get_desktop_app_info(app_name);
|
||||||
icon_name = get_from_icon_theme(icon_theme, app_name);
|
}
|
||||||
if (icon_name.empty())
|
}
|
||||||
icon_name = get_from_desktop_app_info(app_id);
|
|
||||||
if (icon_name.empty())
|
|
||||||
icon_name = get_from_desktop_app_info(lower_app_id);
|
|
||||||
if (icon_name.empty())
|
|
||||||
icon_name = get_from_desktop_app_info(app_name);
|
|
||||||
if (icon_name.empty())
|
|
||||||
icon_name = get_from_desktop_app_info_search(app_id);
|
|
||||||
|
|
||||||
if (icon_name.empty())
|
static std::string get_icon_name_from_icon_theme(const Glib::RefPtr<Gtk::IconTheme>& icon_theme,
|
||||||
icon_name = "unknown";
|
const std::string &app_id)
|
||||||
|
{
|
||||||
|
if (icon_theme->lookup_icon(app_id, 24))
|
||||||
|
return app_id;
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Task::image_load_icon(Gtk::Image& image, const Glib::RefPtr<Gtk::IconTheme>& icon_theme, Glib::RefPtr<Gio::DesktopAppInfo> app_info, int size)
|
||||||
|
{
|
||||||
|
std::string ret_icon_name = "unknown";
|
||||||
|
if (app_info) {
|
||||||
|
std::string icon_name = get_icon_name_from_icon_theme(icon_theme, app_info->get_startup_wm_class());
|
||||||
|
if (!icon_name.empty()) {
|
||||||
|
ret_icon_name = icon_name;
|
||||||
|
} else {
|
||||||
|
if (app_info->get_icon()) {
|
||||||
|
ret_icon_name = app_info->get_icon()->to_string();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Glib::RefPtr<Gdk::Pixbuf> pixbuf;
|
Glib::RefPtr<Gdk::Pixbuf> pixbuf;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
pixbuf = icon_theme->load_icon(icon_name, size, Gtk::ICON_LOOKUP_FORCE_SIZE);
|
pixbuf = icon_theme->load_icon(ret_icon_name, size, Gtk::ICON_LOOKUP_FORCE_SIZE);
|
||||||
} catch(...) {
|
} catch(...) {
|
||||||
if (Glib::file_test(icon_name, Glib::FILE_TEST_EXISTS))
|
if (Glib::file_test(ret_icon_name, Glib::FILE_TEST_EXISTS))
|
||||||
pixbuf = load_icon_from_file(icon_name, size);
|
pixbuf = load_icon_from_file(ret_icon_name, size);
|
||||||
else
|
else
|
||||||
pixbuf = {};
|
pixbuf = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pixbuf) {
|
if (pixbuf) {
|
||||||
image.set(pixbuf);
|
image.set(pixbuf);
|
||||||
found = true;
|
return true;
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return found;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Task class implementation */
|
/* Task class implementation */
|
||||||
|
@ -289,13 +285,15 @@ Task::Task(const waybar::Bar &bar, const Json::Value &config, Taskbar *tbar,
|
||||||
content_.show();
|
content_.show();
|
||||||
button_.add(content_);
|
button_.add(content_);
|
||||||
|
|
||||||
with_icon_ = false;
|
|
||||||
format_before_.clear();
|
format_before_.clear();
|
||||||
format_after_.clear();
|
format_after_.clear();
|
||||||
|
|
||||||
if (config_["format"].isString()) {
|
if (config_["format"].isString()) {
|
||||||
/* The user defined a format string, use it */
|
/* The user defined a format string, use it */
|
||||||
auto format = config_["format"].asString();
|
auto format = config_["format"].asString();
|
||||||
|
if (format.find("{name}") != std::string::npos) {
|
||||||
|
with_name_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
auto icon_pos = format.find("{icon}");
|
auto icon_pos = format.find("{icon}");
|
||||||
if (icon_pos == 0) {
|
if (icon_pos == 0) {
|
||||||
|
@ -402,13 +400,28 @@ void Task::handle_app_id(const char *app_id)
|
||||||
app_id_ = app_id;
|
app_id_ = app_id;
|
||||||
hide_if_ignored();
|
hide_if_ignored();
|
||||||
|
|
||||||
if (!with_icon_)
|
auto ids_replace_map = tbar_->app_ids_replace_map();
|
||||||
|
if (ids_replace_map.count(app_id_)) {
|
||||||
|
auto replaced_id = ids_replace_map[app_id_];
|
||||||
|
spdlog::debug(fmt::format("Task ({}) [{}] app_id was replaced with {}", id_, app_id_, replaced_id));
|
||||||
|
app_id_ = replaced_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!with_icon_ && !with_name_) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
set_app_info_from_app_id_list(app_id_);
|
||||||
|
name_ = app_info_ ? app_info_->get_display_name() : app_id;
|
||||||
|
|
||||||
|
if (!with_icon_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
int icon_size = config_["icon-size"].isInt() ? config_["icon-size"].asInt() : 16;
|
int icon_size = config_["icon-size"].isInt() ? config_["icon-size"].asInt() : 16;
|
||||||
bool found = false;
|
bool found = false;
|
||||||
for (auto& icon_theme : tbar_->icon_themes()) {
|
for (auto& icon_theme : tbar_->icon_themes()) {
|
||||||
if (image_load_icon(icon_, icon_theme, app_id_, icon_size)) {
|
if (image_load_icon(icon_, icon_theme, app_info_, icon_size)) {
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -564,14 +577,17 @@ void Task::update()
|
||||||
{
|
{
|
||||||
bool markup = config_["markup"].isBool() ? config_["markup"].asBool() : false;
|
bool markup = config_["markup"].isBool() ? config_["markup"].asBool() : false;
|
||||||
std::string title = title_;
|
std::string title = title_;
|
||||||
|
std::string name = name_;
|
||||||
std::string app_id = app_id_;
|
std::string app_id = app_id_;
|
||||||
if (markup) {
|
if (markup) {
|
||||||
title = Glib::Markup::escape_text(title);
|
title = Glib::Markup::escape_text(title);
|
||||||
|
name = Glib::Markup::escape_text(name);
|
||||||
app_id = Glib::Markup::escape_text(app_id);
|
app_id = Glib::Markup::escape_text(app_id);
|
||||||
}
|
}
|
||||||
if (!format_before_.empty()) {
|
if (!format_before_.empty()) {
|
||||||
auto txt = fmt::format(format_before_,
|
auto txt = fmt::format(format_before_,
|
||||||
fmt::arg("title", title),
|
fmt::arg("title", title),
|
||||||
|
fmt::arg("name", name),
|
||||||
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))
|
||||||
|
@ -585,6 +601,7 @@ void Task::update()
|
||||||
if (!format_after_.empty()) {
|
if (!format_after_.empty()) {
|
||||||
auto txt = fmt::format(format_after_,
|
auto txt = fmt::format(format_after_,
|
||||||
fmt::arg("title", title),
|
fmt::arg("title", title),
|
||||||
|
fmt::arg("name", name),
|
||||||
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))
|
||||||
|
@ -599,6 +616,7 @@ void Task::update()
|
||||||
if (!format_tooltip_.empty()) {
|
if (!format_tooltip_.empty()) {
|
||||||
auto txt = fmt::format(format_tooltip_,
|
auto txt = fmt::format(format_tooltip_,
|
||||||
fmt::arg("title", title),
|
fmt::arg("title", title),
|
||||||
|
fmt::arg("name", name),
|
||||||
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))
|
||||||
|
@ -726,6 +744,15 @@ Taskbar::Taskbar(const std::string &id, const waybar::Bar &bar, const Json::Valu
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Load app_id remappings
|
||||||
|
if (config_["app_ids-mapping"].isObject()) {
|
||||||
|
const Json::Value& mapping = config_["app_ids-mapping"];
|
||||||
|
const std::vector<std::string> app_ids = config_["app_ids-mapping"].getMemberNames();
|
||||||
|
for (auto& app_id : app_ids) {
|
||||||
|
app_ids_replace_map_.emplace(app_id, mapping[app_id].asString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
icon_themes_.push_back(Gtk::IconTheme::get_default());
|
icon_themes_.push_back(Gtk::IconTheme::get_default());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -857,10 +884,10 @@ 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
|
const std::vector<Glib::RefPtr<Gtk::IconTheme>>& Taskbar::icon_themes() const { return icon_themes_; }
|
||||||
{
|
|
||||||
return icon_themes_;
|
|
||||||
}
|
|
||||||
const std::unordered_set<std::string>& Taskbar::ignore_list() const { return ignore_list_; }
|
const std::unordered_set<std::string>& Taskbar::ignore_list() const { return ignore_list_; }
|
||||||
|
|
||||||
|
const std::map<std::string, std::string>& Taskbar::app_ids_replace_map() const { return app_ids_replace_map_; }
|
||||||
|
|
||||||
} /* namespace waybar::modules::wlr */
|
} /* namespace waybar::modules::wlr */
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
#define CATCH_CONFIG_RUNNER
|
|
||||||
#include "util/SafeSignal.hpp"
|
#include "util/SafeSignal.hpp"
|
||||||
|
|
||||||
#include <glibmm.h>
|
#include <glibmm.h>
|
||||||
|
@ -138,8 +137,3 @@ TEST_CASE_METHOD(GlibTestsFixture, "SafeSignal copy/move counter", "[signal][thr
|
||||||
producer.join();
|
producer.join();
|
||||||
REQUIRE(count == NUM_EVENTS);
|
REQUIRE(count == NUM_EVENTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
|
||||||
Glib::init();
|
|
||||||
return Catch::Session().run(argc, argv);
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
#define CATCH_CONFIG_MAIN
|
|
||||||
#include "config.hpp"
|
#include "config.hpp"
|
||||||
|
|
||||||
#include <catch2/catch.hpp>
|
#include <catch2/catch.hpp>
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
#define CATCH_CONFIG_RUNNER
|
||||||
|
#include <glibmm.h>
|
||||||
|
#include <spdlog/sinks/stdout_sinks.h>
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
|
#include <catch2/catch.hpp>
|
||||||
|
#include <catch2/catch_reporter_tap.hpp>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
Catch::Session session;
|
||||||
|
Glib::init();
|
||||||
|
|
||||||
|
session.applyCommandLine(argc, argv);
|
||||||
|
const auto logger = spdlog::default_logger();
|
||||||
|
const auto& reporter_name = session.config().getReporterName();
|
||||||
|
if (reporter_name == "tap") {
|
||||||
|
spdlog::set_pattern("# [%l] %v");
|
||||||
|
} else if (reporter_name == "compact") {
|
||||||
|
logger->sinks().clear();
|
||||||
|
} else {
|
||||||
|
logger->sinks().assign({std::make_shared<spdlog::sinks::stderr_sink_st>()});
|
||||||
|
}
|
||||||
|
|
||||||
|
return session.run();
|
||||||
|
}
|
|
@ -6,30 +6,27 @@ test_dep = [
|
||||||
jsoncpp,
|
jsoncpp,
|
||||||
spdlog,
|
spdlog,
|
||||||
]
|
]
|
||||||
|
test_src = files(
|
||||||
config_test = executable(
|
'main.cpp',
|
||||||
'config_test',
|
'SafeSignal.cpp',
|
||||||
'config.cpp',
|
'config.cpp',
|
||||||
'../src/config.cpp',
|
'../src/config.cpp',
|
||||||
dependencies: test_dep,
|
|
||||||
include_directories: test_inc,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
safesignal_test = executable(
|
if tz_dep.found()
|
||||||
'safesignal_test',
|
test_dep += tz_dep
|
||||||
'SafeSignal.cpp',
|
test_src += files('waybar_time.cpp')
|
||||||
|
endif
|
||||||
|
|
||||||
|
waybar_test = executable(
|
||||||
|
'waybar_test',
|
||||||
|
test_src,
|
||||||
dependencies: test_dep,
|
dependencies: test_dep,
|
||||||
include_directories: test_inc,
|
include_directories: test_inc,
|
||||||
)
|
)
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'Configuration test',
|
'waybar',
|
||||||
config_test,
|
waybar_test,
|
||||||
workdir: meson.source_root(),
|
|
||||||
)
|
|
||||||
|
|
||||||
test(
|
|
||||||
'SafeSignal test',
|
|
||||||
safesignal_test,
|
|
||||||
workdir: meson.source_root(),
|
workdir: meson.source_root(),
|
||||||
)
|
)
|
||||||
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
#include "util/waybar_time.hpp"
|
||||||
|
|
||||||
|
#include <date/date.h>
|
||||||
|
#include <date/tz.h>
|
||||||
|
|
||||||
|
#include <catch2/catch.hpp>
|
||||||
|
#include <chrono>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
using namespace std::literals::chrono_literals;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check that the date/time formatter with locale and timezone support is working as expected.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const date::zoned_time<std::chrono::seconds> TEST_TIME = date::make_zoned(
|
||||||
|
"UTC", date::local_days{date::Monday[1] / date::January / 2022} + 13h + 4min + 5s);
|
||||||
|
|
||||||
|
TEST_CASE("Format UTC time", "[clock][util]") {
|
||||||
|
waybar::waybar_time tm{std::locale("C"), TEST_TIME};
|
||||||
|
|
||||||
|
REQUIRE(fmt::format("{}", tm).empty()); // no format specified
|
||||||
|
REQUIRE(fmt::format("{:%c %Z}", tm) == "Mon Jan 3 13:04:05 2022 UTC");
|
||||||
|
REQUIRE(fmt::format("{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103130405");
|
||||||
|
|
||||||
|
/* Test a few locales that are most likely to be present */
|
||||||
|
SECTION("US locale") {
|
||||||
|
try {
|
||||||
|
tm.locale = std::locale("en_US");
|
||||||
|
|
||||||
|
REQUIRE(fmt::format("{}", tm).empty()); // no format specified
|
||||||
|
REQUIRE_THAT(fmt::format("{:%c}", tm), // HowardHinnant/date#704
|
||||||
|
Catch::Matchers::StartsWith("Mon 03 Jan 2022 01:04:05 PM"));
|
||||||
|
REQUIRE(fmt::format("{:%x %X}", tm) == "01/03/2022 01:04:05 PM");
|
||||||
|
REQUIRE(fmt::format("{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103130405");
|
||||||
|
} catch (const std::runtime_error&) {
|
||||||
|
// locale not found; ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SECTION("GB locale") {
|
||||||
|
try {
|
||||||
|
tm.locale = std::locale("en_GB");
|
||||||
|
|
||||||
|
REQUIRE(fmt::format("{}", tm).empty()); // no format specified
|
||||||
|
REQUIRE_THAT(fmt::format("{:%c}", tm), // HowardHinnant/date#704
|
||||||
|
Catch::Matchers::StartsWith("Mon 03 Jan 2022 13:04:05"));
|
||||||
|
REQUIRE(fmt::format("{:%x %X}", tm) == "03/01/22 13:04:05");
|
||||||
|
REQUIRE(fmt::format("{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103130405");
|
||||||
|
} catch (const std::runtime_error&) {
|
||||||
|
// locale not found; ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Format zoned time", "[clock][util]") {
|
||||||
|
waybar::waybar_time tm{std::locale("C"), date::make_zoned("America/New_York", TEST_TIME)};
|
||||||
|
|
||||||
|
REQUIRE(fmt::format("{}", tm).empty()); // no format specified
|
||||||
|
REQUIRE(fmt::format("{:%c %Z}", tm) == "Mon Jan 3 08:04:05 2022 EST");
|
||||||
|
REQUIRE(fmt::format("{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103080405");
|
||||||
|
|
||||||
|
/* Test a few locales that are most likely to be present */
|
||||||
|
SECTION("US locale") {
|
||||||
|
try {
|
||||||
|
tm.locale = std::locale("en_US");
|
||||||
|
|
||||||
|
REQUIRE(fmt::format("{}", tm).empty()); // no format specified
|
||||||
|
REQUIRE_THAT(fmt::format("{:%c}", tm), // HowardHinnant/date#704
|
||||||
|
Catch::Matchers::StartsWith("Mon 03 Jan 2022 08:04:05 AM"));
|
||||||
|
REQUIRE(fmt::format("{:%x %X}", tm) == "01/03/2022 08:04:05 AM");
|
||||||
|
REQUIRE(fmt::format("{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103080405");
|
||||||
|
} catch (const std::runtime_error&) {
|
||||||
|
// locale not found; ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("GB locale") {
|
||||||
|
try {
|
||||||
|
tm.locale = std::locale("en_GB");
|
||||||
|
|
||||||
|
REQUIRE(fmt::format("{}", tm).empty()); // no format specified
|
||||||
|
REQUIRE_THAT(fmt::format("{:%c}", tm), // HowardHinnant/date#704
|
||||||
|
Catch::Matchers::StartsWith("Mon 03 Jan 2022 08:04:05"));
|
||||||
|
REQUIRE(fmt::format("{:%x %X}", tm) == "03/01/22 08:04:05");
|
||||||
|
REQUIRE(fmt::format("{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103080405");
|
||||||
|
} catch (const std::runtime_error&) {
|
||||||
|
// locale not found; ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue