Merge branch 'master' into on-update

This commit is contained in:
Alex 2020-04-12 18:32:19 +02:00 committed by GitHub
commit 4a7dd400fe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
63 changed files with 838 additions and 396 deletions

View File

@ -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 libnl3-dev pulseaudio-dev libmpdclient-dev scdoc RUN apk add --no-cache git meson alpine-sdk libinput-dev wayland-dev wayland-protocols mesa-dev libxkbcommon-dev eudev-dev pixman-dev gtkmm3-dev jsoncpp-dev pugixml libnl3-dev pulseaudio-dev libmpdclient-dev scdoc

View File

@ -3,4 +3,4 @@
FROM archlinux/base:latest FROM archlinux/base:latest
RUN pacman -Syu --noconfirm && \ RUN pacman -Syu --noconfirm && \
pacman -S git meson base-devel libinput wayland wayland-protocols pixman libxkbcommon mesa gtkmm3 jsoncpp scdoc --noconfirm pacman -S git meson base-devel libinput wayland wayland-protocols pixman libxkbcommon mesa gtkmm3 jsoncpp pugixml scdoc --noconfirm

View File

@ -3,5 +3,5 @@
FROM debian:sid FROM debian:sid
RUN apt-get update && \ RUN apt-get update && \
apt-get install -y build-essential meson ninja-build git pkg-config libinput10 libinput-dev wayland-protocols libwayland-client0 libwayland-cursor0 libwayland-dev libegl1-mesa-dev libgles2-mesa-dev libgbm-dev libxkbcommon-dev libudev-dev libpixman-1-dev libgtkmm-3.0-dev libjsoncpp-dev scdoc && \ apt-get install -y build-essential meson ninja-build git pkg-config libinput10 libpugixml-dev libinput-dev wayland-protocols libwayland-client0 libwayland-cursor0 libwayland-dev libegl1-mesa-dev libgles2-mesa-dev libgbm-dev libxkbcommon-dev libudev-dev libpixman-1-dev libgtkmm-3.0-dev libjsoncpp-dev scdoc && \
apt-get clean apt-get clean

View File

@ -2,6 +2,6 @@
FROM fedora:30 FROM fedora:30
RUN dnf install sway meson git libinput-devel wayland-devel wayland-protocols-devel egl-wayland-devel mesa-libEGL-devel mesa-libGLES-devel mesa-libgbm-devel libxkbcommon-devel libudev-devel pixman-devel gtkmm30-devel jsoncpp-devel scdoc -y && \ RUN dnf install sway meson git libinput-devel wayland-devel wayland-protocols-devel pugixml-devel egl-wayland-devel mesa-libEGL-devel mesa-libGLES-devel mesa-libgbm-devel libxkbcommon-devel libudev-devel pixman-devel gtkmm30-devel jsoncpp-devel scdoc -y && \
dnf group install "C Development Tools and Libraries" -y && \ dnf group install "C Development Tools and Libraries" -y && \
dnf clean all -y dnf clean all -y

View File

@ -4,4 +4,4 @@ FROM opensuse/tumbleweed:latest
RUN zypper -n up && \ RUN zypper -n up && \
zypper -n install -t pattern devel_C_C++ && \ zypper -n install -t pattern devel_C_C++ && \
zypper -n install git meson clang libinput10 libinput-devel libwayland-client0 libwayland-cursor0 wayland-protocols-devel wayland-devel Mesa-libEGL-devel Mesa-libGLESv2-devel libgbm-devel libxkbcommon-devel libudev-devel libpixman-1-0-devel gtkmm3-devel jsoncpp-devel scdoc zypper -n install git meson clang libinput10 libinput-devel libpugixml1 libwayland-client0 libwayland-cursor0 wayland-protocols-devel wayland-devel Mesa-libEGL-devel Mesa-libGLESv2-devel libgbm-devel libxkbcommon-devel libudev-devel libpixman-1-0-devel gtkmm3-devel jsoncpp-devel scdoc

View File

@ -1,6 +1,6 @@
.PHONY: build build-debug run clean default install .PHONY: build build-debug run clean default install
default: run default: build
build: build:
meson build meson build

View File

@ -49,6 +49,9 @@ libsigc++
fmt fmt
wayland wayland
wlroots wlroots
libgtk-3-dev [gtk-layer-shell]
gobject-introspection [gtk-layer-shell]
libgirepository1.0-dev [gtk-layer-shell]
libpulse [Pulseaudio module] libpulse [Pulseaudio module]
libnl [Network module] libnl [Network module]
sway [Sway modules] sway [Sway modules]
@ -56,6 +59,30 @@ libdbusmenu-gtk3 [Tray module]
libmpdclient [MPD module] libmpdclient [MPD module]
``` ```
On Ubuntu 19.10 you can install all the relevant dependencies using this command:
```
sudo apt install \
clang-tidy \
gobject-introspection \
libdbusmenu-gtk3-dev \
libfmt-dev \
libgirepository1.0-dev \
libgtk-3-dev \
libgtkmm-3.0-dev \
libinput-dev \
libjsoncpp-dev \
libmpdclient-dev \
libnl-3-dev \
libnl-genl-3-dev \
libpulse-dev \
libsigc++-2.0-dev \
libspdlog-dev \
libwayland-dev \
scdoc
```
Contributions welcome! - have fun :)<br> Contributions welcome! - have fun :)<br>
The style guidelines is [Google's](https://google.github.io/styleguide/cppguide.html) The style guidelines is [Google's](https://google.github.io/styleguide/cppguide.html)

View File

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

View File

@ -30,26 +30,24 @@ class Client {
const std::string &style) const; const std::string &style) const;
void bindInterfaces(); void bindInterfaces();
const std::string getValidPath(const std::vector<std::string> &paths) const; const std::string getValidPath(const std::vector<std::string> &paths) const;
void handleOutput(std::unique_ptr<struct waybar_output> &output); void handleOutput(struct waybar_output &output);
bool isValidOutput(const Json::Value &config, std::unique_ptr<struct waybar_output> &output); bool isValidOutput(const Json::Value &config, struct waybar_output &output);
auto setupConfig(const std::string &config_file) -> void; auto setupConfig(const std::string &config_file) -> void;
auto setupCss(const std::string &css_file) -> void; auto setupCss(const std::string &css_file) -> void;
std::unique_ptr<struct waybar_output> &getOutput(uint32_t wl_name); struct waybar_output &getOutput(void *);
std::vector<Json::Value> getOutputConfigs(std::unique_ptr<struct waybar_output> &output); std::vector<Json::Value> getOutputConfigs(struct waybar_output &output);
static void handleGlobal(void *data, struct wl_registry *registry, uint32_t name, static void handleGlobal(void *data, struct wl_registry *registry, uint32_t name,
const char *interface, uint32_t version); const char *interface, uint32_t version);
static void handleGlobalRemove(void *data, struct wl_registry *registry, uint32_t name); static void handleGlobalRemove(void *data, struct wl_registry *registry, uint32_t name);
static void handleLogicalPosition(void *, struct zxdg_output_v1 *, int32_t, int32_t); static void handleOutputName(void *, struct zxdg_output_v1 *, const char *);
static void handleLogicalSize(void *, struct zxdg_output_v1 *, int32_t, int32_t); void handleMonitorAdded(Glib::RefPtr<Gdk::Monitor> monitor);
static void handleDone(void *, struct zxdg_output_v1 *); void handleMonitorRemoved(Glib::RefPtr<Gdk::Monitor> monitor);
static void handleName(void *, struct zxdg_output_v1 *, const char *);
static void handleDescription(void *, struct zxdg_output_v1 *, const char *);
Json::Value config_; Json::Value config_;
Glib::RefPtr<Gtk::StyleContext> style_context_; Glib::RefPtr<Gtk::StyleContext> style_context_;
Glib::RefPtr<Gtk::CssProvider> css_provider_; Glib::RefPtr<Gtk::CssProvider> css_provider_;
std::vector<std::unique_ptr<struct waybar_output>> outputs_; std::list<struct waybar_output> outputs_;
}; };
} // namespace waybar } // namespace waybar

View File

@ -6,11 +6,17 @@
#else #else
#include <fmt/chrono.h> #include <fmt/chrono.h>
#endif #endif
#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::modules {
struct waybar_time {
std::locale locale;
date::zoned_seconds ztime;
};
class Clock : public ALabel { class Clock : public ALabel {
public: public:
Clock(const std::string&, const Json::Value&); Clock(const std::string&, const Json::Value&);
@ -19,6 +25,15 @@ class Clock : public ALabel {
private: private:
util::SleeperThread thread_; util::SleeperThread thread_;
std::locale locale_;
const date::time_zone* time_zone_;
bool fixed_time_zone_;
date::year_month_day cached_calendar_ymd_;
std::string cached_calendar_text_;
auto calendar_text(const waybar_time& wtime) -> std::string;
auto weekdays_header(const date::weekday& first_dow, std::ostream& os) -> void;
auto first_day_of_week() -> date::weekday;
}; };
} // namespace waybar::modules } // namespace waybar::modules

View File

@ -17,8 +17,7 @@ class Memory : public ALabel {
static inline const std::string data_dir_ = "/proc/meminfo"; static inline const std::string data_dir_ = "/proc/meminfo";
void parseMeminfo(); void parseMeminfo();
unsigned long memtotal_; std::unordered_map<std::string, unsigned long> meminfo_;
unsigned long memfree_;
util::SleeperThread thread_; util::SleeperThread thread_;
}; };

View File

@ -36,6 +36,7 @@ class MPD : public ALabel {
bool stopped(); bool stopped();
bool playing(); bool playing();
bool paused();
const std::string module_name_; const std::string module_name_;

View File

@ -34,14 +34,17 @@ class Pulseaudio : public ALabel {
pa_cvolume pa_volume_; pa_cvolume pa_volume_;
bool muted_; bool muted_;
std::string port_name_; std::string port_name_;
std::string form_factor_;
std::string desc_; std::string desc_;
std::string monitor_; std::string monitor_;
std::string default_sink_name_;
// SOURCE // SOURCE
uint32_t source_idx_{0}; uint32_t source_idx_{0};
uint16_t source_volume_; uint16_t source_volume_;
bool source_muted_; bool source_muted_;
std::string source_port_name_; std::string source_port_name_;
std::string source_desc_; std::string source_desc_;
std::string default_source_name_;
}; };
} // namespace waybar::modules } // namespace waybar::modules

View File

@ -21,7 +21,7 @@ class Tray : public AModule {
static inline std::size_t nb_hosts_ = 0; static inline std::size_t nb_hosts_ = 0;
Gtk::Box box_; Gtk::Box box_;
SNI::Watcher watcher_; SNI::Watcher::singleton watcher_;
SNI::Host host_; SNI::Host host_;
}; };

View File

@ -7,10 +7,24 @@
namespace waybar::modules::SNI { namespace waybar::modules::SNI {
class Watcher { class Watcher {
private:
Watcher();
public: public:
Watcher(std::size_t id);
~Watcher(); ~Watcher();
using singleton = std::shared_ptr<Watcher>;
static singleton getInstance() {
static std::weak_ptr<Watcher> weak;
std::shared_ptr<Watcher> strong = weak.lock();
if (!strong) {
strong = std::shared_ptr<Watcher>(new Watcher());
weak = strong;
}
return strong;
}
private: private:
typedef enum { GF_WATCH_TYPE_HOST, GF_WATCH_TYPE_ITEM } GfWatchType; typedef enum { GF_WATCH_TYPE_HOST, GF_WATCH_TYPE_ITEM } GfWatchType;
@ -34,7 +48,6 @@ class Watcher {
void updateRegisteredItems(SnWatcher *obj); void updateRegisteredItems(SnWatcher *obj);
uint32_t bus_name_id_; uint32_t bus_name_id_;
uint32_t watcher_id_;
GSList * hosts_ = nullptr; GSList * hosts_ = nullptr;
GSList * items_ = nullptr; GSList * items_ = nullptr;
SnWatcher *watcher_ = nullptr; SnWatcher *watcher_ = nullptr;

View File

@ -8,6 +8,7 @@
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include "ipc.hpp" #include "ipc.hpp"
#include "util/sleeper_thread.hpp"
namespace waybar::modules::sway { namespace waybar::modules::sway {
@ -28,6 +29,7 @@ class Ipc {
void sendCmd(uint32_t type, const std::string &payload = ""); void sendCmd(uint32_t type, const std::string &payload = "");
void subscribe(const std::string &payload); void subscribe(const std::string &payload);
void handleEvent(); void handleEvent();
void setWorker(std::function<void()> &&func);
protected: protected:
static inline const std::string ipc_magic_ = "i3-ipc"; static inline const std::string ipc_magic_ = "i3-ipc";
@ -38,9 +40,10 @@ class Ipc {
struct ipc_response send(int fd, uint32_t type, const std::string &payload = ""); struct ipc_response send(int fd, uint32_t type, const std::string &payload = "");
struct ipc_response recv(int fd); struct ipc_response recv(int fd);
int fd_; int fd_;
int fd_event_; int fd_event_;
std::mutex mutex_; std::mutex mutex_;
util::SleeperThread thread_;
}; };
} // namespace waybar::modules::sway } // namespace waybar::modules::sway

View File

@ -6,7 +6,6 @@
#include "client.hpp" #include "client.hpp"
#include "modules/sway/ipc/client.hpp" #include "modules/sway/ipc/client.hpp"
#include "util/json.hpp" #include "util/json.hpp"
#include "util/sleeper_thread.hpp"
namespace waybar::modules::sway { namespace waybar::modules::sway {
@ -18,14 +17,11 @@ class Mode : public ALabel, public sigc::trackable {
private: private:
void onEvent(const struct Ipc::ipc_response&); void onEvent(const struct Ipc::ipc_response&);
void worker();
std::string mode_; std::string mode_;
util::JsonParser parser_; util::JsonParser parser_;
std::mutex mutex_; std::mutex mutex_;
Ipc ipc_;
util::SleeperThread thread_;
Ipc ipc_;
}; };
} // namespace waybar::modules::sway } // namespace waybar::modules::sway

View File

@ -7,7 +7,6 @@
#include "client.hpp" #include "client.hpp"
#include "modules/sway/ipc/client.hpp" #include "modules/sway/ipc/client.hpp"
#include "util/json.hpp" #include "util/json.hpp"
#include "util/sleeper_thread.hpp"
namespace waybar::modules::sway { namespace waybar::modules::sway {
@ -20,7 +19,6 @@ class Window : public ALabel, public sigc::trackable {
private: private:
void onEvent(const struct Ipc::ipc_response&); void onEvent(const struct Ipc::ipc_response&);
void onCmd(const struct Ipc::ipc_response&); void onCmd(const struct Ipc::ipc_response&);
void worker();
std::tuple<std::size_t, int, std::string, std::string> getFocusedNode(const Json::Value& nodes, std::tuple<std::size_t, int, std::string, std::string> getFocusedNode(const Json::Value& nodes,
std::string& output); std::string& output);
void getTree(); void getTree();
@ -33,9 +31,7 @@ class Window : public ALabel, public sigc::trackable {
std::size_t app_nb_; std::size_t app_nb_;
util::JsonParser parser_; util::JsonParser parser_;
std::mutex mutex_; std::mutex mutex_;
Ipc ipc_;
util::SleeperThread thread_;
Ipc ipc_;
}; };
} // namespace waybar::modules::sway } // namespace waybar::modules::sway

View File

@ -1,5 +1,6 @@
#pragma once #pragma once
#include <unordered_map>
#include <fmt/format.h> #include <fmt/format.h>
#include <gtkmm/button.h> #include <gtkmm/button.h>
#include <gtkmm/label.h> #include <gtkmm/label.h>
@ -8,7 +9,6 @@
#include "client.hpp" #include "client.hpp"
#include "modules/sway/ipc/client.hpp" #include "modules/sway/ipc/client.hpp"
#include "util/json.hpp" #include "util/json.hpp"
#include "util/sleeper_thread.hpp"
namespace waybar::modules::sway { namespace waybar::modules::sway {
@ -21,7 +21,6 @@ class Workspaces : public AModule, public sigc::trackable {
private: private:
void onCmd(const struct Ipc::ipc_response&); void onCmd(const struct Ipc::ipc_response&);
void onEvent(const struct Ipc::ipc_response&); void onEvent(const struct Ipc::ipc_response&);
void worker();
bool filterButtons(); bool filterButtons();
Gtk::Button& addButton(const Json::Value&); Gtk::Button& addButton(const Json::Value&);
void onButtonReady(const Json::Value&, Gtk::Button&); void onButtonReady(const Json::Value&, Gtk::Button&);
@ -38,9 +37,7 @@ class Workspaces : public AModule, public sigc::trackable {
util::JsonParser parser_; util::JsonParser parser_;
std::unordered_map<std::string, Gtk::Button> buttons_; std::unordered_map<std::string, Gtk::Button> buttons_;
std::mutex mutex_; std::mutex mutex_;
Ipc ipc_;
util::SleeperThread thread_;
Ipc ipc_;
}; };
} // namespace waybar::modules::sway } // namespace waybar::modules::sway

View File

@ -72,7 +72,10 @@ inline struct res exec(std::string cmd) {
if (!fp) return {-1, ""}; if (!fp) return {-1, ""};
auto output = command::read(fp); auto output = command::read(fp);
auto stat = command::close(fp, pid); auto stat = command::close(fp, pid);
return {WEXITSTATUS(stat), output}; if (WIFEXITED(stat)) {
return {WEXITSTATUS(stat), output};
}
return {-1, output};
} }
inline int32_t forkExec(std::string cmd) { inline int32_t forkExec(std::string cmd) {
@ -88,6 +91,7 @@ inline int32_t forkExec(std::string cmd) {
// Child executes the command // Child executes the command
if (!pid) { if (!pid) {
setpgid(pid, pid); setpgid(pid, pid);
signal(SIGCHLD, SIG_DFL);
execl("/bin/sh", "sh", "-c", cmd.c_str(), (char*)0); execl("/bin/sh", "sh", "-c", cmd.c_str(), (char*)0);
exit(0); exit(0);
} else { } else {

View File

@ -36,6 +36,10 @@ The *backlight* module displays the current backlight level.
typeof: string ++ typeof: string ++
Command to execute when the module is clicked. Command to execute when the module is clicked.
*on-click-middle*: ++
typeof: string ++
Command to execute when middle-clicked on the module using mousewheel.
*on-click-right* ++ *on-click-right* ++
typeof: string ++ typeof: string ++
Command to execute when the module is right clicked. Command to execute when the module is right clicked.

View File

@ -18,6 +18,10 @@ The *battery* module displays the current capacity and state (eg. charging) of y
typeof: string ++ typeof: string ++
The adapter to monitor, as in /sys/class/power_supply/ instead of auto detect. The adapter to monitor, as in /sys/class/power_supply/ instead of auto detect.
*full-at* ++
typeof: integer ++
Define the max percentage of the battery, usefull for an old battery, e.g. 96
*interval* ++ *interval* ++
typeof: integer ++ typeof: integer ++
default: 60 ++ default: 60 ++
@ -37,32 +41,36 @@ The *battery* module displays the current capacity and state (eg. charging) of y
default: {H} h {M} min ++ default: {H} h {M} min ++
The format, how the time should be displayed. The format, how the time should be displayed.
*format-icons* *format-icons*: ++
typeof: array/object typeof: array/object ++
Based on the current capacity, the corresponding icon gets selected. ++ Based on the current capacity, the corresponding icon gets selected. ++
The order is *low* to *high*. Or by the state if it is an object. The order is *low* to *high*. Or by the state if it is an object.
*max-length* ++ *max-length*: ++
typeof: integer++ typeof: integer++
The maximum length in character the module should display. The maximum length in character the module should display.
*rotate* ++ *rotate*: ++
typeof: integer++ typeof: integer++
Positive value to rotate the text label. Positive value to rotate the text label.
*on-click* ++ *on-click*: ++
typeof: string ++ typeof: string ++
Command to execute when clicked on the module. Command to execute when clicked on the module.
*on-click-middle*: ++
typeof: string ++
Command to execute when middle-clicked on the module using mousewheel.
*on-click-right* ++ *on-click-right* ++
typeof: string ++ typeof: string ++
Command to execute when you right clicked on the module. Command to execute when you right clicked on the module.
*on-scroll-up* ++ *on-scroll-up*: ++
typeof: string ++ typeof: string ++
Command to execute when scrolling up on the module. Command to execute when scrolling up on the module.
*on-scroll-down* ++ *on-scroll-down*: ++
typeof: string ++ typeof: string ++
Command to execute when scrolling down on the module. Command to execute when scrolling down on the module.

View File

@ -18,7 +18,18 @@ The *clock* module displays the current date and time.
*format*: ++ *format*: ++
typeof: string ++ typeof: string ++
default: {:%H:%M} ++ default: {:%H:%M} ++
The format, how the date and time should be displayed. The format, how the date and time should be displayed. ++
It uses the format of the date library. See https://howardhinnant.github.io/date/date.html#to_stream_formatting for details.
*timezone*: ++
typeof: string ++
default: inferred local timezone ++
The timezone to display the time in, e.g. America/New_York.
*locale*: ++
typeof: string ++
default: inferred from current locale ++
A locale to be used to display the time. Intended to render times in custom timezones with the proper language and format.
*max-length*: ++ *max-length*: ++
typeof: integer ++ typeof: integer ++
@ -32,6 +43,10 @@ The *clock* module displays the current date and time.
typeof: string ++ typeof: string ++
Command to execute when clicked on the module. Command to execute when clicked on the module.
*on-click-middle*: ++
typeof: string ++
Command to execute when middle-clicked on the module using mousewheel.
*on-click-right*: ++ *on-click-right*: ++
typeof: string ++ typeof: string ++
Command to execute when you right clicked on the module. Command to execute when you right clicked on the module.
@ -50,6 +65,10 @@ The *clock* module displays the current date and time.
View all valid format options in *strftime(3)*. View all valid format options in *strftime(3)*.
# FORMAT REPLACEMENTS
*{calendar}*: Current month calendar
# EXAMPLES # EXAMPLES
``` ```

View File

@ -36,6 +36,10 @@ The *cpu* module displays the current cpu utilization.
typeof: string ++ typeof: string ++
Command to execute when clicked on the module. Command to execute when clicked on the module.
*on-click-middle*: ++
typeof: string ++
Command to execute when middle-clicked on the module using mousewheel.
*on-click-right*: ++ *on-click-right*: ++
typeof: string ++ typeof: string ++
Command to execute when you right clicked on the module. Command to execute when you right clicked on the module.

View File

@ -33,6 +33,12 @@ Addressed by *custom/<name>*
You can update it manually with a signal. If no *interval* is defined, You can update it manually with a signal. If no *interval* is defined,
it is assumed that the out script loops it self. it is assumed that the out script loops it self.
*restart-interval*: ++
typeof: integer ++
The restart interval (in seconds).
Can't be used with the *interval* option, so only with continuous scripts.
Once the script exit, it'll be re-executed after the *restart-interval*.
*signal*: ++ *signal*: ++
typeof: integer ++ typeof: integer ++
The signal number used to update the module. The signal number used to update the module.
@ -59,6 +65,10 @@ Addressed by *custom/<name>*
typeof: string ++ typeof: string ++
Command to execute when clicked on the module. Command to execute when clicked on the module.
*on-click-middle*: ++
typeof: string ++
Command to execute when middle-clicked on the module using mousewheel.
*on-click-right*: ++ *on-click-right*: ++
typeof: string ++ typeof: string ++
Command to execute when you right clicked on the module. Command to execute when you right clicked on the module.

View File

@ -39,6 +39,10 @@ Addressed by *disk*
typeof: string ++ typeof: string ++
Command to execute when clicked on the module. Command to execute when clicked on the module.
*on-click-middle*: ++
typeof: string ++
Command to execute when middle-clicked on the module using mousewheel.
*on-click-right*: ++ *on-click-right*: ++
typeof: string ++ typeof: string ++
Command to execute when you right clicked on the module. Command to execute when you right clicked on the module.

View File

@ -31,6 +31,10 @@ screensaving, also known as "presentation mode".
typeof: string ++ typeof: string ++
Command to execute when clicked on the module. A click also toggles the state 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*: ++ *on-click-right*: ++
typeof: string ++ typeof: string ++
Command to execute when you right clicked on the module. Command to execute when you right clicked on the module.

View File

@ -38,6 +38,10 @@ Addressed by *memory*
typeof: string ++ typeof: string ++
Command to execute when clicked on the module. Command to execute when clicked on the module.
*on-click-middle*: ++
typeof: string ++
Command to execute when middle-clicked on the module using mousewheel.
*on-click-right*: ++ *on-click-right*: ++
typeof: string ++ typeof: string ++
Command to execute when you right clicked on the module. Command to execute when you right clicked on the module.

View File

@ -13,110 +13,118 @@ The *mpd* module displays information about a running "Music Player Daemon" inst
Addressed by *mpd* Addressed by *mpd*
*server*: ++ *server*: ++
typeof: string ++ typeof: string ++
The network address or Unix socket path of the MPD server. If empty, connect to the default host. The network address or Unix socket path of the MPD server. If empty, connect to the default host.
*port*: ++ *port*: ++
typeof: integer ++ typeof: integer ++
The port MPD listens to. If empty, use the default port. The port MPD listens to. If empty, use the default port.
*interval*: ++ *interval*: ++
typeof: integer++ typeof: integer++
default: 5 ++ default: 5 ++
The interval in which the connection to the MPD server is retried The interval in which the connection to the MPD server is retried
*timeout*: ++ *timeout*: ++
typeof: integer++ typeof: integer++
default: 30 ++ default: 30 ++
The timeout for the connection. Change this if your MPD server has a low `connection_timeout` setting The timeout for the connection. Change this if your MPD server has a low `connection_timeout` setting
*unknown-tag*: ++ *unknown-tag*: ++
typeof: string ++ typeof: string ++
default: "N/A" ++ default: "N/A" ++
The text to display when a tag is not present in the current song, but used in `format` The text to display when a tag is not present in the current song, but used in `format`
*format*: ++ *format*: ++
typeof: string ++ typeof: string ++
default: "{album} - {artist} - {title}" ++ default: "{album} - {artist} - {title}" ++
Information displayed when a song is playing or paused Information displayed when a song is playing.
*format-stopped*: ++ *format-stopped*: ++
typeof: string ++ typeof: string ++
default: "stopped" ++ default: "stopped" ++
Information displayed when the player is stopped. Information displayed when the player is stopped.
*format-paused*: ++
typeof: string ++
This format is used when a song is paused.
*format-disconnected*: ++ *format-disconnected*: ++
typeof: string ++ typeof: string ++
default: "disconnected" ++ default: "disconnected" ++
Information displayed when the MPD server can't be reached. Information displayed when the MPD server can't be reached.
*tooltip*: ++ *tooltip*: ++
typeof: bool ++ typeof: bool ++
default: true ++ default: true ++
Option to disable tooltip on hover. Option to disable tooltip on hover.
*tooltip-format*: ++ *tooltip-format*: ++
typeof: string ++ typeof: string ++
default: "MPD (connected)" ++ default: "MPD (connected)" ++
Tooltip information displayed when connected to MPD. Tooltip information displayed when connected to MPD.
*tooltip-format-disconnected*: ++ *tooltip-format-disconnected*: ++
typeof: string ++ typeof: string ++
default: "MPD (disconnected)" ++ default: "MPD (disconnected)" ++
Tooltip information displayed when the MPD server can't be reached. Tooltip information displayed when the MPD server can't be reached.
*rotate*: ++ *rotate*: ++
typeof: integer ++ typeof: integer ++
Positive value to rotate the text label. Positive value to rotate the text label.
*max-length*: ++ *max-length*: ++
typeof: integer ++ typeof: integer ++
The maximum length in character the module should display. The maximum length in character the module should display.
*on-click*: ++ *on-click*: ++
typeof: string ++ typeof: string ++
Command to execute when clicked on the module. Command to execute when clicked on the module.
*on-click-middle*: ++
typeof: string ++
Command to execute when middle-clicked on the module using mousewheel.
*on-click-right*: ++ *on-click-right*: ++
typeof: string ++ typeof: string ++
Command to execute when you right clicked on the module. Command to execute when you right clicked on the module.
*on-scroll-up*: ++ *on-scroll-up*: ++
typeof: string ++ typeof: string ++
Command to execute when scrolling up on the module. Command to execute when scrolling up on the module.
*on-scroll-down*: ++ *on-scroll-down*: ++
typeof: string ++ typeof: string ++
Command to execute when scrolling down on the module. Command to execute when scrolling down on the module.
*smooth-scrolling-threshold*: ++ *smooth-scrolling-threshold*: ++
typeof: double ++ typeof: double ++
Threshold to be used when scrolling. Threshold to be used when scrolling.
*state-icons*: ++ *state-icons*: ++
typeof: object ++ typeof: object ++
default: {} ++ default: {} ++
Icon to show depending on the play/pause state of the player (*{ "playing": "...", "paused": "..." }*) Icon to show depending on the play/pause state of the player (*{ "playing": "...", "paused": "..." }*)
*consume-icons*: ++ *consume-icons*: ++
typeof: object ++ typeof: object ++
default: {} ++ default: {} ++
Icon to show depending on the "consume" option (*{ "on": "...", "off": "..." }*) Icon to show depending on the "consume" option (*{ "on": "...", "off": "..." }*)
*random-icons*: ++ *random-icons*: ++
typeof: object ++ typeof: object ++
default: {} ++ default: {} ++
Icon to show depending on the "random" option (*{ "on": "...", "off": "..." }*) Icon to show depending on the "random" option (*{ "on": "...", "off": "..." }*)
*repeat-icons*: ++ *repeat-icons*: ++
typeof: object ++ typeof: object ++
default: {} ++ default: {} ++
Icon to show depending on the "repeat" option (*{ "on": "...", "off": "..." }*) Icon to show depending on the "repeat" option (*{ "on": "...", "off": "..." }*)
*single-icons*: ++ *single-icons*: ++
typeof: object ++ typeof: object ++
default: {} ++ default: {} ++
Icon to show depending on the "single" option (*{ "on": "...", "off": "..." }*) Icon to show depending on the "single" option (*{ "on": "...", "off": "..." }*)
# FORMAT REPLACEMENTS # FORMAT REPLACEMENTS

View File

@ -21,6 +21,11 @@ Addressed by *network*
default: 60 ++ default: 60 ++
The interval in which the network information gets polled (e.g. signal strength). The interval in which the network information gets polled (e.g. signal strength).
*family*: ++
typeof: string ++
default: *ipv4* ++
The address family that is used for the format replacement {ipaddr} and to determine if a network connection is present.
*format*: ++ *format*: ++
typeof: string ++ typeof: string ++
default: *{ifname}* ++ default: *{ifname}* ++
@ -42,6 +47,11 @@ Addressed by *network*
typeof: string ++ typeof: string ++
This format is used when the displayed interface is disconnected. This format is used when the displayed interface is disconnected.
*format-icons*: ++
typeof: array/object ++
Based on the current signal strength, the corresponding icon gets selected. ++
The order is *low* to *high*. Or by the state if it is an object.
*rotate*: ++ *rotate*: ++
typeof: integer ++ typeof: integer ++
Positive value to rotate the text label. Positive value to rotate the text label.
@ -54,6 +64,10 @@ Addressed by *network*
typeof: string ++ typeof: string ++
Command to execute when clicked on the module. Command to execute when clicked on the module.
*on-click-middle*: ++
typeof: string ++
Command to execute when middle-clicked on the module using mousewheel.
*on-click-right*: ++ *on-click-right*: ++
typeof: string ++ typeof: string ++
Command to execute when you right clicked on the module. Command to execute when you right clicked on the module.
@ -117,6 +131,8 @@ Addressed by *network*
*{bandwidthDownOctets}*: Instant down speed in octets/seconds. *{bandwidthDownOctets}*: Instant down speed in octets/seconds.
*{icon}*: Icon, as defined in *format-icons*.
# EXAMPLES # EXAMPLES
``` ```

View File

@ -59,6 +59,10 @@ Additionally you can control the volume by scrolling *up* or *down* while the cu
typeof: string ++ typeof: string ++
Command to execute when clicked on the module. Command to execute when clicked on the module.
*on-click-middle*: ++
typeof: string ++
Command to execute when middle-clicked on the module using mousewheel.
*on-click-right*: ++ *on-click-right*: ++
typeof: string ++ typeof: string ++
Command to execute when you right clicked on the module. Command to execute when you right clicked on the module.
@ -96,16 +100,17 @@ The following strings for *format-icons* are supported.
If they are found in the current PulseAudio port name, the corresponding icons will be selected. If they are found in the current PulseAudio port name, the corresponding icons will be selected.
- *default* (Shown, when no other port is found) - *default* (Shown, when no other port is found)
- *headphones* - *headphone*
- *speaker* - *speaker*
- *hdmi* - *hdmi*
- *headset* - *headset*
- *handsfree* - *hands-free*
- *portable* - *portable*
- *car* - *car*
- *hifi* - *hifi*
- *phone* - *phone*
# EXAMPLES # EXAMPLES
``` ```

View File

@ -29,6 +29,10 @@ Addressed by *sway/mode*
typeof: string ++ typeof: string ++
Command to execute when clicked on the module. Command to execute when clicked on the module.
*on-click-middle*: ++
typeof: string ++
Command to execute when middle-clicked on the module using mousewheel.
*on-click-right*: ++ *on-click-right*: ++
typeof: string ++ typeof: string ++
Command to execute when you right clicked on the module. Command to execute when you right clicked on the module.

View File

@ -29,6 +29,10 @@ Addressed by *sway/window*
typeof: string ++ typeof: string ++
Command to execute when clicked on the module. Command to execute when clicked on the module.
*on-click-middle*: ++
typeof: string ++
Command to execute when middle-clicked on the module using mousewheel.
*on-click-right*: ++ *on-click-right*: ++
typeof: string ++ typeof: string ++
Command to execute when you right clicked on the module. Command to execute when you right clicked on the module.

View File

@ -19,7 +19,7 @@ Addressed by *sway/workspaces*
*format*: ++ *format*: ++
typeof: string ++ typeof: string ++
default: {name} ++ default: {value} ++
The format, how information should be displayed. The format, how information should be displayed.
*format-icons*: ++ *format-icons*: ++
@ -62,7 +62,9 @@ Addressed by *sway/workspaces*
# FORMAT REPLACEMENTS # FORMAT REPLACEMENTS
*{name}*: Name of the workspace, as defined by sway. *{value}*: Name of the workspace, as defined by sway.
*{name}*: Number stripped from workspace value.
*{icon}*: Icon, as defined in *format-icons*. *{icon}*: Icon, as defined in *format-icons*.
@ -75,6 +77,7 @@ Additional to workspace name matching, the following *format-icons* can be set.
- *default*: Will be shown, when no string matches is found. - *default*: Will be shown, when no string matches is found.
- *urgent*: Will be shown, when workspace is flagged as urgent - *urgent*: Will be shown, when workspace is flagged as urgent
- *focused*: Will be shown, when workspace is focused - *focused*: Will be shown, when workspace is focused
- *persistent*: Will be shown, when workspace is persistent one.
# PERSISTENT WORKSPACES # PERSISTENT WORKSPACES

View File

@ -13,71 +13,83 @@ The *temperature* module displays the current temperature from a thermal zone.
Addressed by *temperature* Addressed by *temperature*
*thermal-zone*: ++ *thermal-zone*: ++
typeof: integer ++ typeof: integer ++
The thermal zone, as in */sys/class/thermal/*. The thermal zone, as in */sys/class/thermal/*.
*hwmon-path*: ++ *hwmon-path*: ++
typeof: string ++ typeof: string ++
The temperature path to use, e.g. */sys/class/hwmon/hwmon2/temp1_input* instead of one in */sys/class/thermal/*. The temperature path to use, e.g. */sys/class/hwmon/hwmon2/temp1_input* instead of one in */sys/class/thermal/*.
*hwmon-path-abs*: ++
typeof: string ++
The path of the hwmon-directory of the device, e.g. */sys/devices/pci0000:00/0000:00:18.3/hwmon*. (Note that the subdirectory *hwmon/hwmon#*, where *#* is a number is not part of the path!) Has to be used together with *input-filename*.
*input-filename*: ++
typeof: string ++
The temperature filename of your *hwmon-path-abs*, e.g. *temp1_input*
*critical-threshold*: ++ *critical-threshold*: ++
typeof: integer ++ typeof: integer ++
The threshold before it is considered critical (Celcius). The threshold before it is considered critical (Celsius).
*interval*: ++ *interval*: ++
typeof: integer ++ typeof: integer ++
default: 10 ++ default: 10 ++
The interval in which the information gets polled. The interval in which the information gets polled.
*format-critical*: ++ *format-critical*: ++
typeof: string ++ typeof: string ++
The format to use when temperature is considered critical The format to use when temperature is considered critical
*format*: ++ *format*: ++
typeof: string ++ typeof: string ++
default: {temperatureC}°C ++ default: {temperatureC}°C ++
The format (Celcius/Farenheit) in which the temperature should be displayed. The format (Celsius/Fahrenheit) in which the temperature should be displayed.
*format-icons*: ++ *format-icons*: ++
typeof: array ++ typeof: array ++
Based on the current temperature (Celcius) and *critical-threshold* if available, the corresponding icon gets selected. The order is *low* to *high*. Based on the current temperature (Celsius) and *critical-threshold* if available, the corresponding icon gets selected. The order is *low* to *high*.
*rotate*: ++ *rotate*: ++
typeof: integer ++ typeof: integer ++
Positive value to rotate the text label. Positive value to rotate the text label.
*max-length*: ++ *max-length*: ++
typeof: integer ++ typeof: integer ++
The maximum length in characters the module should display. The maximum length in characters the module should display.
*on-click*: ++ *on-click*: ++
typeof: string ++ typeof: string ++
Command to execute when you clicked on the module. Command to execute when you clicked on the module.
*on-click-middle*: ++
typeof: string ++
Command to execute when middle-clicked on the module using mousewheel.
*on-click-right*: ++ *on-click-right*: ++
typeof: string ++ typeof: string ++
Command to execute when you right clicked on the module. Command to execute when you right clicked on the module.
*on-scroll-up*: ++ *on-scroll-up*: ++
typeof: string ++ typeof: string ++
Command to execute when scrolling up on the module. Command to execute when scrolling up on the module.
*on-scroll-down*: ++ *on-scroll-down*: ++
typeof: string ++ typeof: string ++
Command to execute when scrolling down on the module. Command to execute when scrolling down on the module.
*smooth-scrolling-threshold*: ++ *smooth-scrolling-threshold*: ++
typeof: double ++ typeof: double ++
Threshold to be used when scrolling. Threshold to be used when scrolling.
*tooltip*: ++ *tooltip*: ++
typeof: bool ++ typeof: bool ++
default: true ++ default: true ++
Option to disable tooltip on hover. Option to disable tooltip on hover.
# FORMAT REPLACEMENTS # FORMAT REPLACEMENTS
*{temperatureC}*: Temperature in Celcius. *{temperatureC}*: Temperature in Celsius.
*{temperatureF}*: Temperature in Fahrenheit. *{temperatureF}*: Temperature in Fahrenheit.

View File

@ -23,7 +23,8 @@ Also a minimal example configuration can be found on the at the bottom of this m
*layer* ++ *layer* ++
typeof: string ++ typeof: string ++
default: bottom ++ default: bottom ++
Decide if the bar is displayed in front of the windows or behind them. Decide if the bar is displayed in front (*top*) of the windows or behind (*bottom*)
them.
*output* ++ *output* ++
typeof: string|array ++ typeof: string|array ++
@ -66,6 +67,12 @@ Also a minimal example configuration can be found on the at the bottom of this m
typeof: string ++ typeof: string ++
Optional name added as a CSS class, for styling multiple waybars. Optional name added as a CSS class, for styling multiple waybars.
*gtk-layer-shell* ++
typeof: bool ++
default: true ++
Option to disable the use of gtk-layer-shell for popups.
Only functional if compiled with gtk-layer-shell support.
# MODULE FORMAT # MODULE FORMAT
You can use PangoMarkupFormat (See https://developer.gnome.org/pango/stable/PangoMarkupFormat.html#PangoMarkupFormat). You can use PangoMarkupFormat (See https://developer.gnome.org/pango/stable/PangoMarkupFormat.html#PangoMarkupFormat).

View File

@ -1,6 +1,6 @@
project( project(
'waybar', 'cpp', 'c', 'waybar', 'cpp', 'c',
version: '0.8.0', version: '0.9.2',
license: 'MIT', license: 'MIT',
default_options : [ default_options : [
'cpp_std=c++17', 'cpp_std=c++17',
@ -9,6 +9,8 @@ project(
], ],
) )
compiler = meson.get_compiler('cpp')
cpp_args = [] cpp_args = []
cpp_link_args = [] cpp_link_args = []
@ -16,13 +18,14 @@ if get_option('libcxx')
cpp_args += ['-stdlib=libc++'] cpp_args += ['-stdlib=libc++']
cpp_link_args += ['-stdlib=libc++', '-lc++abi'] cpp_link_args += ['-stdlib=libc++', '-lc++abi']
cpp_link_args += ['-lc++fs'] if compiler.has_link_argument('-lc++fs')
cpp_link_args += ['-lc++fs']
endif
else else
cpp_link_args += ['-lstdc++fs'] cpp_link_args += ['-lstdc++fs']
endif endif
compiler = meson.get_compiler('cpp') git = find_program('git', native: true, required: false)
git = find_program('git', required: false)
if not git.found() if not git.found()
add_project_arguments('-DVERSION="@0@"'.format(meson.project_version()), language: 'cpp') add_project_arguments('-DVERSION="@0@"'.format(meson.project_version()), language: 'cpp')
@ -42,6 +45,22 @@ if not compiler.has_header('filesystem')
endif endif
endif endif
code = '''
#include <langinfo.h>
#include <locale.h>
int main(int argc, char** argv) {
locale_t locale = newlocale(LC_ALL, "en_US.UTF-8", nullptr);
char* str;
str = nl_langinfo_l(_NL_TIME_WEEK_1STDAY, locale);
str = nl_langinfo_l(_NL_TIME_FIRST_WEEKDAY, locale);
freelocale(locale);
return 0;
}
'''
if compiler.links(code, name : 'nl_langinfo with _NL_TIME_WEEK_1STDAY, _NL_TIME_FIRST_WEEKDAY')
add_project_arguments('-DHAVE_LANGINFO_1STDAY', language: 'cpp')
endif
add_global_arguments(cpp_args, language : 'cpp') add_global_arguments(cpp_args, language : 'cpp')
add_global_link_arguments(cpp_link_args, language : 'cpp') add_global_link_arguments(cpp_link_args, language : 'cpp')
@ -52,7 +71,7 @@ spdlog = dependency('spdlog', version : ['>=1.3.1'], fallback : ['spdlog', 'spdl
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') 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'))
jsoncpp = dependency('jsoncpp') jsoncpp = dependency('jsoncpp')
@ -62,7 +81,11 @@ libnlgen = dependency('libnl-genl-3.0', required: get_option('libnl'))
libpulse = dependency('libpulse', required: get_option('pulseaudio')) libpulse = dependency('libpulse', required: get_option('pulseaudio'))
libudev = dependency('libudev', required: get_option('libudev')) libudev = dependency('libudev', required: get_option('libudev'))
libmpdclient = dependency('libmpdclient', required: get_option('mpd')) libmpdclient = dependency('libmpdclient', required: get_option('mpd'))
gtk_layer_shell = dependency('gtk-layer-shell-0',
required: get_option('gtk-layer-shell'),
fallback : ['gtk-layer-shell', 'gtk_layer_shell_dep'])
systemd = dependency('systemd', required: get_option('systemd')) systemd = dependency('systemd', required: get_option('systemd'))
tz_dep = dependency('date', default_options : [ 'use_system_tzdb=true' ], fallback: [ 'date', 'tz_dep' ])
prefix = get_option('prefix') prefix = get_option('prefix')
conf_data = configuration_data() conf_data = configuration_data()
@ -136,6 +159,10 @@ if libmpdclient.found()
src_files += 'src/modules/mpd.cpp' src_files += 'src/modules/mpd.cpp'
endif endif
if gtk_layer_shell.found()
add_project_arguments('-DHAVE_GTK_LAYER_SHELL', language: 'cpp')
endif
subdir('protocol') subdir('protocol')
executable( executable(
@ -158,7 +185,9 @@ executable(
libnlgen, libnlgen,
libpulse, libpulse,
libudev, libudev,
libmpdclient libmpdclient,
gtk_layer_shell,
tz_dep
], ],
include_directories: [include_directories('include')], include_directories: [include_directories('include')],
install: true, install: true,

View File

@ -7,3 +7,4 @@ option('dbusmenu-gtk', type: 'feature', value: 'auto', description: 'Enable supp
option('man-pages', type: 'feature', value: 'auto', description: 'Generate and install man pages') option('man-pages', type: 'feature', value: 'auto', description: 'Generate and install man pages')
option('mpd', type: 'feature', value: 'auto', description: 'Enable support for the Music Player Daemon') option('mpd', type: 'feature', value: 'auto', description: 'Enable support for the Music Player Daemon')
option('out', type: 'string', value : '/', description: 'output prefix directory') option('out', type: 'string', value : '/', description: 'output prefix directory')
option('gtk-layer-shell', type: 'feature', value: 'auto', description: 'Use gtk-layer-shell library for popups support')

View File

@ -31,7 +31,9 @@
<property name='Id' type='s' access='read'/> <property name='Id' type='s' access='read'/>
<property name='Title' type='s' access='read'/> <property name='Title' type='s' access='read'/>
<property name='Status' type='s' access='read'/> <property name='Status' type='s' access='read'/>
<!-- See discussion on pull #536
<property name='WindowId' type='u' access='read'/> <property name='WindowId' type='u' access='read'/>
-->
<property name='IconThemePath' type='s' access='read'/> <property name='IconThemePath' type='s' access='read'/>
<property name='IconName' type='s' access='read'/> <property name='IconName' type='s' access='read'/>
<property name='IconPixmap' type='a(iiay)' access='read'/> <property name='IconPixmap' type='a(iiay)' access='read'/>
@ -44,4 +46,4 @@
<property name='Menu' type='o' access='read'/> <property name='Menu' type='o' access='read'/>
<property name='ItemIsMenu' type='b' access='read'/> <property name='ItemIsMenu' type='b' access='read'/>
</interface> </interface>
</node> </node>

View File

@ -1,5 +1,5 @@
{ {
"layer": "top", // Waybar at top layer // "layer": "top", // Waybar at top layer
// "position": "bottom", // Waybar position (top|bottom|left|right) // "position": "bottom", // Waybar position (top|bottom|left|right)
"height": 30, // Waybar height (to be removed for auto height) "height": 30, // Waybar height (to be removed for auto height)
// "width": 1280, // Waybar width // "width": 1280, // Waybar width
@ -64,7 +64,8 @@
"spacing": 10 "spacing": 10
}, },
"clock": { "clock": {
"tooltip-format": "{:%Y-%m-%d | %H:%M}", // "timezone": "America/New_York",
"tooltip-format": "<big>{:%Y %B}</big>\n<tt><small>{calendar}</small></tt>",
"format-alt": "{:%Y-%m-%d}" "format-alt": "{:%Y-%m-%d}"
}, },
"cpu": { "cpu": {
@ -121,8 +122,8 @@
"format-source": "{volume}% ", "format-source": "{volume}% ",
"format-source-muted": "", "format-source-muted": "",
"format-icons": { "format-icons": {
"headphones": "", "headphone": "",
"handsfree": "", "hands-free": "",
"headset": "", "headset": "",
"phone": "", "phone": "",
"portable": "", "portable": "",

View File

@ -38,6 +38,8 @@ def on_metadata(player, metadata, manager):
elif player.get_artist() != '' and player.get_title() != '': elif player.get_artist() != '' and player.get_title() != '':
track_info = '{artist} - {title}'.format(artist=player.get_artist(), track_info = '{artist} - {title}'.format(artist=player.get_artist(),
title=player.get_title()) title=player.get_title())
else:
track_info = player.get_title()
if player.props.status != 'Playing' and track_info: if player.props.status != 'Playing' and track_info:
track_info = '' + track_info track_info = '' + track_info
@ -77,7 +79,7 @@ def signal_handler(sig, frame):
def parse_arguments(): def parse_arguments():
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
# Increase verbosity with every occurance of -v # Increase verbosity with every occurence of -v
parser.add_argument('-v', '--verbose', action='count', default=0) parser.add_argument('-v', '--verbose', action='count', default=0)
# Define for which player we're listening # Define for which player we're listening
@ -123,4 +125,3 @@ def main():
if __name__ == '__main__': if __name__ == '__main__':
main() main()

View File

@ -2,6 +2,7 @@
Description=Highly customizable Wayland bar for Sway and Wlroots based compositors. Description=Highly customizable Wayland bar for Sway and Wlroots based compositors.
Documentation=https://github.com/Alexays/Waybar/wiki/ Documentation=https://github.com/Alexays/Waybar/wiki/
PartOf=wayland-session.target PartOf=wayland-session.target
After=wayland-session.target
[Service] [Service]
Type=dbus Type=dbus

View File

@ -1,3 +1,7 @@
#ifdef HAVE_GTK_LAYER_SHELL
#include <gtk-layer-shell.h>
#endif
#include "bar.hpp" #include "bar.hpp"
#include "client.hpp" #include "client.hpp"
#include "factory.hpp" #include "factory.hpp"
@ -8,7 +12,7 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config)
config(w_config), config(w_config),
window{Gtk::WindowType::WINDOW_TOPLEVEL}, window{Gtk::WindowType::WINDOW_TOPLEVEL},
surface(nullptr), surface(nullptr),
layer_surface(nullptr), layer_surface_(nullptr),
anchor_(ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP), anchor_(ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP),
left_(Gtk::ORIENTATION_HORIZONTAL, 0), left_(Gtk::ORIENTATION_HORIZONTAL, 0),
center_(Gtk::ORIENTATION_HORIZONTAL, 0), center_(Gtk::ORIENTATION_HORIZONTAL, 0),
@ -28,11 +32,6 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config)
height_ = config["height"].isUInt() ? config["height"].asUInt() : height_; height_ = config["height"].isUInt() ? config["height"].asUInt() : height_;
width_ = config["width"].isUInt() ? config["width"].asUInt() : width_; width_ = config["width"].isUInt() ? config["width"].asUInt() : width_;
window.signal_realize().connect_notify(sigc::mem_fun(*this, &Bar::onRealize));
window.signal_map_event().connect_notify(sigc::mem_fun(*this, &Bar::onMap));
window.signal_configure_event().connect_notify(sigc::mem_fun(*this, &Bar::onConfigure));
window.set_size_request(width_, height_);
if (config["position"] == "bottom") { if (config["position"] == "bottom") {
anchor_ = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; anchor_ = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
} else if (config["position"] == "left") { } else if (config["position"] == "left") {
@ -98,6 +97,17 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config)
margins_ = {.top = gaps, .right = gaps, .bottom = gaps, .left = gaps}; margins_ = {.top = gaps, .right = gaps, .bottom = gaps, .left = gaps};
} }
#ifdef HAVE_GTK_LAYER_SHELL
use_gls_ = config["gtk-layer-shell"].isBool() ? config["gtk-layer-shell"].asBool() : true;
if (use_gls_) {
initGtkLayerShell();
}
#endif
window.signal_realize().connect_notify(sigc::mem_fun(*this, &Bar::onRealize));
window.signal_map_event().connect_notify(sigc::mem_fun(*this, &Bar::onMap));
window.signal_configure_event().connect_notify(sigc::mem_fun(*this, &Bar::onConfigure));
window.set_size_request(width_, height_);
setupWidgets(); setupWidgets();
if (window.get_realized()) { if (window.get_realized()) {
@ -131,11 +141,48 @@ void waybar::Bar::onConfigure(GdkEventConfigure* ev) {
tmp_width = ev->width; tmp_width = ev->width;
} }
} }
if (tmp_width != width_ || tmp_height != height_) { if (use_gls_) {
width_ = tmp_width;
height_ = tmp_height;
spdlog::debug("Set surface size {}x{} for output {}", width_, height_, output->name);
setExclusiveZone(tmp_width, tmp_height);
} else if (tmp_width != width_ || tmp_height != height_) {
setSurfaceSize(tmp_width, tmp_height); setSurfaceSize(tmp_width, tmp_height);
} }
} }
#ifdef HAVE_GTK_LAYER_SHELL
void waybar::Bar::initGtkLayerShell() {
auto gtk_window = window.gobj();
// this has to be executed before GtkWindow.realize
gtk_layer_init_for_window(gtk_window);
gtk_layer_set_keyboard_interactivity(gtk_window, FALSE);
auto layer = config["layer"] == "top" ? GTK_LAYER_SHELL_LAYER_TOP : GTK_LAYER_SHELL_LAYER_BOTTOM;
gtk_layer_set_layer(gtk_window, layer);
gtk_layer_set_monitor(gtk_window, output->monitor->gobj());
gtk_layer_set_namespace(gtk_window, "waybar");
gtk_layer_set_anchor(
gtk_window, GTK_LAYER_SHELL_EDGE_LEFT, anchor_ & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT);
gtk_layer_set_anchor(
gtk_window, GTK_LAYER_SHELL_EDGE_RIGHT, anchor_ & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT);
gtk_layer_set_anchor(
gtk_window, GTK_LAYER_SHELL_EDGE_TOP, anchor_ & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP);
gtk_layer_set_anchor(
gtk_window, GTK_LAYER_SHELL_EDGE_BOTTOM, anchor_ & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM);
gtk_layer_set_margin(gtk_window, GTK_LAYER_SHELL_EDGE_LEFT, margins_.left);
gtk_layer_set_margin(gtk_window, GTK_LAYER_SHELL_EDGE_RIGHT, margins_.right);
gtk_layer_set_margin(gtk_window, GTK_LAYER_SHELL_EDGE_TOP, margins_.top);
gtk_layer_set_margin(gtk_window, GTK_LAYER_SHELL_EDGE_BOTTOM, margins_.bottom);
if (width_ > 1 && height_ > 1) {
/* configure events are not emitted if the bar is using initial size */
setExclusiveZone(width_, height_);
}
}
#endif
void waybar::Bar::onRealize() { void waybar::Bar::onRealize() {
auto gdk_window = window.get_window()->gobj(); auto gdk_window = window.get_window()->gobj();
gdk_wayland_window_set_use_custom_surface(gdk_window); gdk_wayland_window_set_use_custom_surface(gdk_window);
@ -145,16 +192,22 @@ void waybar::Bar::onMap(GdkEventAny* ev) {
auto gdk_window = window.get_window()->gobj(); auto gdk_window = window.get_window()->gobj();
surface = gdk_wayland_window_get_wl_surface(gdk_window); surface = gdk_wayland_window_get_wl_surface(gdk_window);
if (use_gls_) {
return;
}
auto client = waybar::Client::inst(); auto client = waybar::Client::inst();
// owned by output->monitor; no need to destroy
auto wl_output = gdk_wayland_monitor_get_wl_output(output->monitor->gobj());
auto layer = auto layer =
config["layer"] == "top" ? ZWLR_LAYER_SHELL_V1_LAYER_TOP : ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM; config["layer"] == "top" ? ZWLR_LAYER_SHELL_V1_LAYER_TOP : ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM;
layer_surface = zwlr_layer_shell_v1_get_layer_surface( layer_surface_ = zwlr_layer_shell_v1_get_layer_surface(
client->layer_shell, surface, output->output, layer, "waybar"); client->layer_shell, surface, wl_output, layer, "waybar");
zwlr_layer_surface_v1_set_keyboard_interactivity(layer_surface, false); zwlr_layer_surface_v1_set_keyboard_interactivity(layer_surface_, false);
zwlr_layer_surface_v1_set_anchor(layer_surface, anchor_); zwlr_layer_surface_v1_set_anchor(layer_surface_, anchor_);
zwlr_layer_surface_v1_set_margin( zwlr_layer_surface_v1_set_margin(
layer_surface, margins_.top, margins_.right, margins_.bottom, margins_.left); layer_surface_, margins_.top, margins_.right, margins_.bottom, margins_.left);
setSurfaceSize(width_, height_); setSurfaceSize(width_, height_);
setExclusiveZone(width_, height_); setExclusiveZone(width_, height_);
@ -162,7 +215,7 @@ void waybar::Bar::onMap(GdkEventAny* ev) {
.configure = layerSurfaceHandleConfigure, .configure = layerSurfaceHandleConfigure,
.closed = layerSurfaceHandleClosed, .closed = layerSurfaceHandleClosed,
}; };
zwlr_layer_surface_v1_add_listener(layer_surface, &layer_surface_listener, this); zwlr_layer_surface_v1_add_listener(layer_surface_, &layer_surface_listener, this);
wl_surface_commit(surface); wl_surface_commit(surface);
wl_display_roundtrip(client->wl_display); wl_display_roundtrip(client->wl_display);
@ -182,7 +235,15 @@ void waybar::Bar::setExclusiveZone(uint32_t width, uint32_t height) {
} }
} }
spdlog::debug("Set exclusive zone {} for output {}", zone, output->name); spdlog::debug("Set exclusive zone {} for output {}", zone, output->name);
zwlr_layer_surface_v1_set_exclusive_zone(layer_surface, zone);
#ifdef HAVE_GTK_LAYER_SHELL
if (use_gls_) {
gtk_layer_set_exclusive_zone(window.gobj(), zone);
} else
#endif
{
zwlr_layer_surface_v1_set_exclusive_zone(layer_surface_, zone);
}
} }
void waybar::Bar::setSurfaceSize(uint32_t width, uint32_t height) { void waybar::Bar::setSurfaceSize(uint32_t width, uint32_t height) {
@ -198,7 +259,7 @@ void waybar::Bar::setSurfaceSize(uint32_t width, uint32_t height) {
width += margins_.right + margins_.left; width += margins_.right + margins_.left;
} }
spdlog::debug("Set surface size {}x{} for output {}", width, height, output->name); spdlog::debug("Set surface size {}x{} for output {}", width, height, output->name);
zwlr_layer_surface_v1_set_size(layer_surface, width, height); zwlr_layer_surface_v1_set_size(layer_surface_, width, height);
} }
// Converting string to button code rn as to avoid doing it later // Converting string to button code rn as to avoid doing it later
@ -282,9 +343,9 @@ void waybar::Bar::layerSurfaceHandleConfigure(void* data, struct zwlr_layer_surf
void waybar::Bar::layerSurfaceHandleClosed(void* data, struct zwlr_layer_surface_v1* /*surface*/) { void waybar::Bar::layerSurfaceHandleClosed(void* data, struct zwlr_layer_surface_v1* /*surface*/) {
auto o = static_cast<waybar::Bar*>(data); auto o = static_cast<waybar::Bar*>(data);
if (o->layer_surface) { if (o->layer_surface_) {
zwlr_layer_surface_v1_destroy(o->layer_surface); zwlr_layer_surface_v1_destroy(o->layer_surface_);
o->layer_surface = nullptr; o->layer_surface_ = nullptr;
} }
o->modules_left_.clear(); o->modules_left_.clear();
o->modules_center_.clear(); o->modules_center_.clear();

View File

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

View File

@ -115,6 +115,16 @@ const std::tuple<uint8_t, float, std::string> waybar::modules::Battery::getInfos
time_remaining = -(float)(total_energy_full - total_energy) / total_power; time_remaining = -(float)(total_energy_full - total_energy) / total_power;
} }
uint16_t capacity = total / batteries_.size(); uint16_t capacity = total / batteries_.size();
// Handle full-at
if (config_["full-at"].isUInt()) {
auto full_at = config_["full-at"].asUInt();
if (full_at < 100) {
capacity = static_cast<float>(capacity / full_at) * 100;
if (capacity > full_at) {
capacity = full_at;
}
}
}
return {capacity, time_remaining, status}; return {capacity, time_remaining, status};
} catch (const std::exception& e) { } catch (const std::exception& e) {
spdlog::error("Battery: {}", e.what()); spdlog::error("Battery: {}", e.what());
@ -163,7 +173,12 @@ auto waybar::modules::Battery::update() -> void {
} }
label_.set_tooltip_text(tooltip_text); label_.set_tooltip_text(tooltip_text);
} }
// Transform to lowercase
std::transform(status.begin(), status.end(), status.begin(), ::tolower); std::transform(status.begin(), status.end(), status.begin(), ::tolower);
// Replace space with dash
std::transform(status.begin(), status.end(), status.begin(), [](char ch) {
return ch == ' ' ? '-' : ch;
});
auto format = format_; auto format = format_;
auto state = getState(capacity, true); auto state = getState(capacity, true);
if (!old_status_.empty()) { if (!old_status_.empty()) {

View File

@ -1,8 +1,28 @@
#include "modules/clock.hpp" #include "modules/clock.hpp"
#include <time.h> #include <sstream>
#include <type_traits>
#ifdef HAVE_LANGINFO_1STDAY
#include <langinfo.h>
#include <locale.h>
#endif
using waybar::modules::waybar_time;
waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
: ALabel(config, "clock", id, "{:%H:%M}", 60) { : ALabel(config, "clock", id, "{:%H:%M}", 60)
, fixed_time_zone_(false)
{
if (config_["timezone"].isString()) {
time_zone_ = date::locate_zone(config_["timezone"].asString());
fixed_time_zone_ = true;
}
if (config_["locale"].isString()) {
locale_ = std::locale(config_["locale"].asString());
} else {
locale_ = std::locale("");
}
thread_ = [this] { thread_ = [this] {
dp.emit(); dp.emit();
auto now = std::chrono::system_clock::now(); auto now = std::chrono::system_clock::now();
@ -13,21 +33,115 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
} }
auto waybar::modules::Clock::update() -> void { auto waybar::modules::Clock::update() -> void {
tzset(); // Update timezone information if (!fixed_time_zone_) {
auto now = std::chrono::system_clock::now(); // Time zone can change. Be sure to pick that.
auto localtime = fmt::localtime(std::chrono::system_clock::to_time_t(now)); time_zone_ = date::current_zone();
auto text = fmt::format(format_, localtime); }
auto now = std::chrono::system_clock::now();
waybar_time wtime = {locale_,
date::make_zoned(time_zone_, date::floor<std::chrono::seconds>(now))};
auto text = fmt::format(format_, wtime);
label_.set_markup(text); label_.set_markup(text);
if (tooltipEnabled()) { if (tooltipEnabled()) {
if (config_["tooltip-format"].isString()) { if (config_["tooltip-format"].isString()) {
const auto calendar = calendar_text(wtime);
auto tooltip_format = config_["tooltip-format"].asString(); auto tooltip_format = config_["tooltip-format"].asString();
auto tooltip_text = fmt::format(tooltip_format, localtime); auto tooltip_text = fmt::format(tooltip_format, wtime, fmt::arg("calendar", calendar));
label_.set_tooltip_text(tooltip_text); label_.set_tooltip_markup(tooltip_text);
} else { } else {
label_.set_tooltip_text(text); label_.set_tooltip_markup(text);
} }
} }
// Call parent update // Call parent update
ALabel::update(); ALabel::update();
} }
auto waybar::modules::Clock::calendar_text(const waybar_time& wtime) -> std::string {
const auto daypoint = date::floor<date::days>(wtime.ztime.get_local_time());
const auto ymd = date::year_month_day(daypoint);
if (cached_calendar_ymd_ == ymd) {
return cached_calendar_text_;
}
const date::year_month ym(ymd.year(), ymd.month());
const auto curr_day = ymd.day();
std::stringstream os;
const auto first_dow = first_day_of_week();
weekdays_header(first_dow, os);
// First week prefixed with spaces if needed.
auto wd = date::weekday(ym/1);
auto empty_days = (wd - first_dow).count();
if (empty_days > 0) {
os << std::string(empty_days * 3 - 1, ' ');
}
auto last_day = (ym/date::literals::last).day();
for (auto d = date::day(1); d <= last_day; ++d, ++wd) {
if (wd != first_dow) {
os << ' ';
} else if (unsigned(d) != 1) {
os << '\n';
}
if (d == curr_day) {
os << "<b><u>" << date::format("%e", d) << "</u></b>";
} else {
os << date::format("%e", d);
}
}
auto result = os.str();
cached_calendar_ymd_ = ymd;
cached_calendar_text_ = result;
return result;
}
auto waybar::modules::Clock::weekdays_header(const date::weekday& first_dow, std::ostream& os) -> void {
auto wd = first_dow;
do {
if (wd != first_dow) os << ' ';
Glib::ustring wd_ustring(date::format(locale_, "%a", wd));
auto wd_len = wd_ustring.length();
if (wd_len > 2) {
wd_ustring = wd_ustring.substr(0, 2);
wd_len = 2;
}
const std::string pad(2 - wd_len, ' ');
os << pad << wd_ustring;
} while (++wd != first_dow);
os << "\n";
}
#ifdef HAVE_LANGINFO_1STDAY
template <auto fn>
using deleter_from_fn = std::integral_constant<decltype(fn), fn>;
template <typename T, auto fn>
using deleting_unique_ptr = std::unique_ptr<T, deleter_from_fn<fn>>;
#endif
// Computations done similarly to Linux cal utility.
auto waybar::modules::Clock::first_day_of_week() -> date::weekday {
#ifdef HAVE_LANGINFO_1STDAY
deleting_unique_ptr<std::remove_pointer<locale_t>::type, freelocale>
posix_locale{newlocale(LC_ALL, locale_.name().c_str(), nullptr)};
if (posix_locale) {
const int i = (std::intptr_t) nl_langinfo_l(_NL_TIME_WEEK_1STDAY, posix_locale.get());
auto ymd = date::year(i / 10000)/(i / 100 % 100)/(i % 100);
auto wd = date::weekday(ymd);
uint8_t j = *nl_langinfo_l(_NL_TIME_FIRST_WEEKDAY, posix_locale.get());
return wd + date::days(j - 1);
}
#endif
return date::Sunday;
}
template <>
struct fmt::formatter<waybar_time> : fmt::formatter<std::tm> {
template <typename FormatContext>
auto format(const waybar_time& t, FormatContext& ctx) {
return format_to(ctx.out(), "{}", date::format(t.locale, fmt::to_string(tm_format), t.ztime));
}
};

View File

@ -49,19 +49,24 @@ void waybar::modules::Custom::continuousWorker() {
thread_ = [&] { thread_ = [&] {
char* buff = nullptr; char* buff = nullptr;
size_t len = 0; size_t len = 0;
bool restart = false;
if (getline(&buff, &len, fp_) == -1) { if (getline(&buff, &len, fp_) == -1) {
int exit_code = 1; int exit_code = 1;
if (fp_) { if (fp_) {
exit_code = WEXITSTATUS(util::command::close(fp_, pid_)); exit_code = WEXITSTATUS(util::command::close(fp_, pid_));
fp_ = nullptr; fp_ = nullptr;
} }
thread_.stop();
if (exit_code != 0) { if (exit_code != 0) {
output_ = {exit_code, ""}; output_ = {exit_code, ""};
dp.emit(); dp.emit();
spdlog::error("{} stopped unexpectedly, is it endless?", name_); spdlog::error("{} stopped unexpectedly, is it endless?", name_);
} }
return; if (config_["restart-interval"].isUInt()) {
restart = true;
} else {
thread_.stop();
return;
}
} }
std::string output = buff; std::string output = buff;
@ -71,6 +76,14 @@ void waybar::modules::Custom::continuousWorker() {
} }
output_ = {0, output}; output_ = {0, output};
dp.emit(); dp.emit();
if (restart) {
pid_ = -1;
fp_ = util::command::open(cmd, pid_);
if (!fp_) {
throw std::runtime_error("Unable to open " + cmd);
}
thread_.sleep_for(std::chrono::seconds(config_["restart-interval"].asUInt()));
}
}; };
} }

View File

@ -10,11 +10,23 @@ waybar::modules::Memory::Memory(const std::string& id, const Json::Value& config
auto waybar::modules::Memory::update() -> void { auto waybar::modules::Memory::update() -> void {
parseMeminfo(); parseMeminfo();
if (memtotal_ > 0 && memfree_ >= 0) {
auto total_ram_gigabytes = memtotal_ / std::pow(1024, 2); unsigned long memtotal = meminfo_["MemTotal"];
int used_ram_percentage = 100 * (memtotal_ - memfree_) / memtotal_; unsigned long memfree;
auto used_ram_gigabytes = (memtotal_ - memfree_) / std::pow(1024, 2); if (meminfo_.count("MemAvailable")) {
auto available_ram_gigabytes = memfree_ / std::pow(1024, 2); // New kernels (3.4+) have an accurate available memory field.
memfree = meminfo_["MemAvailable"];
} else {
// Old kernel; give a best-effort approximation of available memory.
memfree = meminfo_["MemFree"] + meminfo_["Buffers"] + meminfo_["Cached"] +
meminfo_["SReclaimable"] - meminfo_["Shmem"];
}
if (memtotal > 0 && memfree >= 0) {
auto total_ram_gigabytes = memtotal / std::pow(1024, 2);
int used_ram_percentage = 100 * (memtotal - memfree) / memtotal;
auto used_ram_gigabytes = (memtotal - memfree) / std::pow(1024, 2);
auto available_ram_gigabytes = memfree / std::pow(1024, 2);
getState(used_ram_percentage); getState(used_ram_percentage);
label_.set_markup(fmt::format(format_, label_.set_markup(fmt::format(format_,
@ -35,7 +47,6 @@ auto waybar::modules::Memory::update() -> void {
} }
void waybar::modules::Memory::parseMeminfo() { void waybar::modules::Memory::parseMeminfo() {
int64_t memfree = -1, membuffer = -1, memcache = -1, memavail = -1;
std::ifstream info(data_dir_); std::ifstream info(data_dir_);
if (!info.is_open()) { if (!info.is_open()) {
throw std::runtime_error("Can't open " + data_dir_); throw std::runtime_error("Can't open " + data_dir_);
@ -46,23 +57,9 @@ void waybar::modules::Memory::parseMeminfo() {
if (posDelim == std::string::npos) { if (posDelim == std::string::npos) {
continue; continue;
} }
std::string name = line.substr(0, posDelim); std::string name = line.substr(0, posDelim);
int64_t value = std::stol(line.substr(posDelim + 1)); int64_t value = std::stol(line.substr(posDelim + 1));
meminfo_[name] = value;
if (name.compare("MemTotal") == 0) {
memtotal_ = value;
} else if (name.compare("MemAvailable") == 0) {
memavail = value;
} else if (name.compare("MemFree") == 0) {
memfree = value;
} else if (name.compare("Buffers") == 0) {
membuffer = value;
} else if (name.compare("Cached") == 0) {
memcache = value;
}
if (memtotal_ > 0 && (memavail >= 0 || (memfree > -1 && membuffer > -1 && memcache > -1))) {
break;
}
} }
memfree_ = memavail >= 0 ? memavail : memfree + membuffer + memcache;
} }

View File

@ -143,7 +143,9 @@ void waybar::modules::MPD::setLabel() {
if (playing()) { if (playing()) {
label_.get_style_context()->add_class("playing"); label_.get_style_context()->add_class("playing");
label_.get_style_context()->remove_class("paused"); label_.get_style_context()->remove_class("paused");
} else { } else if (paused()) {
format =
config_["format-paused"].isString() ? config_["format-paused"].asString() : config_["format"].asString();
label_.get_style_context()->add_class("paused"); label_.get_style_context()->add_class("paused");
label_.get_style_context()->remove_class("playing"); label_.get_style_context()->remove_class("playing");
} }
@ -349,3 +351,5 @@ bool waybar::modules::MPD::stopped() {
} }
bool waybar::modules::MPD::playing() { return connection_ != nullptr && state_ == MPD_STATE_PLAY; } bool waybar::modules::MPD::playing() { return connection_ != nullptr && state_ == MPD_STATE_PLAY; }
bool waybar::modules::MPD::paused() { return connection_ != nullptr && state_ == MPD_STATE_PAUSE; }

View File

@ -2,6 +2,7 @@
#include <spdlog/spdlog.h> #include <spdlog/spdlog.h>
#include <sys/eventfd.h> #include <sys/eventfd.h>
#include <fstream> #include <fstream>
#include <cassert>
#include "util/format.hpp" #include "util/format.hpp"
@ -278,8 +279,13 @@ auto waybar::modules::Network::update() -> void {
fmt::arg("bandwidthUpBits", pow_format(bandwidth_up * 8ull / interval_.count(), "b/s")), fmt::arg("bandwidthUpBits", pow_format(bandwidth_up * 8ull / interval_.count(), "b/s")),
fmt::arg("bandwidthDownOctets", pow_format(bandwidth_down / interval_.count(), "o/s")), fmt::arg("bandwidthDownOctets", pow_format(bandwidth_down / interval_.count(), "o/s")),
fmt::arg("bandwidthUpOctets", pow_format(bandwidth_up / interval_.count(), "o/s"))); fmt::arg("bandwidthUpOctets", pow_format(bandwidth_up / interval_.count(), "o/s")));
if (text != label_.get_label()) { if (text.compare(label_.get_label()) != 0) {
label_.set_markup(text); label_.set_markup(text);
if (text.empty()) {
event_box_.hide();
} else {
event_box_.show();
}
} }
if (tooltipEnabled()) { if (tooltipEnabled()) {
if (tooltip_format.empty() && config_["tooltip-format"].isString()) { if (tooltip_format.empty() && config_["tooltip-format"].isString()) {
@ -437,7 +443,6 @@ out:
} }
void waybar::modules::Network::getInterfaceAddress() { void waybar::modules::Network::getInterfaceAddress() {
unsigned int cidrRaw;
struct ifaddrs *ifaddr, *ifa; struct ifaddrs *ifaddr, *ifa;
cidr_ = 0; cidr_ = 0;
int success = getifaddrs(&ifaddr); int success = getifaddrs(&ifaddr);
@ -449,18 +454,34 @@ void waybar::modules::Network::getInterfaceAddress() {
if (ifa->ifa_addr != nullptr && ifa->ifa_addr->sa_family == family_ && if (ifa->ifa_addr != nullptr && ifa->ifa_addr->sa_family == family_ &&
ifa->ifa_name == ifname_) { ifa->ifa_name == ifname_) {
char ipaddr[INET6_ADDRSTRLEN]; char ipaddr[INET6_ADDRSTRLEN];
ipaddr_ = inet_ntop(family_,
&reinterpret_cast<struct sockaddr_in *>(ifa->ifa_addr)->sin_addr,
ipaddr,
INET6_ADDRSTRLEN);
char netmask[INET6_ADDRSTRLEN]; char netmask[INET6_ADDRSTRLEN];
auto net_addr = reinterpret_cast<struct sockaddr_in *>(ifa->ifa_netmask);
netmask_ = inet_ntop(family_, &net_addr->sin_addr, netmask, INET6_ADDRSTRLEN);
cidrRaw = net_addr->sin_addr.s_addr;
unsigned int cidr = 0; unsigned int cidr = 0;
while (cidrRaw) { if (family_ == AF_INET) {
cidr += cidrRaw & 1; ipaddr_ = inet_ntop(AF_INET,
cidrRaw >>= 1; &reinterpret_cast<struct sockaddr_in *>(ifa->ifa_addr)->sin_addr,
ipaddr,
INET_ADDRSTRLEN);
auto net_addr = reinterpret_cast<struct sockaddr_in *>(ifa->ifa_netmask);
netmask_ = inet_ntop(AF_INET, &net_addr->sin_addr, netmask, INET_ADDRSTRLEN);
unsigned int cidrRaw = net_addr->sin_addr.s_addr;
while (cidrRaw) {
cidr += cidrRaw & 1;
cidrRaw >>= 1;
}
} else {
ipaddr_ = inet_ntop(AF_INET6,
&reinterpret_cast<struct sockaddr_in6 *>(ifa->ifa_addr)->sin6_addr,
ipaddr,
INET6_ADDRSTRLEN);
auto net_addr = reinterpret_cast<struct sockaddr_in6 *>(ifa->ifa_netmask);
netmask_ = inet_ntop(AF_INET6, &net_addr->sin6_addr, netmask, INET6_ADDRSTRLEN);
for (size_t i = 0; i < sizeof(net_addr->sin6_addr.s6_addr); ++i) {
unsigned char cidrRaw = net_addr->sin6_addr.s6_addr[i];
while (cidrRaw) {
cidr += cidrRaw & 1;
cidrRaw >>= 1;
}
}
} }
cidr_ = cidr; cidr_ = cidr;
break; break;

View File

@ -21,7 +21,7 @@ waybar::modules::Pulseaudio::Pulseaudio(const std::string &id, const Json::Value
if (context_ == nullptr) { if (context_ == nullptr) {
throw std::runtime_error("pa_context_new() failed."); throw std::runtime_error("pa_context_new() failed.");
} }
if (pa_context_connect(context_, nullptr, PA_CONTEXT_NOAUTOSPAWN, nullptr) < 0) { if (pa_context_connect(context_, nullptr, PA_CONTEXT_NOFAIL, nullptr) < 0) {
auto err = auto err =
fmt::format("pa_context_connect() failed: {}", pa_strerror(pa_context_errno(context_))); fmt::format("pa_context_connect() failed: {}", pa_strerror(pa_context_errno(context_)));
throw std::runtime_error(err); throw std::runtime_error(err);
@ -52,7 +52,8 @@ void waybar::modules::Pulseaudio::contextStateCb(pa_context *c, void *data) {
pa_context_set_subscribe_callback(c, subscribeCb, data); pa_context_set_subscribe_callback(c, subscribeCb, data);
pa_context_subscribe( pa_context_subscribe(
c, c,
static_cast<enum pa_subscription_mask>(static_cast<int>(PA_SUBSCRIPTION_MASK_SINK) | 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_SOURCE)), static_cast<int>(PA_SUBSCRIPTION_MASK_SOURCE)),
nullptr, nullptr,
nullptr); nullptr);
@ -109,7 +110,9 @@ void waybar::modules::Pulseaudio::subscribeCb(pa_context * conte
if (operation != PA_SUBSCRIPTION_EVENT_CHANGE) { if (operation != PA_SUBSCRIPTION_EVENT_CHANGE) {
return; return;
} }
if (facility == PA_SUBSCRIPTION_EVENT_SINK) { if (facility == PA_SUBSCRIPTION_EVENT_SERVER) {
pa_context_get_server_info(context, serverInfoCb, data);
} 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_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);
@ -131,15 +134,15 @@ void waybar::modules::Pulseaudio::volumeModifyCb(pa_context *c, int success, voi
*/ */
void waybar::modules::Pulseaudio::sourceInfoCb(pa_context * /*context*/, const pa_source_info *i, void waybar::modules::Pulseaudio::sourceInfoCb(pa_context * /*context*/, const pa_source_info *i,
int /*eol*/, void *data) { int /*eol*/, void *data) {
if (i != nullptr) { auto pa = static_cast<waybar::modules::Pulseaudio *>(data);
auto self = static_cast<waybar::modules::Pulseaudio *>(data); if (i != nullptr && pa->default_source_name_ == i->name) {
auto source_volume = static_cast<float>(pa_cvolume_avg(&(i->volume))) / float{PA_VOLUME_NORM}; auto source_volume = static_cast<float>(pa_cvolume_avg(&(i->volume))) / float{PA_VOLUME_NORM};
self->source_volume_ = std::round(source_volume * 100.0F); pa->source_volume_ = std::round(source_volume * 100.0F);
self->source_idx_ = i->index; pa->source_idx_ = i->index;
self->source_muted_ = i->mute != 0; pa->source_muted_ = i->mute != 0;
self->source_desc_ = i->description; pa->source_desc_ = i->description;
self->source_port_name_ = i->active_port != nullptr ? i->active_port->name : "Unknown"; pa->source_port_name_ = i->active_port != nullptr ? i->active_port->name : "Unknown";
self->dp.emit(); pa->dp.emit();
} }
} }
@ -147,9 +150,9 @@ void waybar::modules::Pulseaudio::sourceInfoCb(pa_context * /*context*/, const p
* Called when the requested sink information is ready. * Called when the requested sink information is ready.
*/ */
void waybar::modules::Pulseaudio::sinkInfoCb(pa_context * /*context*/, const pa_sink_info *i, void waybar::modules::Pulseaudio::sinkInfoCb(pa_context * /*context*/, const pa_sink_info *i,
int /*eol*/, void * data) { int /*eol*/, void *data) {
if (i != nullptr) { auto pa = static_cast<waybar::modules::Pulseaudio *>(data);
auto pa = static_cast<waybar::modules::Pulseaudio *>(data); if (i != nullptr && pa->default_sink_name_ == i->name) {
pa->pa_volume_ = i->volume; pa->pa_volume_ = i->volume;
float volume = static_cast<float>(pa_cvolume_avg(&(pa->pa_volume_))) / float{PA_VOLUME_NORM}; float volume = static_cast<float>(pa_cvolume_avg(&(pa->pa_volume_))) / float{PA_VOLUME_NORM};
pa->sink_idx_ = i->index; pa->sink_idx_ = i->index;
@ -158,6 +161,9 @@ void waybar::modules::Pulseaudio::sinkInfoCb(pa_context * /*context*/, const pa_
pa->desc_ = i->description; pa->desc_ = i->description;
pa->monitor_ = i->monitor_source_name; pa->monitor_ = i->monitor_source_name;
pa->port_name_ = i->active_port != nullptr ? i->active_port->name : "Unknown"; pa->port_name_ = i->active_port != nullptr ? i->active_port->name : "Unknown";
if (auto ff = pa_proplist_gets(i->proplist, PA_PROP_DEVICE_FORM_FACTOR)) {
pa->form_factor_ = ff;
}
pa->dp.emit(); pa->dp.emit();
} }
} }
@ -168,16 +174,20 @@ void waybar::modules::Pulseaudio::sinkInfoCb(pa_context * /*context*/, const pa_
*/ */
void waybar::modules::Pulseaudio::serverInfoCb(pa_context *context, const pa_server_info *i, void waybar::modules::Pulseaudio::serverInfoCb(pa_context *context, const pa_server_info *i,
void *data) { void *data) {
auto pa = static_cast<waybar::modules::Pulseaudio *>(data);
pa->default_sink_name_ = i->default_sink_name;
pa->default_source_name_ = i->default_source_name;
pa_context_get_sink_info_by_name(context, i->default_sink_name, sinkInfoCb, data); pa_context_get_sink_info_by_name(context, i->default_sink_name, sinkInfoCb, data);
pa_context_get_source_info_by_name(context, i->default_source_name, sourceInfoCb, data); pa_context_get_source_info_by_name(context, i->default_source_name, sourceInfoCb, data);
} }
static const std::array<std::string, 9> ports = { static const std::array<std::string, 9> ports = {
"headphones", "headphone",
"speaker", "speaker",
"hdmi", "hdmi",
"headset", "headset",
"handsfree", "hands-free",
"portable", "portable",
"car", "car",
"hifi", "hifi",
@ -185,7 +195,7 @@ static const std::array<std::string, 9> ports = {
}; };
const std::string waybar::modules::Pulseaudio::getPortIcon() const { const std::string waybar::modules::Pulseaudio::getPortIcon() const {
std::string nameLC = port_name_; std::string nameLC = port_name_ + form_factor_;
std::transform(nameLC.begin(), nameLC.end(), nameLC.begin(), ::tolower); std::transform(nameLC.begin(), nameLC.end(), nameLC.begin(), ::tolower);
for (auto const &port : ports) { for (auto const &port : ports) {
if (nameLC.find(port) != std::string::npos) { if (nameLC.find(port) != std::string::npos) {
@ -197,21 +207,27 @@ const std::string waybar::modules::Pulseaudio::getPortIcon() const {
auto waybar::modules::Pulseaudio::update() -> void { auto waybar::modules::Pulseaudio::update() -> void {
auto format = format_; auto format = format_;
std::string format_name = "format"; if (!alt_) {
if (monitor_.find("a2dp_sink") != std::string::npos) { std::string format_name = "format";
format_name = format_name + "-bluetooth"; if (monitor_.find("a2dp_sink") != std::string::npos) {
label_.get_style_context()->add_class("bluetooth"); format_name = format_name + "-bluetooth";
} else { label_.get_style_context()->add_class("bluetooth");
label_.get_style_context()->remove_class("bluetooth"); } else {
label_.get_style_context()->remove_class("bluetooth");
}
if (muted_) {
// Check muted bluetooth format exist, otherwise fallback to default muted format
if (format_name != "format" && !config_[format_name + "-muted"].isString()) {
format_name = "format";
}
format_name = format_name + "-muted";
label_.get_style_context()->add_class("muted");
} else {
label_.get_style_context()->remove_class("muted");
}
format =
config_[format_name].isString() ? config_[format_name].asString() : format;
} }
if (muted_ ) {
format_name = format_name + "-muted";
label_.get_style_context()->add_class("muted");
} else {
label_.get_style_context()->remove_class("muted");
}
format =
config_[format_name].isString() ? config_[format_name].asString() : format;
// TODO: find a better way to split source/sink // TODO: find a better way to split source/sink
std::string format_source = "{volume}%"; std::string format_source = "{volume}%";
if (source_muted_ && config_["format-source-muted"].isString()) { if (source_muted_ && config_["format-source-muted"].isString()) {

View File

@ -265,7 +265,11 @@ void Item::updateImage() {
if (pixbuf->gobj() != nullptr) { if (pixbuf->gobj() != nullptr) {
// An icon specified by path and filename may be the wrong size for // An icon specified by path and filename may be the wrong size for
// the tray // the tray
pixbuf = pixbuf->scale_simple(icon_size, icon_size, Gdk::InterpType::INTERP_BILINEAR); // Keep the aspect ratio and scale to make the height equal to icon_size
// If people have non square icons, assume they want it to grow in width not height
int width = icon_size * pixbuf->get_width() / pixbuf->get_height();
pixbuf = pixbuf->scale_simple(width, icon_size, Gdk::InterpType::INTERP_BILINEAR);
image.set(pixbuf); image.set(pixbuf);
} }
} else { } else {

View File

@ -6,7 +6,7 @@ namespace waybar::modules::SNI {
Tray::Tray(const std::string& id, const Bar& bar, const Json::Value& config) Tray::Tray(const std::string& id, const Bar& bar, const Json::Value& config)
: AModule(config, "tray", id), : AModule(config, "tray", id),
box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0), box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0),
watcher_(nb_hosts_), watcher_(SNI::Watcher::getInstance()),
host_(nb_hosts_, config, std::bind(&Tray::onAdd, this, std::placeholders::_1), host_(nb_hosts_, config, std::bind(&Tray::onAdd, this, std::placeholders::_1),
std::bind(&Tray::onRemove, this, std::placeholders::_1)) { std::bind(&Tray::onRemove, this, std::placeholders::_1)) {
spdlog::warn( spdlog::warn(

View File

@ -3,14 +3,13 @@
using namespace waybar::modules::SNI; using namespace waybar::modules::SNI;
Watcher::Watcher(std::size_t id) Watcher::Watcher()
: bus_name_id_(Gio::DBus::own_name(Gio::DBus::BusType::BUS_TYPE_SESSION, : bus_name_id_(Gio::DBus::own_name(Gio::DBus::BusType::BUS_TYPE_SESSION,
"org.kde.StatusNotifierWatcher", "org.kde.StatusNotifierWatcher",
sigc::mem_fun(*this, &Watcher::busAcquired), sigc::mem_fun(*this, &Watcher::busAcquired),
Gio::DBus::SlotNameAcquired(), Gio::DBus::SlotNameLost(), Gio::DBus::SlotNameAcquired(), Gio::DBus::SlotNameLost(),
Gio::DBus::BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT | Gio::DBus::BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT |
Gio::DBus::BUS_NAME_OWNER_FLAGS_REPLACE)), Gio::DBus::BUS_NAME_OWNER_FLAGS_REPLACE)),
watcher_id_(id),
watcher_(sn_watcher_skeleton_new()) {} watcher_(sn_watcher_skeleton_new()) {}
Watcher::~Watcher() { Watcher::~Watcher() {
@ -23,6 +22,7 @@ Watcher::~Watcher() {
g_slist_free_full(items_, gfWatchFree); g_slist_free_full(items_, gfWatchFree);
items_ = nullptr; items_ = nullptr;
} }
Gio::DBus::unown_name(bus_name_id_);
auto iface = G_DBUS_INTERFACE_SKELETON(watcher_); auto iface = G_DBUS_INTERFACE_SKELETON(watcher_);
g_dbus_interface_skeleton_unexport(iface); g_dbus_interface_skeleton_unexport(iface);
} }
@ -34,7 +34,7 @@ void Watcher::busAcquired(const Glib::RefPtr<Gio::DBus::Connection>& conn, Glib:
if (error != nullptr) { if (error != nullptr) {
// Don't print an error when a watcher is already present // Don't print an error when a watcher is already present
if (error->code != 2) { if (error->code != 2) {
spdlog::error("Watcher {}: {}", watcher_id_, error->message); spdlog::error("Watcher: {}", error->message);
} }
g_error_free(error); g_error_free(error);
return; return;

View File

@ -10,19 +10,23 @@ Ipc::Ipc() {
} }
Ipc::~Ipc() { Ipc::~Ipc() {
// To fail the IPC header thread_.stop();
write(fd_, "close-sway-ipc", 14);
write(fd_event_, "close-sway-ipc", 14);
if (fd_ > 0) { if (fd_ > 0) {
// To fail the IPC header
write(fd_, "close-sway-ipc", 14);
close(fd_); close(fd_);
fd_ = -1; fd_ = -1;
} }
if (fd_event_ > 0) { if (fd_event_ > 0) {
write(fd_event_, "close-sway-ipc", 14);
close(fd_event_); close(fd_event_);
fd_event_ = -1; fd_event_ = -1;
} }
} }
void Ipc::setWorker(std::function<void()>&& func) { thread_ = func; }
const std::string Ipc::getSocketPath() const { const std::string Ipc::getSocketPath() const {
const char* env = getenv("SWAYSOCK"); const char* env = getenv("SWAYSOCK");
if (env != nullptr) { if (env != nullptr) {

View File

@ -8,7 +8,13 @@ Mode::Mode(const std::string& id, const Json::Value& config)
ipc_.subscribe(R"(["mode"])"); ipc_.subscribe(R"(["mode"])");
ipc_.signal_event.connect(sigc::mem_fun(*this, &Mode::onEvent)); ipc_.signal_event.connect(sigc::mem_fun(*this, &Mode::onEvent));
// Launch worker // Launch worker
worker(); ipc_.setWorker([this] {
try {
ipc_.handleEvent();
} catch (const std::exception& e) {
spdlog::error("Mode: {}", e.what());
}
});
dp.emit(); dp.emit();
} }
@ -31,16 +37,6 @@ void Mode::onEvent(const struct Ipc::ipc_response& res) {
} }
} }
void Mode::worker() {
thread_ = [this] {
try {
ipc_.handleEvent();
} catch (const std::exception& e) {
spdlog::error("Mode: {}", e.what());
}
};
}
auto Mode::update() -> void { auto Mode::update() -> void {
if (mode_.empty()) { if (mode_.empty()) {
event_box_.hide(); event_box_.hide();

View File

@ -11,7 +11,13 @@ Window::Window(const std::string& id, const Bar& bar, const Json::Value& config)
// Get Initial focused window // Get Initial focused window
getTree(); getTree();
// Launch worker // Launch worker
worker(); ipc_.setWorker([this] {
try {
ipc_.handleEvent();
} catch (const std::exception& e) {
spdlog::error("Window: {}", e.what());
}
});
} }
void Window::onEvent(const struct Ipc::ipc_response& res) { getTree(); } void Window::onEvent(const struct Ipc::ipc_response& res) { getTree(); }
@ -28,16 +34,6 @@ void Window::onCmd(const struct Ipc::ipc_response& res) {
} }
} }
void Window::worker() {
thread_ = [this] {
try {
ipc_.handleEvent();
} catch (const std::exception& e) {
spdlog::error("Window: {}", e.what());
}
};
}
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_);

View File

@ -22,7 +22,13 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value
window.signal_scroll_event().connect(sigc::mem_fun(*this, &Workspaces::handleScroll)); window.signal_scroll_event().connect(sigc::mem_fun(*this, &Workspaces::handleScroll));
} }
// Launch worker // Launch worker
worker(); ipc_.setWorker([this] {
try {
ipc_.handleEvent();
} catch (const std::exception &e) {
spdlog::error("Workspaces: {}", e.what());
}
});
} }
void Workspaces::onEvent(const struct Ipc::ipc_response &res) { void Workspaces::onEvent(const struct Ipc::ipc_response &res) {
@ -102,16 +108,6 @@ void Workspaces::onCmd(const struct Ipc::ipc_response &res) {
} }
} }
void Workspaces::worker() {
thread_ = [this] {
try {
ipc_.handleEvent();
} catch (const std::exception &e) {
spdlog::error("Workspaces: {}", e.what());
}
};
}
bool Workspaces::filterButtons() { bool Workspaces::filterButtons() {
bool needReorder = false; bool needReorder = false;
for (auto it = buttons_.begin(); it != buttons_.end();) { for (auto it = buttons_.begin(); it != buttons_.end();) {
@ -161,12 +157,13 @@ auto Workspaces::update() -> void {
if (needReorder) { if (needReorder) {
box_.reorder_child(button, it - workspaces_.begin()); box_.reorder_child(button, it - workspaces_.begin());
} }
std::string output = getIcon((*it)["name"].asString(), *it); std::string output = (*it)["name"].asString();
if (config_["format"].isString()) { if (config_["format"].isString()) {
auto format = config_["format"].asString(); auto format = config_["format"].asString();
output = fmt::format(format, output = fmt::format(format,
fmt::arg("icon", output), fmt::arg("icon", getIcon(output, *it)),
fmt::arg("name", trimWorkspaceName((*it)["name"].asString())), fmt::arg("value", output),
fmt::arg("name", trimWorkspaceName(output)),
fmt::arg("index", (*it)["num"].asString())); fmt::arg("index", (*it)["num"].asString()));
} }
if (!config_["disable-markup"].asBool()) { if (!config_["disable-markup"].asBool()) {
@ -211,6 +208,8 @@ std::string Workspaces::getIcon(const std::string &name, const Json::Value &node
if (config_["format-icons"][key].isString() && node[key].asBool()) { if (config_["format-icons"][key].isString() && node[key].asBool()) {
return config_["format-icons"][key].asString(); return config_["format-icons"][key].asString();
} }
} else if (config_["format_icons"]["persistent"].isString() && node["target_output"].isString()) {
return config_["format-icons"]["persistent"].asString();
} else if (config_["format-icons"][key].isString()) { } else if (config_["format-icons"][key].isString()) {
return config_["format-icons"][key].asString(); return config_["format-icons"][key].asString();
} }

View File

@ -1,9 +1,12 @@
#include "modules/temperature.hpp" #include "modules/temperature.hpp"
#include <filesystem>
waybar::modules::Temperature::Temperature(const std::string& id, const Json::Value& config) waybar::modules::Temperature::Temperature(const std::string& id, const Json::Value& config)
: ALabel(config, "temperature", id, "{temperatureC}°C", 10) { : ALabel(config, "temperature", id, "{temperatureC}°C", 10) {
if (config_["hwmon-path"].isString()) { if (config_["hwmon-path"].isString()) {
file_path_ = config_["hwmon-path"].asString(); file_path_ = config_["hwmon-path"].asString();
} else if (config_["hwmon-path-abs"].isString() && config_["input-filename"].isString()) {
file_path_ = (*std::filesystem::directory_iterator(config_["hwmon-path-abs"].asString())).path().u8string() + "/" + config_["input-filename"].asString();
} else { } else {
auto zone = config_["thermal-zone"].isInt() ? config_["thermal-zone"].asInt() : 0; auto zone = config_["thermal-zone"].isInt() ? config_["thermal-zone"].asInt() : 0;
file_path_ = fmt::format("/sys/class/thermal/thermal_zone{}/temp", zone); file_path_ = fmt::format("/sys/class/thermal/thermal_zone{}/temp", zone);

9
subprojects/date.wrap Normal file
View File

@ -0,0 +1,9 @@
[wrap-file]
source_url=https://github.com/HowardHinnant/date/archive/v2.4.1.tar.gz
source_filename=date-2.4.1.tar.gz
source_hash=98907d243397483bd7ad889bf6c66746db0d7d2a39cc9aacc041834c40b65b98
directory=date-2.4.1
patch_url = https://github.com/mesonbuild/hinnant-date/releases/download/2.4.1-1/hinnant-date.zip
patch_filename = hinnant-date-2.4.1-1-wrap.zip
patch_hash = 2061673a6f8e6d63c3a40df4da58fa2b3de2835fd9b3e74649e8279599f3a8f6

View File

@ -5,6 +5,6 @@ source_url = https://github.com/fmtlib/fmt/archive/5.3.0.tar.gz
source_filename = fmt-5.3.0.tar.gz source_filename = fmt-5.3.0.tar.gz
source_hash = defa24a9af4c622a7134076602070b45721a43c51598c8456ec6f2c4dbb51c89 source_hash = defa24a9af4c622a7134076602070b45721a43c51598c8456ec6f2c4dbb51c89
patch_url = https://wrapdb.mesonbuild.com/v1/projects/fmt/5.3.0/1/get_zip patch_url = https://github.com/mesonbuild/fmt/releases/download/5.3.0-1/fmt.zip
patch_filename = fmt-5.3.0-1-wrap.zip patch_filename = fmt-5.3.0-1-wrap.zip
patch_hash = 18f21a3b8833949c35d4ac88a7059577d5fa24b98786e4b1b2d3d81bb811440f patch_hash = 18f21a3b8833949c35d4ac88a7059577d5fa24b98786e4b1b2d3d81bb811440f

View File

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

View File

@ -5,6 +5,6 @@ source_url = https://github.com/gabime/spdlog/archive/v1.3.1.tar.gz
source_filename = v1.3.1.tar.gz source_filename = v1.3.1.tar.gz
source_hash = 160845266e94db1d4922ef755637f6901266731c4cb3b30b45bf41efa0e6ab70 source_hash = 160845266e94db1d4922ef755637f6901266731c4cb3b30b45bf41efa0e6ab70
patch_url = https://wrapdb.mesonbuild.com/v1/projects/spdlog/1.3.1/1/get_zip patch_url = https://github.com/mesonbuild/spdlog/releases/download/1.3.1-1/spdlog.zip
patch_filename = spdlog-1.3.1-1-wrap.zip patch_filename = spdlog-1.3.1-1-wrap.zip
patch_hash = 715a0229781019b853d409cc0bf891ee4b9d3a17bec0cf87f4ad30b28bbecc87 patch_hash = 715a0229781019b853d409cc0bf891ee4b9d3a17bec0cf87f4ad30b28bbecc87