Compare commits

..

126 Commits
0.1.2 ... 0.2.3

Author SHA1 Message Date
3691d84543 chore: v0.2.3 2018-12-18 17:42:35 +01:00
b554094c7e feat: args && class id 2018-12-18 17:30:54 +01:00
66ad2864c2 fix(Pulseaudio): use markup 2018-12-17 09:00:40 +01:00
c7b0639f32 fix(workspaces): check thread is running 2018-12-09 10:49:28 +01:00
0acc50264e fix(custom): failed getline after closed pipe 2018-12-08 13:57:56 +01:00
1b13f9e38c fix(custom): close endless scripts 2018-12-08 12:58:47 +01:00
7befd27059 fix: clock interval 2018-12-04 09:38:08 +01:00
2ec34e4adb feat: add $HOME to valid path 2018-12-03 20:12:34 +01:00
812a7a9861 fix: typo 2018-12-03 11:13:56 +01:00
1d96d57b75 feat: warn user about stopped endless custom module 2018-12-03 09:35:10 +01:00
691fb88057 refactor(modules): const bar 2018-12-01 00:10:41 +01:00
281bab4943 feat(ci): opensuse 2018-11-30 21:48:42 +01:00
8e93fd3ae0 revert: Update to new Sway IPC protocol 2018-11-29 09:50:00 +01:00
37c7e586d6 Merge pull request #110 from tokyovigilante/ipc-fix
Update to new Sway IPC protocol
2018-11-29 00:40:57 +01:00
3a5a470d90 Update to new Sway IPC protocol 2018-11-28 22:27:06 +00:00
c051b517ca Merge pull request #108 from SibrenVasse/spotify
fix(custom): fix conditional statement
2018-11-26 20:09:21 +01:00
a0cdef569b fix(custom): fix conditional statement 2018-11-26 19:35:03 +01:00
a123711a8d fix(network): disconnected forced interface 2018-11-25 21:32:50 +01:00
391a7de94a fix(window): escape window name 2018-11-25 15:21:00 +01:00
952d877652 Merge pull request #103 from SibrenVasse/spotify
Event based mediaplayer script
2018-11-24 18:13:08 +01:00
5356d25a9b Merge branch 'master' into spotify 2018-11-24 18:11:48 +01:00
65b9911df4 Merge pull request #105 from David96/master
Allow custom plugins that don't execute anything
2018-11-24 18:01:31 +01:00
2aed121903 Allow custom plugins that don't execute anything 2018-11-24 17:24:02 +01:00
08bfdda4cb revert(network): wait interface 2018-11-24 16:01:22 +01:00
8f8ec3b999 feat(Label): on-click-right 2018-11-24 15:56:16 +01:00
a17220054d feat(client): pefix module name on update error 2018-11-24 11:20:03 +01:00
4cc48b3cfd fix(client): catch error on update 2018-11-24 11:13:52 +01:00
087de4e956 refactor(client): lambda to method 2018-11-24 11:04:56 +01:00
2c2a0473f4 feat(client): throw when we don't have required resources files 2018-11-23 19:31:40 +01:00
2e1f8b2fc5 fix(network): check len of netlinkResponse 2018-11-23 17:52:10 +01:00
686bc4828e refactor(network): only get info when there is an interface 2018-11-23 17:46:14 +01:00
f6c2a8d9b7 fix(network): free the message instead of the socket 2018-11-23 17:42:26 +01:00
baa7f52e21 refactor(network): wait for new address 2018-11-23 16:04:29 +01:00
c4c0c01a2f feat(custom): event based mediaplayer script 2018-11-23 12:08:15 +01:00
2b05b8e69a chore: v0.2.2 2018-11-23 12:03:23 +01:00
ad7400d5ce refactor(ALabel): add interval 2018-11-23 11:57:37 +01:00
36652158ad refactor(tray): more cpp binding 2018-11-23 10:46:58 +01:00
8db94f7efd revert(ALabel): ref on format 2018-11-22 16:50:42 +01:00
aaaa17d94f fix(config): remove duplicate tray 2018-11-22 16:21:46 +01:00
0b1b0eb1a7 feat(tray): multiple hosts 2018-11-22 16:20:49 +01:00
f00be0b552 refactor(tray): remove useless variable 2018-11-22 15:51:55 +01:00
ba79b4d397 refactor(tray): cleanup and fixes 2018-11-22 15:47:23 +01:00
bd62c4cbc5 feat(Label): use set_markup 2018-11-21 20:49:09 +01:00
b3559328f1 fix(window): ellipsize 2018-11-20 23:24:33 +01:00
69fceb1c92 fix(tray): sigsev on click when multiple icons is available 2018-11-16 12:01:03 +01:00
33f138c16e chore: v0.2.1 2018-11-16 10:15:27 +01:00
01692b719a chore: update README 2018-11-16 10:07:20 +01:00
8c26a6aab7 chore: update README 2018-11-16 10:06:24 +01:00
e42fae32ab feat(network): network info interval 2018-11-16 10:02:12 +01:00
c910767378 refactor: remove usless using 2018-11-15 14:48:49 +01:00
94b9f0a399 feat(cpu): add both usage and load 2018-11-15 14:44:43 +01:00
1665003d23 fix: sigsev 2018-11-14 19:14:51 +01:00
e5573c20e6 Merge pull request #96 from Robinhuett/module_network_ipaddr
Module network ipaddr
2018-11-14 10:44:27 +01:00
75cc1bc318 refactor(network): codestyle and error handling 2018-11-14 10:31:17 +01:00
50e782e028 chore: update example config 2018-11-13 21:40:47 +01:00
5c66b1a770 feat(network): display ip address and subnetmask 2018-11-13 21:31:26 +01:00
3dc0f7ccf9 Merge pull request #94 from Robinhuett/cpu_fix
Use /proc/stat for cpu load
2018-11-11 13:41:11 +01:00
e1d98f0ad9 fix(cpu): show correct load
feat(cpu): show cores in tooltip
2018-11-11 03:11:32 +01:00
7222668326 refactor: disable battery timer stop on inotify event for now 2018-11-09 23:02:46 +01:00
315e2defde Merge pull request #93 from Robinhuett/pulseaudio_scroll
Add config option for volume change scroll step size
2018-11-09 22:55:51 +01:00
45bb8b1a1f refactor: simpler memory code 2018-11-09 22:55:25 +01:00
e21df5ae36 Add config option for volume change scroll step size 2018-11-09 22:48:27 +01:00
a5bca24f9b Merge pull request #91 from Robinhuett/ram_free_used
Use /proc/meminfo for Memory module
2018-11-09 22:34:11 +01:00
c07037d6b8 Update README.md 2018-11-09 17:27:35 +01:00
a9751545fa fix: update travis 2018-11-09 17:15:19 +01:00
9ea0815dea Use ifstream to reaad /proc/meminfo 2018-11-09 16:24:13 +01:00
b8b799a187 [ci skip] remove .SRCINFO at root 2018-11-09 15:36:21 +01:00
39dfa66261 feat(ci): archlinux 2018-11-09 15:19:17 +01:00
13702012a4 CI (#90) 2018-11-09 12:07:16 +01:00
6b62079d8a rewind stream instead of opening a new one 2018-11-09 00:17:30 +01:00
ac0963c608 Use /proc/meminfo for Memory module 2018-11-08 21:09:56 +01:00
2d2fb88040 fix: fmt 2018-11-08 09:57:24 +01:00
0933aad75f Merge pull request #87 from David96/master
Fix workspaces not being removed from bar when moved to another output
2018-11-05 21:06:53 +01:00
adcd956c24 Fix workspaces not being removed from bar when moved to another output 2018-11-05 20:59:28 +01:00
6c9e37699b Merge pull request #86 from David96/master
Fix clicking and scrolling through workspaces
2018-11-05 20:35:48 +01:00
168415440f Fix clicking and scrolling through workspaces
The way waybar used the workspace "num", clicking a workspace called "1:
something" resulted in going to a newly created workspace called "1",
because the workspace ipc command expects the workspace name, not its number.
2018-11-05 20:16:19 +01:00
d6af63d84a chore: add travis 2018-11-05 11:59:05 +01:00
26182c222b Merge pull request #79 from vberger/master
Don't call layer_surface.set_size on configure
2018-11-03 13:36:04 +01:00
43cd80fb31 chore: 0.2.0 2018-11-03 13:20:05 +01:00
2f6abfda59 Don't call layer_surface.set_size on configure 2018-11-03 13:16:13 +01:00
5ece0d98ee Merge pull request #78 from mithodin/filesystem-experimental
add option for when filesystem still lives in the experimental namespace
2018-11-03 13:00:04 +01:00
0637888460 even simpler check 2018-11-03 12:44:15 +01:00
ebbdaa168c automatically detect where filesystem lives 2018-11-02 23:15:42 +01:00
6ab01b1ad4 fix(style): not charging 2018-11-02 23:00:38 +01:00
cf921a5e14 Merge pull request #76 from mithodin/charging-full
Add class for full battery and give option to interpret unknown as full
2018-11-02 22:51:57 +01:00
25f31b19f6 formatting is hard. 2018-11-02 22:50:01 +01:00
d8b6201632 ...and fix the function signature in the header 2018-11-02 22:15:54 +01:00
123ce083b4 fix typo and initialize old_status_ 2018-11-02 22:08:55 +01:00
0522577fe5 make status and state fully configurable formats 2018-11-02 22:04:43 +01:00
1ff9fd06af Merge pull request #77 from mithodin/old-gdbus-codegen
fix compilation on systems with old gdbus-codegen
2018-11-02 21:23:10 +01:00
b6cad05489 fix formatting 2018-11-02 21:13:57 +01:00
236be90c2f add option for when filesystem still lives in the experimental namespace 2018-11-02 20:59:41 +01:00
f137090d55 fix compilation on systems with old gdbus-codegen 2018-11-02 20:13:09 +01:00
9c57df505c Add class for full battery and give option to interpret unknown as full 2018-11-02 19:41:00 +01:00
00e7e87f55 fix: style 2018-11-02 17:39:00 +01:00
836c543c62 fix: style 2018-11-02 17:07:51 +01:00
7bca5fd6bd feat(Bar): add a warning about minimum height 2018-11-02 12:35:26 +01:00
61e9f0803d Merge pull request #75 from ForTheReallys/proper_height
Fix #54
2018-11-02 12:26:14 +01:00
9b201c77d7 feat: battery states && format-full/charging 2018-11-02 11:23:29 +01:00
4b68840212 Fix #54 2018-11-01 16:00:38 -05:00
9d4048983d refactor: remove useless tmp variable 2018-11-01 09:27:00 +01:00
0670225e69 Merge pull request #72 from Robinhuett/custom_module_states
Custom modules can control tooltip and CSS class
2018-11-01 09:11:15 +01:00
e23fbd0add Added return-type json to custom module 2018-11-01 00:40:44 +01:00
341d3300fa Custom modules can control tooltip and CSS class 2018-10-30 21:28:31 +01:00
c3e185546d Merge pull request #68 from harishkrupo/master
Add configuration options for widgets on mouse events
2018-10-30 16:32:37 +01:00
0e93de9c0a Merge pull request #71 from Robinhuett/configurable_battery_levels
Added second warning stage to battery module
2018-10-30 16:31:01 +01:00
3e34137ac7 pulseaudio: Change volume on scroll event
Subscribe for mouse scroll events on the pulseaudio widget
and change volume when event is received.
Scroll up increments the volume and scroll down decrements it.
These events are only subscibed when there are no user defined
commands present for them.

Signed-off-by: Harish Krupo <harishkrupo@gmail.com>
2018-10-30 20:53:43 +05:30
4c8621c7a5 Added second warning stage to battery module
Also naming is a bit more consistent
2018-10-30 16:23:36 +01:00
d7d1ebd736 ALabel: Add support for configurable mouse events
This patch adds 3 new configuration options applicable for
subclasses of ALabel. The options can be used to execute
user defined code in response to the 3 mouse events:
* on-click: The left mouse button click
* on-scroll-up
* on-scroll-down
This patch also modifies the behaviour of the format-alt toggle
such that when the on-click event is configured, format-alt is
toggled on any mouse click other than left click. When on-click
is not defined, any mouse button would toggle format-alt.

Signed-off-by: Harish Krupo <harishkrupo@gmail.com>
2018-10-30 20:52:23 +05:30
e93c5e7957 Merge pull request #70 from Robinhuett/mode_module
Add module to show sway binding mode
2018-10-30 14:26:43 +01:00
668b7b736c Added default config for sway binding mode 2018-10-30 13:44:44 +01:00
a042eea384 Add module to show sway binding mode 2018-10-30 13:39:30 +01:00
c9a8a07976 fix(window): title on new workspace 2018-10-29 21:52:53 +01:00
4307e4fd8e chore: upgrade fmt to 5.2.0 2018-10-28 14:40:25 +01:00
daf613f8ca feat: add debug about tray beta 2018-10-28 08:43:48 +01:00
3f2eb0b492 chore: 0.1.3 2018-10-28 08:39:33 +01:00
4f773ea268 Merge pull request #65 from maxice8/fix-musl
add missing <cstring> include for strncpy, fixes musl
2018-10-28 08:29:56 +01:00
047473e5a4 add missing <cstring> include for strncpy, fixes musl
I/usr/include/libdbusmenu-glib-0.4 -flto -fdiagnostics-color=always -DNDEBUG -pipe -D_FILE_OFFSET_BITS=64 -std=c++17 -DHAVE_SWAY -DHAVE_LIBPULSE -DHAVE_DBUSMENU -D_FORTIFY_SOURCE=2 -mtune=generic -O2 -D_REENTRANT -pthread  -MD -MQ 'waybar@exe/src_modules_sway_ipc_client.cpp.o' -MF 'waybar@exe/src_modules_sway_ipc_client.cpp.o.d' -o 'waybar@exe/src_modules_sway_ipc_client.cpp.o' -c ../src/modules/sway/ipc/client.cpp
../src/modules/sway/ipc/client.cpp: In member function 'int waybar::modules::sway::Ipc::open(const string&) const':
../src/modules/sway/ipc/client.cpp:47:3: error: 'strncpy' was not declared in this scope
   strncpy(addr.sun_path, socketPath.c_str(), sizeof(addr.sun_path) - 1);
   ^~~~~~~
../src/modules/sway/ipc/client.cpp:47:3: note: 'strncpy' is defined in header '<cstring>'; did you forget to '#include <cstring>'?
../src/modules/sway/ipc/client.cpp:2:1:
+#include <cstring>

../src/modules/sway/ipc/client.cpp:47:3:
   strncpy(addr.sun_path, socketPath.c_str(), sizeof(addr.sun_path) - 1);
   ^~~~~~~
[36/44] Compiling C++ object 'waybar@exe/src_modules_custom.cpp.o'.
[37/44] Compiling C++ object 'waybar@exe/src_client.cpp.o'.
[38/44] Compiling C++ object 'waybar@exe/src_modules_cpu.cpp.o'.
ninja: build stopped: subcommand failed.
2018-10-28 04:06:07 -03:00
ed3e4b1395 fix(pulseaudio): check active_port is set 2018-10-27 11:23:43 +02:00
16b01e1059 Merge pull request #62 from colemickens/giounix20
meson: fix 'gio-unix-2.0' dependency
2018-10-27 09:35:47 +02:00
1ae490c8f7 Merge pull request #61 from colemickens/outdir
meson: make extra output directory configurable
2018-10-27 09:16:11 +02:00
0d0a3be483 meson: fix 'gio-unix-2.0' dependency 2018-10-26 23:21:03 -07:00
a1c4b9bb0c meson: make extra output directory configurable 2018-10-26 23:20:38 -07:00
a55a1ae866 fix(tray): icons size 2018-10-26 14:53:39 +02:00
07d8dfb3d6 feat(tray): spacing config 2018-10-26 12:08:50 +02:00
5010227e6b fix(tray): icons 2018-10-26 11:59:03 +02:00
59 changed files with 2623 additions and 615 deletions

View File

2
.gitignore vendored
View File

@ -4,7 +4,7 @@ vgcore.*
/.vscode
*.swp
packagecache
/subprojects/fmt-4.1.0
/subprojects/**/
/build
/dist
/meson.egg-info

18
.travis.yml Normal file
View File

@ -0,0 +1,18 @@
sudo: false
services:
- docker
env:
- distro: debian
- distro: archlinux
- distro: opensuse
before_install:
- docker pull alexays/waybar:${distro}
script:
- echo FROM alexays/waybar:${distro} > Dockerfile
- echo ADD . /root >> Dockerfile
- docker build -t waybar .
- docker run waybar /bin/sh -c "cd /root && git clone https://github.com/swaywm/wlroots subprojects/wlroots && meson build && ninja -C build"

4
Dockerfiles/archlinux Normal file
View File

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

5
Dockerfiles/debian Normal file
View File

@ -0,0 +1,5 @@
FROM debian:sid
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 && \
apt-get clean

5
Dockerfiles/opensuse Normal file
View File

@ -0,0 +1,5 @@
FROM opensuse/tumbleweed:latest
RUN zypper -n up && \
zypper -n install -t pattern devel_C_C++ && \
zypper -n install git meson clang 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

View File

@ -1,12 +1,11 @@
# Waybar [![Licence](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)<br>![Waybar](https://raw.githubusercontent.com/alexays/waybar/master/preview-2.png)
# Waybar [![Travis](https://travis-ci.org/Alexays/Waybar.svg?branch=master)](https://travis-ci.org/Alexays/Waybar) [![Licence](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)<br>![Waybar](https://raw.githubusercontent.com/alexays/waybar/master/preview-2.png)
**Proof of concept**
> Highly customizable Wayland bar for Sway and Wlroots based compositors.<br>
> Available on [AUR](https://aur.archlinux.org/packages/waybar-git/)
**Current features**
- Sway Workspaces
- Sway focused window name
- Sway (Workspaces, Binding mode, Focused window name)
- Tray (Beta) [#21](https://github.com/Alexays/Waybar/issues/21)
- Local time
- Battery
@ -25,6 +24,7 @@
```bash
$ git clone https://github.com/Alexays/Waybar
$ cd Waybar
$ meson build
$ ninja -C build
$ ./build/waybar

View File

@ -6,21 +6,26 @@
namespace waybar {
class ALabel : public IModule {
public:
ALabel(const Json::Value&, const std::string format);
virtual ~ALabel() = default;
virtual auto update() -> void;
virtual std::string getIcon(uint16_t, const std::string& alt = "");
virtual operator Gtk::Widget &();
protected:
Gtk::EventBox event_box_;
Gtk::Label label_;
const Json::Value& config_;
std::string format_;
private:
bool handleToggle(GdkEventButton* const& ev);
bool alt = false;
const std::string default_format_;
public:
ALabel(const Json::Value &, const std::string format, uint16_t interval = 0);
virtual ~ALabel() = default;
virtual auto update() -> void;
virtual std::string getIcon(uint16_t, const std::string &alt = "");
virtual operator Gtk::Widget &();
protected:
Gtk::EventBox event_box_;
Gtk::Label label_;
const Json::Value &config_;
std::string format_;
std::mutex mutex_;
const std::chrono::seconds interval_;
private:
bool handleToggle(GdkEventButton *const &ev);
bool handleScroll(GdkEventScroll *);
bool alt = false;
const std::string default_format_;
};
}
} // namespace waybar

View File

@ -15,7 +15,7 @@ class Client {
Client(int argc, char *argv[]);
int main(int argc, char *argv[]);
Glib::RefPtr<Gtk::Application> gtk_app;
Gtk::Main gtk_main;
std::string css_file;
std::string config_file;
Glib::RefPtr<Gdk::Display> gdk_display;
@ -27,8 +27,9 @@ class Client {
std::vector<std::unique_ptr<Bar>> bars;
private:
void setupConfigs(const std::string& config, const std::string& style);
void bindInterfaces();
auto setupCss();
const std::string getValidPath(std::vector<std::string> paths);
static void handleGlobal(void *data, struct wl_registry *registry,
uint32_t name, const char *interface, uint32_t version);

View File

@ -3,6 +3,7 @@
#include <json/json.h>
#include "modules/clock.hpp"
#ifdef HAVE_SWAY
#include "modules/sway/mode.hpp"
#include "modules/sway/workspaces.hpp"
#include "modules/sway/window.hpp"
#endif
@ -26,10 +27,10 @@ class Bar;
class Factory {
public:
Factory(Bar& bar, const Json::Value& config);
Factory(const Bar& bar, const Json::Value& config);
IModule* makeModule(const std::string &name) const;
private:
Bar& bar_;
const Bar& bar_;
const Json::Value& config_;
};

View File

@ -1,6 +1,10 @@
#pragma once
#ifdef FILESYSTEM_EXPERIMENTAL
#include <experimental/filesystem>
#else
#include <filesystem>
#endif
#include <fstream>
#include <iostream>
#include <fmt/format.h>
@ -11,22 +15,29 @@
namespace waybar::modules {
#ifdef FILESYSTEM_EXPERIMENTAL
namespace fs = std::experimental::filesystem;
#else
namespace fs = std::filesystem;
#endif
class Battery : public ALabel {
public:
Battery(const Json::Value&);
Battery(const std::string&, const Json::Value&);
~Battery();
auto update() -> void;
private:
static inline const fs::path data_dir_ = "/sys/class/power_supply/";
void worker();
const std::tuple<uint8_t, std::string> getInfos() const;
const std::string getState(uint8_t) const;
util::SleeperThread thread_;
util::SleeperThread threadTimer_;
util::SleeperThread thread_timer_;
std::vector<fs::path> batteries_;
int fd_;
std::string old_status_;
};
}

View File

@ -9,7 +9,7 @@ namespace waybar::modules {
class Clock : public ALabel {
public:
Clock(const Json::Value&);
Clock(const std::string&, const Json::Value&);
auto update() -> void;
private:
waybar::util::SleeperThread thread_;

View File

@ -2,6 +2,10 @@
#include <fmt/format.h>
#include <sys/sysinfo.h>
#include <fstream>
#include <vector>
#include <numeric>
#include <iostream>
#include "util/chrono.hpp"
#include "ALabel.hpp"
@ -9,9 +13,15 @@ namespace waybar::modules {
class Cpu : public ALabel {
public:
Cpu(const Json::Value&);
Cpu(const std::string&, const Json::Value&);
auto update() -> void;
private:
static inline const std::string data_dir_ = "/proc/stat";
uint16_t getCpuLoad();
std::tuple<uint16_t, std::string> getCpuUsage();
std::vector<std::tuple<size_t, size_t>> parseCpuinfo();
std::vector<std::tuple<size_t, size_t>> prev_times_;
waybar::util::SleeperThread thread_;
};

View File

@ -4,21 +4,31 @@
#include <iostream>
#include "util/chrono.hpp"
#include "util/command.hpp"
#include "util/json.hpp"
#include "ALabel.hpp"
namespace waybar::modules {
class Custom : public ALabel {
public:
Custom(const std::string, const Json::Value&);
Custom(const std::string&, const Json::Value&);
~Custom();
auto update() -> void;
private:
void delayWorker();
void continuousWorker();
void parseOutputRaw();
void parseOutputJson();
const std::string name_;
std::string text_;
std::string tooltip_;
std::string class_;
std::string prevclass_;
waybar::util::SleeperThread thread_;
waybar::util::command::res output_;
waybar::util::JsonParser parser_;
FILE* fp_;
};
}

View File

@ -1,7 +1,7 @@
#pragma once
#include <fmt/format.h>
#include <sys/sysinfo.h>
#include <fstream>
#include "util/chrono.hpp"
#include "ALabel.hpp"
@ -9,9 +9,13 @@ namespace waybar::modules {
class Memory : public ALabel {
public:
Memory(const Json::Value&);
Memory(const std::string&, const Json::Value&);
auto update() -> void;
private:
static inline const std::string data_dir_ = "/proc/meminfo";
unsigned long memtotal_;
unsigned long memfree_;
void parseMeminfo();
waybar::util::SleeperThread thread_;
};

View File

@ -1,6 +1,8 @@
#pragma once
#include <net/if.h>
#include <arpa/inet.h>
#include <ifaddrs.h>
#include <netlink/netlink.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/ctrl.h>
@ -13,7 +15,7 @@ namespace waybar::modules {
class Network : public ALabel {
public:
Network(const Json::Value&);
Network(const std::string&, const Json::Value&);
~Network();
auto update() -> void;
private:
@ -21,24 +23,30 @@ class Network : public ALabel {
static int netlinkResponse(int, void*, uint32_t, uint32_t groups = 0);
static int scanCb(struct nl_msg*, void*);
void worker();
void disconnected();
void initNL80211();
int getExternalInterface();
void getInterfaceAddress();
void parseEssid(struct nlattr**);
void parseSignal(struct nlattr**);
bool associatedOrJoined(struct nlattr**);
auto getInfo() -> void;
waybar::util::SleeperThread thread_;
waybar::util::SleeperThread thread_timer_;
int ifid_;
sa_family_t family_;
int sock_fd_;
struct sockaddr_nl nladdr_ = {};
struct sockaddr_nl nladdr_ = {0};
struct nl_sock* sk_ = nullptr;
int nl80211_id_;
std::string essid_;
std::string ifname_;
std::string ipaddr_;
std::string netmask_;
int cidr_;
int signal_strength_dbm_;
uint16_t signal_strength_;
};

View File

@ -1,7 +1,8 @@
#pragma once
#include <pulse/pulseaudio.h>
#include <fmt/format.h>
#include <pulse/pulseaudio.h>
#include <pulse/volume.h>
#include <algorithm>
#include "ALabel.hpp"
@ -9,7 +10,7 @@ namespace waybar::modules {
class Pulseaudio : public ALabel {
public:
Pulseaudio(const Json::Value&);
Pulseaudio(const std::string&, const Json::Value&);
~Pulseaudio();
auto update() -> void;
private:
@ -18,6 +19,8 @@ class Pulseaudio : public ALabel {
static void contextStateCb(pa_context*, void*);
static void sinkInfoCb(pa_context*, const pa_sink_info*, int, void*);
static void serverInfoCb(pa_context*, const pa_server_info*, void*);
static void volumeModifyCb(pa_context*, int, void*);
bool handleScroll(GdkEventScroll* e);
const std::string getPortIcon() const;
@ -26,9 +29,11 @@ class Pulseaudio : public ALabel {
pa_context* context_;
uint32_t sink_idx_{0};
uint16_t volume_;
pa_cvolume pa_volume_;
bool muted_;
std::string port_name_;
std::string desc_;
bool scrolling_;
};
}
} // namespace waybar::modules

View File

@ -0,0 +1,41 @@
#pragma once
#include <gtkmm.h>
#include <json/json.h>
#include <tuple>
#include <dbus-status-notifier-watcher.h>
#include "modules/sni/item.hpp"
namespace waybar::modules::SNI {
class Host {
public:
Host(const std::size_t id, const Json::Value&,
const std::function<void(std::unique_ptr<Item>&)>&,
const std::function<void(std::unique_ptr<Item>&)>&);
~Host();
private:
void busAcquired(const Glib::RefPtr<Gio::DBus::Connection>&, Glib::ustring);
void nameAppeared(const Glib::RefPtr<Gio::DBus::Connection>&, Glib::ustring, const Glib::ustring&);
void nameVanished(const Glib::RefPtr<Gio::DBus::Connection>&, Glib::ustring);
static void proxyReady(GObject*, GAsyncResult*, gpointer);
static void registerHost(GObject*, GAsyncResult*, gpointer);
static void itemRegistered(SnWatcher*, const gchar*, gpointer);
static void itemUnregistered(SnWatcher*, const gchar*, gpointer);
std::tuple<std::string, std::string> getBusNameAndObjectPath(const std::string);
void addRegisteredItem(std::string service);
std::vector<std::unique_ptr<Item>> items_;
const std::string bus_name_;
const std::string object_path_;
std::size_t bus_name_id_;
std::size_t watcher_id_;
GCancellable* cancellable_ = nullptr;
SnWatcher* watcher_ = nullptr;
const Json::Value &config_;
const std::function<void(std::unique_ptr<Item>&)> on_add_;
const std::function<void(std::unique_ptr<Item>&)> on_remove_;
};
}

View File

@ -2,21 +2,28 @@
#include <dbus-status-notifier-item.h>
#include <gtkmm.h>
#include <json/json.h>
#include <libdbusmenu-gtk/dbusmenu-gtk.h>
#ifdef FILESYSTEM_EXPERIMENTAL
#include <experimental/filesystem>
#else
#include <filesystem>
#endif
namespace waybar::modules::SNI {
class Item {
public:
Item(std::string, std::string, Glib::Dispatcher *);
Item(std::string, std::string, const Json::Value&);
~Item() = default;
std::string bus_name;
std::string object_path;
Gtk::EventBox event_box;
int icon_size;
int effective_icon_size;
Gtk::Image *image;
Gtk::Menu *gtk_menu = nullptr;
Gtk::Image image;
Gtk::EventBox event_box;
std::string category;
std::string id;
std::string status;
@ -30,20 +37,21 @@ public:
std::string attention_movie_name;
std::string icon_theme_path;
std::string menu;
DbusmenuGtkMenu *dbus_menu = nullptr;
Gtk::Menu *gtk_menu = nullptr;
bool item_is_menu;
private:
static void proxyReady(GObject *obj, GAsyncResult *res, gpointer data);
static void getAll(GObject *obj, GAsyncResult *res, gpointer data);
static void handleActivate(GObject *, GAsyncResult *, gpointer);
static void handleSecondaryActivate(GObject *, GAsyncResult *, gpointer);
void updateImage();
Glib::RefPtr<Gdk::Pixbuf> extractPixBuf(GVariant *variant);
Glib::RefPtr<Gdk::Pixbuf> getIconByName(std::string name, int size);
static void onMenuDestroyed(Item *self);
bool makeMenu(GdkEventButton *const &ev);
bool handleClick(GdkEventButton *const & /*ev*/);
Glib::Dispatcher *dp_;
GCancellable *cancellable_ = nullptr;
SnItem *proxy_ = nullptr;
};

View File

@ -1,37 +0,0 @@
#pragma once
#include <gtkmm.h>
#include <tuple>
#include <dbus-status-notifier-watcher.h>
#include "modules/sni/sni.hpp"
namespace waybar::modules::SNI {
class Host {
public:
Host(Glib::Dispatcher*);
std::vector<Item> items;
private:
static void busAcquired(GDBusConnection*, const gchar*, gpointer);
static void nameAppeared(GDBusConnection*, const gchar*, const gchar*,
gpointer);
static void nameVanished(GDBusConnection*, const gchar*, gpointer);
static void proxyReady(GObject*, GAsyncResult*, gpointer);
static void registerHost(GObject*, GAsyncResult*, gpointer);
static void itemRegistered(SnWatcher*, const gchar*, gpointer);
static void itemUnregistered(SnWatcher*, const gchar*, gpointer);
std::tuple<std::string, std::string> getBusNameAndObjectPath(const gchar*);
void addRegisteredItem(const gchar* service);
uint32_t bus_name_id_;
uint32_t watcher_id_;
std::string bus_name_;
std::string object_path_;
Glib::Dispatcher* dp_;
GCancellable* cancellable_ = nullptr;
SnWatcher* watcher_ = nullptr;
};
}

View File

@ -2,19 +2,24 @@
#include <fmt/format.h>
#include <thread>
#include "bar.hpp"
#include "util/json.hpp"
#include "IModule.hpp"
#include "modules/sni/snw.hpp"
#include "modules/sni/snh.hpp"
#include "modules/sni/watcher.hpp"
#include "modules/sni/host.hpp"
namespace waybar::modules::SNI {
class Tray : public IModule {
public:
Tray(const Json::Value&);
Tray(const std::string&, const Json::Value&);
auto update() -> void;
operator Gtk::Widget &();
private:
void onAdd(std::unique_ptr<Item>& item);
void onRemove(std::unique_ptr<Item>& item);
static inline std::size_t nb_hosts_ = 0;
std::thread thread_;
const Json::Value& config_;
Gtk::Box box_;

View File

@ -8,7 +8,7 @@ namespace waybar::modules::SNI {
class Watcher {
public:
Watcher();
~Watcher();
~Watcher() = default;
private:
typedef enum { GF_WATCH_TYPE_HOST, GF_WATCH_TYPE_ITEM } GfWatchType;
@ -22,7 +22,7 @@ private:
guint watch_id;
} GfWatch;
static void busAcquired(GDBusConnection *, const gchar *, gpointer);
void busAcquired(const Glib::RefPtr<Gio::DBus::Connection>&, Glib::ustring);
static gboolean handleRegisterHost(Watcher *, GDBusMethodInvocation *,
const gchar *);
static gboolean handleRegisterItem(Watcher *, GDBusMethodInvocation *,

View File

@ -1,6 +1,7 @@
#pragma once
#include <iostream>
#include <cstring>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>

View File

@ -0,0 +1,27 @@
#pragma once
#include <fmt/format.h>
#include "bar.hpp"
#include "client.hpp"
#include "util/chrono.hpp"
#include "util/json.hpp"
#include "ALabel.hpp"
#include "modules/sway/ipc/client.hpp"
namespace waybar::modules::sway {
class Mode : public ALabel {
public:
Mode(const std::string&, const waybar::Bar&, const Json::Value&);
auto update() -> void;
private:
void worker();
const Bar& bar_;
waybar::util::SleeperThread thread_;
util::JsonParser parser_;
Ipc ipc_;
std::string mode_;
};
}

View File

@ -13,14 +13,14 @@ namespace waybar::modules::sway {
class Window : public ALabel {
public:
Window(waybar::Bar&, const Json::Value&);
Window(const std::string&, const waybar::Bar&, const Json::Value&);
auto update() -> void;
private:
void worker();
std::tuple<int, std::string> getFocusedNode(Json::Value nodes);
void getFocusedWindow();
Bar& bar_;
const Bar& bar_;
waybar::util::SleeperThread thread_;
util::JsonParser parser_;
Ipc ipc_;

View File

@ -12,7 +12,7 @@ namespace waybar::modules::sway {
class Workspaces : public IModule {
public:
Workspaces(waybar::Bar&, const Json::Value&);
Workspaces(const std::string&, const waybar::Bar&, const Json::Value&);
auto update() -> void;
operator Gtk::Widget &();
private:
@ -20,17 +20,17 @@ class Workspaces : public IModule {
void addWorkspace(Json::Value);
std::string getIcon(std::string, Json::Value);
bool handleScroll(GdkEventScroll*);
int getPrevWorkspace();
int getNextWorkspace();
std::string getPrevWorkspace();
std::string getNextWorkspace();
Bar& bar_;
const Bar& bar_;
const Json::Value& config_;
waybar::util::SleeperThread thread_;
Gtk::Box box_;
util::JsonParser parser_;
std::mutex mutex_;
bool scrolling_;
std::unordered_map<int, Gtk::Button> buttons_;
std::unordered_map<std::string, Gtk::Button> buttons_;
Json::Value workspaces_;
Ipc ipc_;
};

View File

@ -23,35 +23,24 @@ struct SleeperThread {
SleeperThread() = default;
SleeperThread(std::function<void()> func)
: thread_{[this, func] {
while(true) {
{
std::lock_guard<std::mutex> lock(mutex_);
if (!do_run_) {
break;
}
}
func();
}
: do_run_(true), thread_{[this, func] {
while (do_run_) func();
}}
{}
SleeperThread& operator=(std::function<void()> func)
{
do_run_ = true;
thread_ = std::thread([this, func] {
while(true) {
{
std::lock_guard<std::mutex> lock(mutex_);
if (!do_run_) {
break;
}
}
func();
}
while (do_run_) func();
});
return *this;
}
bool isRunnging() const
{
return do_run_;
}
auto sleep_for(chrono::duration dur)
{
@ -74,21 +63,21 @@ struct SleeperThread {
{
do_run_ = false;
condvar_.notify_all();
if (thread_.joinable()) {
thread_.detach();
}
}
~SleeperThread()
{
stop();
if (thread_.joinable()) {
thread_.detach();
}
}
private:
bool do_run_ = false;
std::thread thread_;
std::condition_variable condvar_;
std::mutex mutex_;
bool do_run_ = true;
};
}

1264
include/util/clara.hpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -29,7 +29,23 @@ inline struct res exec(const std::string cmd)
output.erase(output.length()-1);
}
int exit_code = WEXITSTATUS(pclose(fp));
return { exit_code, output };
return {exit_code, output};
}
inline bool forkExec(std::string cmd) {
if (cmd == "") return true;
int32_t pid = fork();
if (pid < 0) {
printf("Unable to exec cmd %s, error %s", cmd.c_str(), strerror(errno));
return false;
}
// Child executes the command
if (!pid) execl("/bin/sh", "sh", "-c", cmd.c_str(), (char*)0);
return true;
}
} // namespace waybar::util::command

View File

@ -1,14 +1,15 @@
project(
'waybar', 'cpp', 'c',
version: '0.1.2',
version: '0.2.3',
license: 'MIT',
default_options : [
'cpp_std=c++17',
'buildtype=release'
'buildtype=release',
'default_library=static'
],
)
cpp_args = []
cpp_args = ['-DVERSION="@0@"'.format(meson.project_version())]
cpp_link_args = []
if false # libc++
@ -17,22 +18,28 @@ if false # libc++
cpp_link_args += ['-lc++fs']
else
# TODO: For std::filesystem in libstdc++. Still unstable? Or why is it not in libstdc++ proper yet?
cpp_link_args += ['-lstdc++fs']
endif
compiler = meson.get_compiler('cpp')
if not compiler.has_header('filesystem')
add_project_arguments('-DFILESYSTEM_EXPERIMENTAL', language: 'cpp')
endif
add_global_arguments(cpp_args, language : 'cpp')
add_global_link_arguments(cpp_link_args, language : 'cpp')
thread_dep = dependency('threads')
libinput = dependency('libinput')
fmt = dependency('fmt', fallback: ['fmtlib', 'fmt_dep'])
fmt = dependency('fmt', version : ['>=5.2.1'], fallback : ['fmt', 'fmt_dep'])
wayland_client = dependency('wayland-client')
wayland_cursor = dependency('wayland-cursor')
wayland_protos = dependency('wayland-protocols')
wlroots = dependency('wlroots', fallback: ['wlroots', 'wlroots'])
gtkmm = dependency('gtkmm-3.0')
dbusmenu_gtk = dependency('dbusmenu-gtk3-0.4', required: get_option('dbusmenu-gtk'))
giounix = dependency('gio-unix-2.0', required: get_option('dbusmenu-gtk'))
jsoncpp = dependency('jsoncpp')
sigcpp = dependency('sigc++-2.0')
libnl = dependency('libnl-3.0', required: get_option('libnl'))
@ -56,6 +63,7 @@ if find_program('sway', required : false).found()
add_project_arguments('-DHAVE_SWAY', language: 'cpp')
src_files += [
'src/modules/sway/ipc/client.cpp',
'src/modules/sway/mode.cpp',
'src/modules/sway/window.cpp',
'src/modules/sway/workspaces.cpp'
]
@ -75,9 +83,9 @@ if dbusmenu_gtk.found()
add_project_arguments('-DHAVE_DBUSMENU', language: 'cpp')
src_files += files(
'src/modules/sni/tray.cpp',
'src/modules/sni/snw.cpp',
'src/modules/sni/snh.cpp',
'src/modules/sni/sni.cpp'
'src/modules/sni/watcher.cpp',
'src/modules/sni/host.cpp',
'src/modules/sni/item.cpp'
)
endif
@ -98,6 +106,7 @@ executable(
wayland_cursor,
gtkmm,
dbusmenu_gtk,
giounix,
libnl,
libnlgen,
libpulse
@ -109,7 +118,7 @@ executable(
install_data(
'./resources/config',
'./resources/style.css',
install_dir: '/etc/xdg/waybar',
install_dir: join_paths(get_option('out'), 'etc/xdg/waybar')
)
clangtidy = find_program('clang-tidy', required: false)

View File

@ -1,3 +1,4 @@
option('libnl', type: 'feature', value: 'auto', description: 'Enable libnl support for network related features')
option('pulseaudio', type: 'feature', value: 'auto', description: 'Enable support for pulseaudio')
option('dbusmenu-gtk', type: 'feature', value: 'auto', description: 'Enable support for tray')
option('dbusmenu-gtk', type: 'feature', value: 'auto', description: 'Enable support for tray')
option('out', type: 'string', value : '/', description: 'output prefix directory')

View File

@ -1,78 +1,47 @@
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.kde.StatusNotifierItem">
<interface name='org.kde.StatusNotifierItem'>
<annotation name="org.gtk.GDBus.C.Name" value="Item" />
<property name="Category" type="s" access="read"/>
<property name="Id" type="s" access="read"/>
<property name="Title" type="s" access="read"/>
<property name="Status" type="s" access="read"/>
<property name="WindowId" type="i" access="read"/>
<property name="Menu" type="o" access="read" />
<!-- main icon -->
<!-- names are preferred over pixmaps -->
<property name="IconName" type="s" access="read" />
<property name="IconThemePath" type="s" access="read" />
<!-- struct containing width, height and image data-->
<!-- implementation has been dropped as of now -->
<property name="IconPixmap" type="a(iiay)" access="read" />
<!-- not used in ayatana code, no test case so far -->
<property name="OverlayIconName" type="s" access="read"/>
<property name="OverlayIconPixmap" type="a(iiay)" access="read" />
<!-- Requesting attention icon -->
<property name="AttentionIconName" type="s" access="read"/>
<!--same definition as image-->
<property name="AttentionIconPixmap" type="a(iiay)" access="read" />
<!-- tooltip data -->
<!-- unimplemented as of now -->
<!--(iiay) is an image-->
<property name="ToolTip" type="(sa(iiay)ss)" access="read" />
<!-- interaction: actually, we do not use them. -->
<method name="Activate">
<arg name="x" type="i" direction="in"/>
<arg name="y" type="i" direction="in"/>
<method name='ContextMenu'>
<arg type='i' direction='in' name='x'/>
<arg type='i' direction='in' name='y'/>
</method>
<method name="SecondaryActivate">
<arg name="x" type="i" direction="in"/>
<arg name="y" type="i" direction="in"/>
<method name='Activate'>
<arg type='i' direction='in' name='x'/>
<arg type='i' direction='in' name='y'/>
</method>
<method name="Scroll">
<arg name="delta" type="i" direction="in"/>
<arg name="dir" type="s" direction="in"/>
<method name='SecondaryActivate'>
<arg type='i' direction='in' name='x'/>
<arg type='i' direction='in' name='y'/>
</method>
<!-- Signals: the client wants to change something in the status-->
<signal name="NewTitle"></signal>
<signal name="NewIcon"></signal>
<signal name="NewIconThemePath">
<arg type="s" name="icon_theme_path" direction="out" />
<method name='Scroll'>
<arg type='i' direction='in' name='delta'/>
<arg type='s' direction='in' name='orientation'/>
</method>
<signal name='NewTitle'/>
<signal name='NewIcon'/>
<signal name='NewAttentionIcon'/>
<signal name='NewOverlayIcon'/>
<signal name='NewToolTip'/>
<signal name='NewStatus'>
<arg type='s' name='status'/>
</signal>
<signal name="NewAttentionIcon"></signal>
<signal name="NewOverlayIcon"></signal>
<signal name="NewToolTip"></signal>
<signal name="NewStatus">
<arg name="status" type="s" />
</signal>
<!-- ayatana labels -->
<!-- These are commented out because GDBusProxy would otherwise require them,
but they are not available for KDE indicators
-->
<!--<signal name="XAyatanaNewLabel">
<arg type="s" name="label" direction="out" />
<arg type="s" name="guide" direction="out" />
</signal>
<property name="XAyatanaLabel" type="s" access="read" />
<property name="XAyatanaLabelGuide" type="s" access="read" />-->
</interface>
<property name='Category' type='s' access='read'/>
<property name='Id' type='s' access='read'/>
<property name='Title' type='s' access='read'/>
<property name='Status' type='s' access='read'/>
<property name='WindowId' type='u' access='read'/>
<property name='IconThemePath' type='s' access='read'/>
<property name='IconName' type='s' access='read'/>
<property name='IconPixmap' type='a(iiay)' access='read'/>
<property name='OverlayIconName' type='s' access='read'/>
<property name='OverlayIconPixmap' type='a(iiay)' access='read'/>
<property name='AttentionIconName' type='s' access='read'/>
<property name='AttentionIconPixmap' type='a(iiay)' access='read'/>
<property name='AttentionMovieName' type='s' access='read'/>
<property name='ToolTip' type='(sa(iiay)ss)' access='read'/>
<property name='Menu' type='o' access='read'/>
<property name='ItemIsMenu' type='b' access='read'/>
</interface>
</node>

View File

@ -38,31 +38,63 @@ endforeach
gdbus_codegen = find_program('gdbus-codegen')
gdbus_code = generator(
gdbus_codegen,
output: '@BASENAME@.c',
arguments: ['--c-namespace', 'Sn', '--body', '--output', '@OUTPUT@', '@INPUT@']
)
r = run_command(gdbus_codegen, '--body', '--output', '/dev/null')
if r.returncode() != 0
gdbus_code_dsnw = custom_target(
'dbus-status-notifier-watcher.[ch]',
output: ['@BASENAME@.c','@BASENAME@.h'],
input: './dbus-status-notifier-watcher.xml',
command: [gdbus_codegen,'--c-namespace', 'Sn', '--generate-c-code', 'protocol/@BASENAME@', '@INPUT@'],
)
gdbus_header = generator(
gdbus_codegen,
output: '@BASENAME@.h',
arguments: ['--c-namespace', 'Sn', '--header', '--output', '@OUTPUT@', '@INPUT@']
)
gdbus_code_dsni = custom_target(
'dbus-status-notifier-item.[ch]',
output: ['@BASENAME@.c','@BASENAME@.h'],
input: './dbus-status-notifier-item.xml',
command: [gdbus_codegen,'--c-namespace', 'Sn', '--generate-c-code', 'protocol/@BASENAME@', '@INPUT@'],
)
client_protos_src += gdbus_code.process('./dbus-status-notifier-watcher.xml')
client_protos_headers += gdbus_header.process('./dbus-status-notifier-watcher.xml')
gdbus_code_dm = custom_target(
'dbus-menu.[ch]',
output: ['@BASENAME@.c','@BASENAME@.h'],
input: './dbus-menu.xml',
command: [gdbus_codegen,'--c-namespace', 'Sn', '--generate-c-code', 'protocol/@BASENAME@', '@INPUT@'],
)
client_protos_src += gdbus_code.process('./dbus-status-notifier-item.xml')
client_protos_headers += gdbus_header.process('./dbus-status-notifier-item.xml')
client_protos_src += gdbus_code_dsnw[0]
client_protos_headers += gdbus_code_dsnw[1]
client_protos_src += gdbus_code_dsni[0]
client_protos_headers += gdbus_code_dsni[1]
client_protos_src += gdbus_code_dm[0]
client_protos_headers += gdbus_code_dm[1]
else
gdbus_code = generator(
gdbus_codegen,
output: '@BASENAME@.c',
arguments: ['--c-namespace', 'Sn', '--body', '--output', '@OUTPUT@', '@INPUT@']
)
gdbus_header = generator(
gdbus_codegen,
output: '@BASENAME@.h',
arguments: ['--c-namespace', 'Sn', '--header', '--output', '@OUTPUT@', '@INPUT@']
)
client_protos_src += gdbus_code.process('./dbus-status-notifier-watcher.xml')
client_protos_headers += gdbus_header.process('./dbus-status-notifier-watcher.xml')
client_protos_src += gdbus_code.process('./dbus-status-notifier-item.xml')
client_protos_headers += gdbus_header.process('./dbus-status-notifier-item.xml')
client_protos_src += gdbus_code.process('./dbus-menu.xml')
client_protos_headers += gdbus_header.process('./dbus-menu.xml')
endif
client_protos_src += gdbus_code.process('./dbus-menu.xml')
client_protos_headers += gdbus_header.process('./dbus-menu.xml')
lib_client_protos = static_library(
'client_protos',
client_protos_src + client_protos_headers,
dependencies: [wayland_client, gtkmm],
dependencies: [wayland_client, gtkmm, giounix],
include_directories: include_directories('..'),
) # for the include directory

View File

@ -4,7 +4,7 @@
// "height": 30, // Waybar height
// "width": 1280, // Waybar width
// Choose the order of the modules
"modules-left": ["sway/workspaces", "custom/spotify"],
"modules-left": ["sway/workspaces", "sway/mode", "custom/spotify"],
"modules-center": ["sway/window"],
"modules-right": ["pulseaudio", "network", "cpu", "memory", "battery", "battery#bat2", "clock", "tray"],
// Modules configuration
@ -23,20 +23,31 @@
// "default": ""
// }
// },
"sway/window": {
"max-length": 50
"sway/mode": {
"format": "<span style=\"italic\">{}</span>"
},
"tray": {
// "icon-size": 21,
"spacing": 10
},
"clock": {
"format-alt": "{:%Y-%m-%d}"
},
"cpu": {
"format": "{}% "
"format": "{usage}% "
},
"memory": {
"format": "{}% "
},
"battery": {
"states": {
// "good": 95,
"warning": 30,
"critical": 15
},
"format": "{capacity}% {icon}",
// "format-good": "", // An empty format will hide the module
// "format-full": "",
"format-icons": ["", "", "", "", ""]
},
"battery#bat2": {
@ -45,10 +56,11 @@
"network": {
// "interface": "wlp2s0", // (Optional) To force the use of this interface
"format-wifi": "{essid} ({signalStrength}%) ",
"format-ethernet": "{ifname} ",
"format-ethernet": "{ifname}: {ipaddr}/{cidr} ",
"format-disconnected": "Disconnected ⚠"
},
"pulseaudio": {
//"scroll-step": 1,
"format": "{volume}% {icon}",
"format-bluetooth": "{volume}% {icon}",
"format-muted": "",
@ -60,13 +72,12 @@
"portable": "",
"car": "",
"default": ["", ""]
}
},
"on-click": "pavucontrol"
},
"custom/spotify": {
"format": " {}",
"max-length": 40,
"interval": 30, // Remove this if your script is endless and write in loop
"exec": "$HOME/.config/waybar/mediaplayer.sh", // Script in resources folder
"exec-if": "pgrep spotify"
"exec": "$HOME/.config/waybar/mediaplayer.py 2> /dev/null" // Script in resources folder
}
}

View File

@ -0,0 +1,71 @@
#!/usr/bin/env python3
import sys
import signal
import gi
gi.require_version('Playerctl', '2.0')
from gi.repository import Playerctl, GLib
manager = Playerctl.PlayerManager()
loop = GLib.MainLoop()
def on_play(player, status, manager):
on_metadata(player, player.props.metadata, manager)
def on_metadata(player, metadata, manager):
track_info = ''
if player.props.player_name == 'spotify' and \
'mpris:trackid' in metadata.keys() and \
':ad:' in player.props.metadata['mpris:trackid']:
track_info = 'AD PLAYING'
elif player.get_artist() != '' and player.get_title() != '':
track_info = '{artist} - {title}'.format(artist=player.get_artist(),
title=player.get_title())
else:
sys.stdout.write('\n')
sys.stdout.flush()
return
if player.props.status == 'Playing':
sys.stdout.write(track_info + '\n')
else:
sys.stdout.write('' + track_info + '\n')
sys.stdout.flush()
def on_name_appeared(manager, name):
init_player(name)
def on_player_vanished(manager, player):
sys.stdout.write("\n")
sys.stdout.flush()
def init_player(name):
player = Playerctl.Player.new_from_name(name)
player.connect('playback-status', on_play, manager)
player.connect('metadata', on_metadata, manager)
manager.manage_player(player)
on_metadata(player, player.props.metadata, manager)
def signal_handler(sig, frame):
sys.stdout.write("\n")
sys.stdout.flush()
loop.quit()
sys.exit(0)
manager.connect('name-appeared', on_name_appeared)
manager.connect('player-vanished', on_player_vanished)
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
for player in manager.props.player_names:
init_player(player)
loop.run()

View File

@ -1,7 +0,0 @@
#!/bin/sh
player_status=$(playerctl status 2> /dev/null)
if [ "$player_status" = "Playing" ]; then
echo "$(playerctl metadata artist) - $(playerctl metadata title)"
elif [ "$player_status" = "Paused" ]; then
echo "$(playerctl metadata artist) - $(playerctl metadata title)"
fi

View File

@ -3,6 +3,7 @@
border-radius: 0;
font-family: Roboto, Helvetica, Arial, sans-serif;
font-size: 13px;
min-height: 0;
}
window#waybar {
@ -23,7 +24,12 @@ window#waybar {
border-bottom: 3px solid white;
}
#clock, #battery, #cpu, #memory, #network, #pulseaudio, #custom-spotify, #tray {
#mode {
background: #64727D;
border-bottom: 3px solid white;
}
#clock, #battery, #cpu, #memory, #network, #pulseaudio, #custom-spotify, #tray, #mode {
padding: 0 10px;
margin: 0 5px;
}
@ -49,7 +55,7 @@ window#waybar {
}
}
#battery.warning {
#battery.warning:not(.charging) {
background: #f53c3c;
color: white;
animation-name: blink;

View File

@ -1,11 +1,13 @@
#include "ALabel.hpp"
#include <util/command.hpp>
#include <iostream>
waybar::ALabel::ALabel(const Json::Value& config, const std::string format)
waybar::ALabel::ALabel(const Json::Value& config, const std::string format, uint16_t interval)
: config_(config),
format_(config_["format"].isString() ? config_["format"].asString() : format),
default_format_(format_)
interval_(std::chrono::seconds(config_["interval"].isUInt()
? config_["interval"].asUInt() : interval)), default_format_(format_)
{
event_box_.add(label_);
if (config_["max-length"].isUInt()) {
@ -14,30 +16,78 @@ waybar::ALabel::ALabel(const Json::Value& config, const std::string format)
}
if (config_["format-alt"].isString()) {
event_box_.add_events(Gdk::BUTTON_PRESS_MASK);
event_box_.signal_button_press_event()
.connect(sigc::mem_fun(*this, &ALabel::handleToggle));
event_box_.signal_button_press_event().connect(
sigc::mem_fun(*this, &ALabel::handleToggle));
}
// configure events' user commands
if (config_["on-click"].isString() || config_["on-click-right"].isString()) {
event_box_.add_events(Gdk::BUTTON_PRESS_MASK);
event_box_.signal_button_press_event().connect(
sigc::mem_fun(*this, &ALabel::handleToggle));
}
if (config_["on-scroll-up"].isString() || config_["on-scroll-down"].isString()) {
event_box_.add_events(Gdk::SCROLL_MASK);
event_box_.signal_scroll_event().connect(
sigc::mem_fun(*this, &ALabel::handleScroll));
}
}
auto waybar::ALabel::update() -> void
{
auto waybar::ALabel::update() -> void {
// Nothing here
}
bool waybar::ALabel::handleToggle(GdkEventButton* const& /*ev*/)
{
alt = !alt;
if (alt) {
format_ = config_["format-alt"].asString();
bool waybar::ALabel::handleToggle(GdkEventButton* const& e) {
if (config_["on-click"].isString() && e->button == 1) {
waybar::util::command::forkExec(config_["on-click"].asString());
} else if (config_["on-click-right"].isString() && e->button == 3) {
waybar::util::command::forkExec(config_["on-click-right"].asString());
} else {
format_ = default_format_;
alt = !alt;
if (alt) {
format_ = config_["format-alt"].asString();
} else {
format_ = default_format_;
}
}
dp.emit();
return true;
}
bool waybar::ALabel::handleScroll(GdkEventScroll* e) {
// Avoid concurrent scroll event
std::lock_guard<std::mutex> lock(mutex_);
bool direction_up = false;
if (e->direction == GDK_SCROLL_UP) {
direction_up = true;
}
if (e->direction == GDK_SCROLL_DOWN) {
direction_up = false;
}
if (e->direction == GDK_SCROLL_SMOOTH) {
gdouble delta_x, delta_y;
gdk_event_get_scroll_deltas(reinterpret_cast<const GdkEvent*>(e),
&delta_x, &delta_y);
if (delta_y < 0) {
direction_up = true;
} else if (delta_y > 0) {
direction_up = false;
}
}
if (direction_up && config_["on-scroll-up"].isString()) {
waybar::util::command::forkExec(config_["on-scroll-up"].asString());
} else if (config_["on-scroll-down"].isString()) {
waybar::util::command::forkExec(config_["on-scroll-down"].asString());
}
dp.emit();
return true;
}
std::string waybar::ALabel::getIcon(uint16_t percentage, const std::string& alt)
{
std::string waybar::ALabel::getIcon(uint16_t percentage,
const std::string& alt) {
auto format_icons = config_["format-icons"];
if (format_icons.isObject()) {
if (!alt.empty() && format_icons[alt].isString()) {
@ -57,6 +107,4 @@ std::string waybar::ALabel::getIcon(uint16_t percentage, const std::string& alt)
return "";
}
waybar::ALabel::operator Gtk::Widget &() {
return event_box_;
}
waybar::ALabel::operator Gtk::Widget&() { return event_box_; }

View File

@ -20,16 +20,15 @@ waybar::Bar::Bar(const Client& client,
zxdg_output_manager_v1_get_xdg_output(client.xdg_output_manager, *output);
zxdg_output_v1_add_listener(xdg_output_, &xdgOutputListener, this);
window.set_title("waybar");
window.set_decorated(false);
window.set_name("waybar");
window.set_decorated(false);
window.set_resizable(false);
setupConfig();
setupCss();
setupWidgets();
Gtk::Widget& wrap(window);
gtk_widget_realize(wrap.gobj());
GdkWindow *gdk_window = gtk_widget_get_window(wrap.gobj());
auto wrap = reinterpret_cast<GtkWidget*>(window.gobj());
gtk_widget_realize(wrap);
GdkWindow *gdk_window = gtk_widget_get_window(wrap);
gdk_wayland_window_set_use_custom_surface(gdk_window);
surface = gdk_wayland_window_get_wl_surface(gdk_window);
@ -60,6 +59,8 @@ waybar::Bar::Bar(const Client& client,
zwlr_layer_surface_v1_set_size(layer_surface, width, height);
wl_surface_commit(surface);
setupWidgets();
}
void waybar::Bar::handleLogicalPosition(void* /*data*/,
@ -104,13 +105,20 @@ void waybar::Bar::layerSurfaceHandleConfigure(void* data,
if (width != o->width_ || height != o->height_) {
o->width_ = width;
o->height_ = height;
o->window.set_size_request(o->width_, o->height_);
o->window.resize(o->width_, o->height_);
int dummy_width, min_height;
o->window.get_size(dummy_width, min_height);
if (o->height_ < static_cast<uint32_t>(min_height)) {
std::cout << fmt::format("Requested height: {} exceeds the minimum \
height: {} required by the modules", o->height_, min_height) << std::endl;
o->height_ = min_height;
}
std::cout << fmt::format(
"Bar configured (width: {}, height: {}) for output: {}",
o->width_, o->height_, o->output_name) << std::endl;
o->window.set_size_request(o->width_, o->height_);
o->window.resize(o->width_, o->height_);
zwlr_layer_surface_v1_set_exclusive_zone(surface, o->height_);
zwlr_layer_surface_v1_set_size(surface, o->width_, o->height_);
wl_surface_commit(o->surface);
}
}
@ -176,7 +184,13 @@ void waybar::Bar::getModules(const Factory& factory, const std::string& pos)
if (pos == "modules-right") {
modules_right_.emplace_back(module);
}
module->dp.connect([module] { module->update(); });
module->dp.connect([module, &name] {
try {
module->update();
} catch (const std::exception& e) {
std::cerr << name.asString() + ": " + e.what() << std::endl;
}
});
} catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
}

View File

@ -1,7 +1,9 @@
#include "client.hpp"
#include "util/clara.hpp"
#include <iostream>
waybar::Client::Client(int argc, char* argv[])
: gtk_app(Gtk::Application::create(argc, argv, "fr.arouillard.waybar")),
: gtk_main(argc, argv),
gdk_display(Gdk::Display::get_default())
{
if (!gdk_display) {
@ -11,36 +13,24 @@ waybar::Client::Client(int argc, char* argv[])
throw std::runtime_error("Bar need to run under Wayland");
}
wl_display = gdk_wayland_display_get_wl_display(gdk_display->gobj());
auto getFirstValidPath = [] (std::vector<std::string> possiblePaths) {
wordexp_t p;
}
for (const std::string &path: possiblePaths) {
if (wordexp(path.c_str(), &p, 0) == 0) {
if (access(*p.we_wordv, F_OK) == 0) {
std::string result = *p.we_wordv;
wordfree(&p);
return result;
}
const std::string waybar::Client::getValidPath(std::vector<std::string> paths)
{
wordexp_t p;
for (const std::string &path: paths) {
if (wordexp(path.c_str(), &p, 0) == 0) {
if (access(*p.we_wordv, F_OK) == 0) {
std::string result = *p.we_wordv;
wordfree(&p);
return result;
}
wordfree(&p);
}
}
return std::string();
};
config_file = getFirstValidPath({
"$XDG_CONFIG_HOME/waybar/config",
"$HOME/waybar/config",
"/etc/xdg/waybar/config",
"./resources/config",
});
css_file = getFirstValidPath({
"$XDG_CONFIG_HOME/waybar/style.css",
"$HOME/waybar/style.css",
"/etc/xdg/waybar/style.css",
"./resources/style.css",
});
return std::string();
}
void waybar::Client::handleGlobal(void *data, struct wl_registry *registry,
@ -80,6 +70,28 @@ void waybar::Client::handleGlobalRemove(void* data,
}
}
void waybar::Client::setupConfigs(const std::string& config, const std::string& style)
{
config_file = config.empty() ? getValidPath({
"$XDG_CONFIG_HOME/waybar/config",
"$HOME/.config/waybar/config",
"$HOME/waybar/config",
"/etc/xdg/waybar/config",
"./resources/config",
}) : config;
css_file = style.empty() ? getValidPath({
"$XDG_CONFIG_HOME/waybar/style.css",
"$HOME/.config/waybar/style.css",
"$HOME/waybar/style.css",
"/etc/xdg/waybar/style.css",
"./resources/style.css",
}) : style;
if (css_file.empty() || config_file.empty()) {
throw std::runtime_error("Missing required resources files");
}
std::cout << "Resources files: " + config_file + ", " + css_file << std::endl;
}
void waybar::Client::bindInterfaces()
{
registry = wl_display_get_registry(wl_display);
@ -89,13 +101,38 @@ void waybar::Client::bindInterfaces()
};
wl_registry_add_listener(registry, &registry_listener, this);
wl_display_roundtrip(wl_display);
if (!layer_shell || !seat || !xdg_output_manager) {
throw std::runtime_error("Failed to acquire required resources.");
}
wl_display_roundtrip(wl_display);
}
int waybar::Client::main(int /*argc*/, char* /*argv*/[])
int waybar::Client::main(int argc, char* argv[])
{
bool show_help = false;
bool show_version = false;
std::string config;
std::string style;
auto cli = clara::detail::Help(show_help)
| clara::detail::Opt(show_version)["-v"]["--version"]("Show version")
| clara::detail::Opt(config, "config")["-c"]["--config"]("Config path")
| clara::detail::Opt(style, "style")["-s"]["--style"]("Style path");
auto res = cli.parse(clara::detail::Args(argc, argv));
if (!res) {
std::cerr << "Error in command line: " << res.errorMessage() << std::endl;
return 1;
}
if (show_help) {
std::cout << cli << std::endl;
return 0;
}
if (show_version) {
std::cout << "Waybar v" << VERSION << std::endl;
return 0;
}
setupConfigs(config, style);
bindInterfaces();
gtk_app->hold();
gtk_app->run();
gtk_main.run();
bars.clear();
zxdg_output_manager_v1_destroy(xdg_output_manager);
zwlr_layer_shell_v1_destroy(layer_shell);

View File

@ -1,46 +1,51 @@
#include "factory.hpp"
waybar::Factory::Factory(Bar& bar, const Json::Value& config)
waybar::Factory::Factory(const Bar& bar, const Json::Value& config)
: bar_(bar), config_(config)
{}
waybar::IModule* waybar::Factory::makeModule(const std::string &name) const
{
try {
auto ref = name.substr(0, name.find("#"));
auto hash_pos = name.find("#");
auto ref = name.substr(0, hash_pos);
auto id = hash_pos != std::string::npos ? name.substr(hash_pos + 1) : "";
if (ref == "battery") {
return new waybar::modules::Battery(config_[name]);
return new waybar::modules::Battery(id, config_[name]);
}
#ifdef HAVE_SWAY
if (ref == "sway/mode") {
return new waybar::modules::sway::Mode(id, bar_, config_[name]);
}
if (ref == "sway/workspaces") {
return new waybar::modules::sway::Workspaces(bar_, config_[name]);
return new waybar::modules::sway::Workspaces(id, bar_, config_[name]);
}
if (ref == "sway/window") {
return new waybar::modules::sway::Window(bar_, config_[name]);
return new waybar::modules::sway::Window(id, bar_, config_[name]);
}
#endif
if (ref == "memory") {
return new waybar::modules::Memory(config_[name]);
return new waybar::modules::Memory(id, config_[name]);
}
if (ref == "cpu") {
return new waybar::modules::Cpu(config_[name]);
return new waybar::modules::Cpu(id, config_[name]);
}
if (ref == "clock") {
return new waybar::modules::Clock(config_[name]);
return new waybar::modules::Clock(id, config_[name]);
}
#ifdef HAVE_DBUSMENU
if (ref == "tray") {
return new waybar::modules::SNI::Tray(config_[name]);
return new waybar::modules::SNI::Tray(id, config_[name]);
}
#endif
#ifdef HAVE_LIBNL
if (ref == "network") {
return new waybar::modules::Network(config_[name]);
return new waybar::modules::Network(id, config_[name]);
}
#endif
#ifdef HAVE_LIBPULSE
if (ref == "pulseaudio") {
return new waybar::modules::Pulseaudio(config_[name]);
return new waybar::modules::Pulseaudio(id, config_[name]);
}
#endif
if (ref.compare(0, 7, "custom/") == 0 && ref.size() > 7) {

View File

@ -15,7 +15,7 @@ int main(int argc, char* argv[])
waybar::client = &c;
std::signal(SIGUSR1, [] (int /*signal*/) {
for (auto& bar : waybar::client->bars) {
(*bar).toggle();
bar->toggle();
}
});

View File

@ -1,8 +1,12 @@
#include "modules/battery.hpp"
waybar::modules::Battery::Battery(const Json::Value& config)
: ALabel(config, "{capacity}%")
waybar::modules::Battery::Battery(const std::string& id, const Json::Value& config)
: ALabel(config, "{capacity}%", 60)
{
label_.set_name("battery");
if (!id.empty()) {
label_.get_style_context()->add_class(id);
}
try {
if (config_["bat"].isString()) {
auto dir = data_dir_ / config_["bat"].asString();
@ -34,7 +38,6 @@ waybar::modules::Battery::Battery(const Json::Value& config)
for (auto const& bat : batteries_) {
inotify_add_watch(fd_, (bat / "uevent").c_str(), IN_ACCESS);
}
label_.set_name("battery");
worker();
}
@ -47,9 +50,8 @@ void waybar::modules::Battery::worker()
{
// Trigger first values
update();
uint32_t interval = config_["interval"].isUInt() ? config_["interval"].asUInt() : 60;
threadTimer_ = [this, interval] {
thread_.sleep_for(chrono::seconds(interval));
thread_timer_ = [this] {
thread_.sleep_for(interval_);
dp.emit();
};
thread_ = [this] {
@ -58,16 +60,17 @@ void waybar::modules::Battery::worker()
if (nbytes != sizeof(event)) {
return;
}
threadTimer_.stop();
// TODO: don't stop timer for now since there is some bugs :?
// thread_timer_.stop();
dp.emit();
};
}
auto waybar::modules::Battery::update() -> void
const std::tuple<uint8_t, std::string> waybar::modules::Battery::getInfos() const
{
try {
uint16_t total = 0;
std::string status;
std::string status = "Unknown";
for (auto const& bat : batteries_) {
uint16_t capacity;
std::string _status;
@ -79,22 +82,62 @@ auto waybar::modules::Battery::update() -> void
total += capacity;
}
uint16_t capacity = total / batteries_.size();
label_.set_text(fmt::format(format_, fmt::arg("capacity", capacity),
fmt::arg("icon", getIcon(capacity))));
label_.set_tooltip_text(status);
bool charging = status == "Charging";
if (charging) {
label_.get_style_context()->add_class("charging");
} else {
label_.get_style_context()->remove_class("charging");
}
auto critical = config_["critical"].isUInt() ? config_["critical"].asUInt() : 15;
if (capacity <= critical && !charging) {
label_.get_style_context()->add_class("warning");
} else {
label_.get_style_context()->remove_class("warning");
}
return {capacity, status};
} catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
return {0, "Unknown"};
}
}
const std::string waybar::modules::Battery::getState(uint8_t capacity) const
{
// Get current state
std::vector<std::pair<std::string, uint8_t>> states;
if (config_["states"].isObject()) {
for (auto it = config_["states"].begin(); it != config_["states"].end(); ++it) {
if (it->isUInt() && it.key().isString()) {
states.push_back({it.key().asString(), it->asUInt()});
}
}
}
// Sort states
std::sort(states.begin(), states.end(), [](auto &a, auto &b) {
return a.second < b.second;
});
std::string valid_state;
for (auto const& state : states) {
if (capacity <= state.second && valid_state.empty()) {
label_.get_style_context()->add_class(state.first);
valid_state = state.first;
} else {
label_.get_style_context()->remove_class(state.first);
}
}
return valid_state;
}
auto waybar::modules::Battery::update() -> void
{
auto [capacity, status] = getInfos();
label_.set_tooltip_text(status);
std::transform(status.begin(), status.end(), status.begin(), ::tolower);
auto format = format_;
auto state = getState(capacity);
label_.get_style_context()->remove_class(old_status_);
label_.get_style_context()->add_class(status);
old_status_ = status;
if (!state.empty() && config_["format-" + status + "-" + state].isString()) {
format = config_["format-" + status + "-" + state].asString();
} else if (config_["format-" + status].isString()) {
format = config_["format-" + status].asString();
} else if (!state.empty() && config_["format-" + state].isString()) {
format = config_["format-" + state].asString();
}
if (format.empty()) {
event_box_.hide();
} else {
event_box_.show();
label_.set_markup(fmt::format(format, fmt::arg("capacity", capacity),
fmt::arg("icon", getIcon(capacity))));
}
}

View File

@ -1,21 +1,25 @@
#include "modules/clock.hpp"
waybar::modules::Clock::Clock(const Json::Value& config)
: ALabel(config, "{:%H:%M}")
waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
: ALabel(config, "{:%H:%M}", 60)
{
label_.set_name("clock");
uint32_t interval = config_["interval"].isUInt() ? config_["interval"].asUInt() : 60;
thread_ = [this, interval] {
if (!id.empty()) {
label_.get_style_context()->add_class(id);
}
thread_ = [this] {
auto now = waybar::chrono::clock::now();
dp.emit();
auto timeout = std::chrono::floor<std::chrono::seconds>(now
+ std::chrono::seconds(interval));
thread_.sleep_until(timeout);
auto timeout = std::chrono::floor<std::chrono::seconds>(now + interval_);
auto time_s = std::chrono::time_point_cast<std::chrono::seconds>(timeout);
auto sub_m =
std::chrono::duration_cast<std::chrono::seconds>(time_s.time_since_epoch()).count() % interval_.count();
thread_.sleep_until(timeout - std::chrono::seconds(sub_m - 1));
};
}
auto waybar::modules::Clock::update() -> void
{
auto localtime = fmt::localtime(std::time(nullptr));
label_.set_text(fmt::format(format_, localtime));
label_.set_markup(fmt::format(format_, localtime));
}

View File

@ -1,22 +1,88 @@
#include "modules/cpu.hpp"
waybar::modules::Cpu::Cpu(const Json::Value& config)
: ALabel(config, "{}%")
waybar::modules::Cpu::Cpu(const std::string& id, const Json::Value& config)
: ALabel(config, "{usage}%", 10)
{
label_.set_name("cpu");
uint32_t interval = config_["interval"].isUInt() ? config_["interval"].asUInt() : 10;
thread_ = [this, interval] {
if (!id.empty()) {
label_.get_style_context()->add_class(id);
}
thread_ = [this] {
dp.emit();
thread_.sleep_for(chrono::seconds(interval));
thread_.sleep_for(interval_);
};
}
auto waybar::modules::Cpu::update() -> void
{
struct sysinfo info = {};
// TODO: as creating dynamic fmt::arg arrays is buggy we have to calc both
auto cpu_load = getCpuLoad();
auto [cpu_usage, tooltip] = getCpuUsage();
label_.set_tooltip_text(tooltip);
label_.set_markup(fmt::format(format_,
fmt::arg("load", cpu_load), fmt::arg("usage", cpu_usage)));
}
uint16_t waybar::modules::Cpu::getCpuLoad()
{
struct sysinfo info = {0};
if (sysinfo(&info) == 0) {
float f_load = 1.f / (1u << SI_LOAD_SHIFT);
uint16_t load = info.loads[0] * f_load * 100 / get_nprocs();
label_.set_text(fmt::format(format_, load));
return load;
}
throw std::runtime_error("Can't get Cpu load");
}
std::tuple<uint16_t, std::string> waybar::modules::Cpu::getCpuUsage()
{
if (prev_times_.empty()) {
prev_times_ = parseCpuinfo();
std::this_thread::sleep_for(chrono::milliseconds(100));
}
std::vector<std::tuple<size_t, size_t>> curr_times = parseCpuinfo();
std::string tooltip;
uint16_t usage = 0;
for (size_t i = 0; i < curr_times.size(); ++i) {
auto [curr_idle, curr_total] = curr_times[i];
auto [prev_idle, prev_total] = prev_times_[i];
const float delta_idle = curr_idle - prev_idle;
const float delta_total = curr_total - prev_total;
uint16_t tmp = 100 * (1 - delta_idle / delta_total);
if (i == 0) {
usage = tmp;
tooltip = fmt::format("Total: {}%", tmp);
} else {
tooltip = tooltip + fmt::format("\nCore{}: {}%", i - 1, tmp);
}
}
prev_times_ = curr_times;
return {usage, tooltip};
}
std::vector<std::tuple<size_t, size_t>> waybar::modules::Cpu::parseCpuinfo()
{
std::ifstream info(data_dir_);
if (!info.is_open()) {
throw std::runtime_error("Can't open " + data_dir_);
}
std::vector< std::tuple<size_t, size_t> > cpuinfo;
std::string line;
while (getline(info, line)) {
if (line.substr(0,3).compare("cpu") != 0) {
break;
}
std::stringstream sline(line.substr(5));
std::vector<size_t> times;
for (size_t time; sline >> time; times.push_back(time));
size_t idle_time = 0;
size_t total_time = 0;
if (times.size() >= 4) {
idle_time = times[3];
total_time = std::accumulate(times.begin(), times.end(), 0);
}
cpuinfo.push_back({idle_time, total_time});
}
return cpuinfo;
}

View File

@ -1,54 +1,65 @@
#include "modules/custom.hpp"
waybar::modules::Custom::Custom(const std::string name,
waybar::modules::Custom::Custom(const std::string& name,
const Json::Value& config)
: ALabel(config, "{}"), name_(name)
: ALabel(config, "{}"), name_(name), fp_(nullptr)
{
if (!config_["exec"].isString()) {
throw std::runtime_error(name_ + " has no exec path.");
label_.set_name("custom-" + name_);
if (config_["exec"].isString()) {
if (interval_.count() > 0) {
delayWorker();
} else {
continuousWorker();
}
}
if (config_["interval"].isUInt()) {
delayWorker();
} else {
continuousWorker();
dp.emit();
}
waybar::modules::Custom::~Custom()
{
if (fp_) {
pclose(fp_);
fp_ = nullptr;
}
}
void waybar::modules::Custom::delayWorker()
{
auto interval = config_["interval"].asUInt();
thread_ = [this, interval] {
thread_ = [this] {
bool can_update = true;
if (config_["exec-if"].isString()) {
auto res = waybar::util::command::exec(config_["exec-if"].asString());
if (res.exit_code != 0) {
can_update = false;
label_.hide();
label_.set_name("");
event_box_.hide();
}
}
if (can_update) {
output_ = waybar::util::command::exec(config_["exec"].asString());
dp.emit();
}
thread_.sleep_for(chrono::seconds(interval));
thread_.sleep_for(interval_);
};
}
void waybar::modules::Custom::continuousWorker()
{
auto cmd = config_["exec"].asString();
FILE* fp(popen(cmd.c_str(), "r"));
if (!fp) {
fp_ = popen(cmd.c_str(), "r");
if (!fp_) {
throw std::runtime_error("Unable to open " + cmd);
}
thread_ = [this, fp] {
thread_ = [this] {
char* buff = nullptr;
size_t len = 0;
if (getline(&buff, &len, fp) == -1) {
pclose(fp);
if (getline(&buff, &len, fp_) == -1) {
if (fp_) {
pclose(fp_);
fp_ = nullptr;
}
thread_.stop();
output_ = { 1, "" };
std::cerr << name_ + " just stopped, is it endless?" << std::endl;
dp.emit();
return;
}
@ -67,14 +78,67 @@ void waybar::modules::Custom::continuousWorker()
auto waybar::modules::Custom::update() -> void
{
// Hide label if output is empty
if (output_.out.empty() || output_.exit_code != 0) {
label_.hide();
label_.set_name("");
if (config_["exec"].isString() && (output_.out.empty() || output_.exit_code != 0)) {
event_box_.hide();
} else {
label_.set_name("custom-" + name_);
auto str = fmt::format(format_, output_.out);
label_.set_text(str);
label_.set_tooltip_text(str);
label_.show();
if (config_["return-type"].asString() == "json") {
parseOutputJson();
} else {
parseOutputRaw();
}
auto str = fmt::format(format_, text_);
label_.set_markup(str);
if (text_ == tooltip_) {
label_.set_tooltip_text(str);
} else {
label_.set_tooltip_text(tooltip_);
}
if (class_ != "") {
if (prevclass_ != "") {
label_.get_style_context()->remove_class(prevclass_);
}
label_.get_style_context()->add_class(class_);
prevclass_ = class_;
} else {
label_.get_style_context()->remove_class(prevclass_);
prevclass_ = "";
}
event_box_.show();
}
}
}
void waybar::modules::Custom::parseOutputRaw()
{
std::istringstream output(output_.out);
std::string line;
int i = 0;
while (getline(output, line)) {
if (i == 0) {
text_ = line;
tooltip_ = line;
class_ = "";
} else if (i == 1) {
tooltip_ = line;
} else if (i == 2) {
class_ = line;
} else {
break;
}
i++;
}
}
void waybar::modules::Custom::parseOutputJson()
{
std::istringstream output(output_.out);
std::string line;
while (getline(output, line)) {
auto parsed = parser_.parse(line);
text_ = parsed["text"].asString();
tooltip_ = parsed["tooltip"].asString();
class_ = parsed["class"].asString();
break;
}
}

View File

@ -1,25 +1,63 @@
#include "modules/memory.hpp"
waybar::modules::Memory::Memory(const Json::Value& config)
: ALabel(config, "{}%")
waybar::modules::Memory::Memory(const std::string& id, const Json::Value& config)
: ALabel(config, "{}%", 30)
{
label_.set_name("memory");
uint32_t interval = config_["interval"].isUInt() ? config_["interval"].asUInt() : 30;
thread_ = [this, interval] {
if (!id.empty()) {
label_.get_style_context()->add_class(id);
}
thread_ = [this] {
dp.emit();
thread_.sleep_for(chrono::seconds(interval));
thread_.sleep_for(interval_);
};
}
auto waybar::modules::Memory::update() -> void
{
struct sysinfo info = {};
if (sysinfo(&info) == 0) {
auto total = info.totalram * info.mem_unit;
auto freeram = info.freeram * info.mem_unit;
int used_ram_percentage = 100 * (total - freeram) / total;
label_.set_text(fmt::format(format_, used_ram_percentage));
auto used_ram_gigabytes = (total - freeram) / std::pow(1024, 3);
parseMeminfo();
if (memtotal_ > 0 && memfree_ >= 0) {
int used_ram_percentage = 100 * (memtotal_ - memfree_) / memtotal_;
label_.set_markup(fmt::format(format_, used_ram_percentage));
auto used_ram_gigabytes = (memtotal_ - memfree_) / std::pow(1024, 2);
label_.set_tooltip_text(fmt::format("{:.{}f}Gb used", used_ram_gigabytes, 1));
event_box_.show();
} else {
event_box_.hide();
}
}
void waybar::modules::Memory::parseMeminfo()
{
long memfree = -1, membuffer = -1, memcache = -1, memavail = -1;
std::ifstream info(data_dir_);
if (!info.is_open()) {
throw std::runtime_error("Can't open " + data_dir_);
}
std::string line;
while (getline(info, line)) {
auto posDelim = line.find(":");
if (posDelim == std::string::npos) {
continue;
}
std::string name = line.substr(0, posDelim);
long value = std::stol(line.substr(posDelim + 1));
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

@ -1,9 +1,13 @@
#include "modules/network.hpp"
waybar::modules::Network::Network(const Json::Value& config)
: ALabel(config, "{ifname}"), family_(AF_INET),
signal_strength_dbm_(0), signal_strength_(0)
waybar::modules::Network::Network(const std::string& id, const Json::Value& config)
: ALabel(config, "{ifname}", 60), family_(AF_INET),
cidr_(-1), signal_strength_dbm_(0), signal_strength_(0)
{
label_.set_name("network");
if (!id.empty()) {
label_.get_style_context()->add_class(id);
}
sock_fd_ = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (sock_fd_ < 0) {
throw std::runtime_error("Can't open network socket");
@ -29,14 +33,26 @@ waybar::modules::Network::Network(const Json::Value& config)
}
}
initNL80211();
label_.set_name("network");
// Trigger first values
getInfo();
update();
dp.emit();
worker();
}
waybar::modules::Network::~Network()
{
close(sock_fd_);
nl_socket_free(sk_);
}
void waybar::modules::Network::worker()
{
thread_ = [this] {
char buf[4096];
uint64_t len = netlinkResponse(sock_fd_, buf, sizeof(buf),
RTMGRP_LINK | RTMGRP_IPV4_IFADDR);
auto len = netlinkResponse(sock_fd_, buf, sizeof(buf), RTMGRP_LINK | RTMGRP_IPV4_IFADDR);
if (len == 0) {
return;
}
bool need_update = false;
for (auto nh = reinterpret_cast<struct nlmsghdr *>(buf); NLMSG_OK(nh, len);
nh = NLMSG_NEXT(nh, len)) {
@ -46,6 +62,9 @@ waybar::modules::Network::Network(const Json::Value& config)
if (nh->nlmsg_type == NLMSG_ERROR) {
continue;
}
if (nh->nlmsg_type == RTM_NEWADDR) {
need_update = true;
}
if (nh->nlmsg_type < RTM_NEWADDR) {
auto rtif = static_cast<struct ifinfomsg *>(NLMSG_DATA(nh));
if (rtif->ifi_index == static_cast<int>(ifid_)) {
@ -68,22 +87,25 @@ waybar::modules::Network::Network(const Json::Value& config)
}
}
if (need_update) {
if (ifid_ > 0) {
getInfo();
}
dp.emit();
}
};
thread_timer_ = [this] {
thread_.sleep_for(interval_);
if (ifid_ > 0) {
getInfo();
dp.emit();
}
};
}
waybar::modules::Network::~Network()
{
close(sock_fd_);
nl_socket_free(sk_);
}
auto waybar::modules::Network::update() -> void
{
auto format = format_;
if (ifid_ <= 0) {
if (ifid_ <= 0 || ipaddr_.empty()) {
format = config_["format-disconnected"].isString()
? config_["format-disconnected"].asString() : format;
label_.get_style_context()->add_class("disconnected");
@ -97,11 +119,14 @@ auto waybar::modules::Network::update() -> void
}
label_.get_style_context()->remove_class("disconnected");
}
label_.set_text(fmt::format(format,
label_.set_markup(fmt::format(format,
fmt::arg("essid", essid_),
fmt::arg("signaldBm", signal_strength_dbm_),
fmt::arg("signalStrength", signal_strength_),
fmt::arg("ifname", ifname_)
fmt::arg("ifname", ifname_),
fmt::arg("netmask", netmask_),
fmt::arg("ipaddr", ipaddr_),
fmt::arg("cidr", cidr_)
));
}
@ -110,8 +135,15 @@ void waybar::modules::Network::disconnected()
essid_.clear();
signal_strength_dbm_ = 0;
signal_strength_ = 0;
ifname_.clear();
ifid_ = -1;
ipaddr_.clear();
netmask_.clear();
cidr_ = 0;
if (!config_["interface"].isString()) {
ifname_.clear();
ifid_ = -1;
}
// Need to wait otherwise we'll have the same information
thread_.sleep_for(std::chrono::seconds(1));
}
void waybar::modules::Network::initNL80211()
@ -255,6 +287,36 @@ out:
return ifidx;
}
void waybar::modules::Network::getInterfaceAddress() {
unsigned int cidrRaw;
struct ifaddrs *ifaddr, *ifa;
int success = getifaddrs(&ifaddr);
if (success == 0) {
ifa = ifaddr;
while (ifa != nullptr && ipaddr_.empty() && netmask_.empty()) {
if (ifa->ifa_addr != nullptr && ifa->ifa_addr->sa_family == family_) {
if (strcmp(ifa->ifa_name, ifname_.c_str()) == 0) {
ipaddr_ = inet_ntoa(((struct sockaddr_in*)ifa->ifa_addr)->sin_addr);
netmask_ = inet_ntoa(((struct sockaddr_in*)ifa->ifa_netmask)->sin_addr);
cidrRaw = ((struct sockaddr_in *)(ifa->ifa_netmask))->sin_addr.s_addr;
unsigned int cidr = 0;
while (cidrRaw) {
cidr += cidrRaw & 1;
cidrRaw >>= 1;
}
cidr_ = cidr;
}
}
ifa = ifa->ifa_next;
}
freeifaddrs(ifaddr);
} else {
ipaddr_.clear();
netmask_.clear();
cidr_ = 0;
}
}
int waybar::modules::Network::netlinkRequest(int fd, void *req,
uint32_t reqlen, uint32_t groups)
{
@ -269,13 +331,12 @@ int waybar::modules::Network::netlinkRequest(int fd, void *req,
int waybar::modules::Network::netlinkResponse(int fd, void *resp,
uint32_t resplen, uint32_t groups)
{
int ret;
struct sockaddr_nl sa = {};
sa.nl_family = AF_NETLINK;
sa.nl_groups = groups;
struct iovec iov = { resp, resplen };
struct msghdr msg = { &sa, sizeof(sa), &iov, 1, nullptr, 0, 0 };
ret = recvmsg(fd, &msg, 0);
auto ret = recvmsg(fd, &msg, 0);
if (msg.msg_flags & MSG_TRUNC) {
return -1;
}
@ -368,9 +429,10 @@ bool waybar::modules::Network::associatedOrJoined(struct nlattr** bss)
auto waybar::modules::Network::getInfo() -> void
{
getInterfaceAddress();
struct nl_msg* nl_msg = nlmsg_alloc();
if (nl_msg == nullptr) {
nl_socket_free(sk_);
nlmsg_free(nl_msg);
return;
}
if (genlmsg_put(nl_msg, NL_AUTO_PORT, NL_AUTO_SEQ, nl80211_id_, 0, NLM_F_DUMP,

View File

@ -1,10 +1,18 @@
#include "modules/pulseaudio.hpp"
waybar::modules::Pulseaudio::Pulseaudio(const Json::Value& config)
: ALabel(config, "{volume}%"), mainloop_(nullptr), mainloop_api_(nullptr),
context_(nullptr), sink_idx_(0), volume_(0), muted_(false)
{
waybar::modules::Pulseaudio::Pulseaudio(const std::string& id, const Json::Value &config)
: ALabel(config, "{volume}%"),
mainloop_(nullptr),
mainloop_api_(nullptr),
context_(nullptr),
sink_idx_(0),
volume_(0),
muted_(false),
scrolling_(false) {
label_.set_name("pulseaudio");
if (!id.empty()) {
label_.get_style_context()->add_class(id);
}
mainloop_ = pa_threaded_mainloop_new();
if (mainloop_ == nullptr) {
throw std::runtime_error("pa_mainloop_new() failed.");
@ -26,10 +34,18 @@ waybar::modules::Pulseaudio::Pulseaudio(const Json::Value& config)
throw std::runtime_error("pa_mainloop_run() failed.");
}
pa_threaded_mainloop_unlock(mainloop_);
// define the pulse scroll events only when no user provided
// events are configured
if (!config["on-scroll-up"].isString() &&
!config["on-scroll-down"].isString()) {
event_box_.add_events(Gdk::SCROLL_MASK);
event_box_.signal_scroll_event().connect(
sigc::mem_fun(*this, &Pulseaudio::handleScroll));
}
}
waybar::modules::Pulseaudio::~Pulseaudio()
{
waybar::modules::Pulseaudio::~Pulseaudio() {
mainloop_api_->quit(mainloop_api_, 0);
pa_threaded_mainloop_stop(mainloop_);
pa_threaded_mainloop_free(mainloop_);
@ -58,6 +74,46 @@ void waybar::modules::Pulseaudio::contextStateCb(pa_context *c, void *data)
}
}
bool waybar::modules::Pulseaudio::handleScroll(GdkEventScroll *e) {
// Avoid concurrent scroll event
bool direction_up = false;
uint16_t change = config_["scroll-step"].isUInt() ? config_["scroll-step"].asUInt() * 100 : 100;
pa_cvolume pa_volume = pa_volume_;
if (scrolling_) {
return false;
}
scrolling_ = true;
if (e->direction == GDK_SCROLL_UP) {
direction_up = true;
}
if (e->direction == GDK_SCROLL_DOWN) {
direction_up = false;
}
if (e->direction == GDK_SCROLL_SMOOTH) {
gdouble delta_x, delta_y;
gdk_event_get_scroll_deltas(reinterpret_cast<const GdkEvent *>(e), &delta_x,
&delta_y);
if (delta_y < 0) {
direction_up = true;
} else if (delta_y > 0) {
direction_up = false;
}
}
if (direction_up) {
if (volume_ + 1 < 100) pa_cvolume_inc(&pa_volume, change);
} else {
if (volume_ - 1 > 0) pa_cvolume_dec(&pa_volume, change);
}
pa_context_set_sink_volume_by_index(context_, sink_idx_, &pa_volume,
volumeModifyCb, this);
return true;
}
/*
* Called when an event we subscribed to occurs.
*/
@ -75,21 +131,34 @@ void waybar::modules::Pulseaudio::subscribeCb(pa_context* context,
}
}
/*
* Called in response to a volume change request
*/
void waybar::modules::Pulseaudio::volumeModifyCb(pa_context *c, int success,
void *data) {
auto pa = static_cast<waybar::modules::Pulseaudio *>(data);
if (success) {
pa_context_get_sink_info_by_index(pa->context_, pa->sink_idx_, sinkInfoCb,
data);
}
}
/*
* Called when the requested sink information is ready.
*/
void waybar::modules::Pulseaudio::sinkInfoCb(pa_context* /*context*/,
const pa_sink_info* i, int /*eol*/, void* data)
{
void waybar::modules::Pulseaudio::sinkInfoCb(pa_context * /*context*/,
const pa_sink_info *i, int /*eol*/,
void *data) {
if (i != nullptr) {
auto pa = static_cast<waybar::modules::Pulseaudio *>(data);
float volume = static_cast<float>(pa_cvolume_avg(&(i->volume)))
/ float{PA_VOLUME_NORM};
pa->pa_volume_ = i->volume;
float volume = static_cast<float>(pa_cvolume_avg(&(pa->pa_volume_))) /
float{PA_VOLUME_NORM};
pa->sink_idx_ = i->index;
pa->volume_ = std::round(volume * 100.0f);
pa->muted_ = i->mute != 0;
pa->desc_ = i->description;
pa->port_name_ = i->active_port->name;
pa->port_name_ = i->active_port ? i->active_port->name : "Unknown";
pa->dp.emit();
}
}
@ -141,8 +210,11 @@ auto waybar::modules::Pulseaudio::update() -> void
label_.get_style_context()->remove_class("muted");
label_.get_style_context()->add_class("bluetooth");
}
label_.set_label(fmt::format(format,
fmt::arg("volume", volume_),
fmt::arg("icon", getIcon(volume_, getPortIcon()))));
label_.set_markup(
fmt::format(format, fmt::arg("volume", volume_),
fmt::arg("icon", getIcon(volume_, getPortIcon()))));
label_.set_tooltip_text(desc_);
if (scrolling_) {
scrolling_ = false;
}
}

View File

@ -1,56 +1,53 @@
#include "modules/sni/snh.hpp"
#include "modules/sni/host.hpp"
#include <iostream>
using namespace waybar::modules::SNI;
Host::Host(Glib::Dispatcher* dp)
: dp_(dp)
Host::Host(const std::size_t id, const Json::Value &config,
const std::function<void(std::unique_ptr<Item>&)>& on_add,
const std::function<void(std::unique_ptr<Item>&)>& on_remove)
: bus_name_("org.kde.StatusNotifierHost-" + std::to_string(getpid()) + "-" + std::to_string(id)),
object_path_("/StatusNotifierHost/" + std::to_string(id)),
bus_name_id_(Gio::DBus::own_name(Gio::DBus::BusType::BUS_TYPE_SESSION, bus_name_,
sigc::mem_fun(*this, &Host::busAcquired))),
config_(config), on_add_(on_add), on_remove_(on_remove)
{
GBusNameOwnerFlags flags = static_cast<GBusNameOwnerFlags>(
G_BUS_NAME_OWNER_FLAGS_NONE);
bus_name_ = "org.kde.StatusNotifierHost-" + std::to_string(getpid());
object_path_ = "/StatusNotifierHost";
bus_name_id_ = g_bus_own_name(G_BUS_TYPE_SESSION,
bus_name_.c_str(), flags,
&Host::busAcquired, nullptr, nullptr, this, nullptr);
}
void Host::busAcquired(GDBusConnection* connection,
const gchar* name, gpointer data)
Host::~Host()
{
auto host = static_cast<SNI::Host *>(data);
host->watcher_id_ = g_bus_watch_name(
G_BUS_TYPE_SESSION,
"org.kde.StatusNotifierWatcher",
G_BUS_NAME_WATCHER_FLAGS_NONE,
&Host::nameAppeared, &Host::nameVanished, data, nullptr);
Gio::DBus::unwatch_name(bus_name_id_);
}
void Host::nameAppeared(GDBusConnection* connection,
const gchar* name, const gchar* name_owner, gpointer data)
void Host::busAcquired(const Glib::RefPtr<Gio::DBus::Connection>& conn, Glib::ustring name)
{
auto host = static_cast<SNI::Host *>(data);
if (host->cancellable_ != nullptr) {
watcher_id_ = Gio::DBus::watch_name(conn, "org.kde.StatusNotifierWatcher",
sigc::mem_fun(*this, &Host::nameAppeared), sigc::mem_fun(*this, &Host::nameVanished));
}
void Host::nameAppeared(const Glib::RefPtr<Gio::DBus::Connection>& conn, const Glib::ustring name,
const Glib::ustring& name_owner)
{
if (cancellable_ != nullptr) {
// TODO
return;
}
host->cancellable_ = g_cancellable_new();
cancellable_ = g_cancellable_new();
sn_watcher_proxy_new(
connection,
conn->gobj(),
G_DBUS_PROXY_FLAGS_NONE,
"org.kde.StatusNotifierWatcher",
"/StatusNotifierWatcher",
host->cancellable_, &Host::proxyReady, data);
cancellable_, &Host::proxyReady, this);
}
void Host::nameVanished(GDBusConnection* connection,
const gchar* name, gpointer data)
void Host::nameVanished(const Glib::RefPtr<Gio::DBus::Connection>& conn, const Glib::ustring name)
{
auto host = static_cast<SNI::Host *>(data);
g_cancellable_cancel(host->cancellable_);
g_clear_object(&host->cancellable_);
g_clear_object(&host->watcher_);
host->items.clear();
g_cancellable_cancel(cancellable_);
g_clear_object(&cancellable_);
g_clear_object(&watcher_);
items_.clear();
}
void Host::proxyReady(GObject* src, GAsyncResult* res,
@ -104,8 +101,7 @@ void Host::registerHost(GObject* src, GAsyncResult* res,
g_strfreev(items);
}
void Host::itemRegistered(
SnWatcher* watcher, const gchar* service, gpointer data)
void Host::itemRegistered(SnWatcher* watcher, const gchar* service, gpointer data)
{
auto host = static_cast<SNI::Host *>(data);
host->addRegisteredItem(service);
@ -116,35 +112,28 @@ void Host::itemUnregistered(
{
auto host = static_cast<SNI::Host *>(data);
auto [bus_name, object_path] = host->getBusNameAndObjectPath(service);
for (auto it = host->items.begin(); it != host->items.end(); ++it) {
if (it->bus_name == bus_name && it->object_path == object_path) {
host->items.erase(it);
for (auto it = host->items_.begin(); it != host->items_.end(); ++it) {
if ((*it)->bus_name == bus_name && (*it)->object_path == object_path) {
host->on_remove_(*it);
host->items_.erase(it);
break;
}
}
host->dp_->emit();
}
std::tuple<std::string, std::string> Host::getBusNameAndObjectPath(
const gchar* service)
const std::string service)
{
std::string bus_name;
std::string object_path;
gchar* tmp = g_strstr_len(service, -1, "/");
if (tmp != nullptr) {
gchar** str = g_strsplit(service, "/", 2);
bus_name = str[0];
object_path = tmp;
g_strfreev(str);
} else {
bus_name = service;
object_path = "/StatusNotifierItem";
auto it = service.find("/");
if (it != std::string::npos) {
return {service.substr(0, it), service.substr(it)};
}
return { bus_name, object_path };
return {service, "/StatusNotifierItem"};
}
void Host::addRegisteredItem(const gchar* service)
void Host::addRegisteredItem(std::string service)
{
auto [bus_name, object_path] = getBusNameAndObjectPath(service);
items.emplace_back(bus_name, object_path, dp_);
items_.emplace_back(new Item(bus_name, object_path, config_));
on_add_(items_.back());
}

View File

@ -1,13 +1,14 @@
#include "modules/sni/sni.hpp"
#include "modules/sni/item.hpp"
#include <iostream>
#include <libdbusmenu-gtk/dbusmenu-gtk.h>
waybar::modules::SNI::Item::Item(std::string bn, std::string op,
Glib::Dispatcher *dp)
: bus_name(bn), object_path(op), event_box(), icon_size(16),
effective_icon_size(0), image(Gtk::manage(new Gtk::Image())), dp_(dp) {
event_box.add(*image);
waybar::modules::SNI::Item::Item(std::string bn, std::string op, const Json::Value& config)
: bus_name(bn), object_path(op), icon_size(16), effective_icon_size(0)
{
if (config["icon-size"].isUInt()) {
icon_size = config["icon-size"].asUInt();
}
event_box.add(image);
event_box.add_events(Gdk::BUTTON_PRESS_MASK);
event_box.signal_button_press_event().connect(
sigc::mem_fun(*this, &Item::handleClick));
@ -20,8 +21,7 @@ waybar::modules::SNI::Item::Item(std::string bn, std::string op,
void waybar::modules::SNI::Item::proxyReady(GObject *obj, GAsyncResult *res,
gpointer data) {
GError *error = nullptr;
SnItem *proxy =
sn_item_proxy_new_for_bus_finish(res, &error);
SnItem *proxy = sn_item_proxy_new_for_bus_finish(res, &error);
if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
g_error_free(error);
return;
@ -36,11 +36,10 @@ void waybar::modules::SNI::Item::proxyReady(GObject *obj, GAsyncResult *res,
auto conn = g_dbus_proxy_get_connection(G_DBUS_PROXY(proxy));
g_dbus_connection_call(conn, item->bus_name.c_str(),
item->object_path.c_str(),
"org.freedesktop.DBus.Properties", "GetAll",
g_variant_new("(s)", "org.kde.StatusNotifierItem"),
G_VARIANT_TYPE("(a{sv})"), G_DBUS_CALL_FLAGS_NONE, -1,
item->cancellable_, &Item::getAll, data);
item->object_path.c_str(), "org.freedesktop.DBus.Properties", "GetAll",
g_variant_new("(s)", "org.kde.StatusNotifierItem"),
G_VARIANT_TYPE("(a{sv})"), G_DBUS_CALL_FLAGS_NONE, -1, item->cancellable_,
&Item::getAll, data);
}
void waybar::modules::SNI::Item::getAll(GObject *obj, GAsyncResult *res,
@ -103,17 +102,16 @@ void waybar::modules::SNI::Item::getAll(GObject *obj, GAsyncResult *res,
g_variant_unref(properties);
if (item->id.empty() || item->category.empty() || item->status.empty()) {
std::cerr << "Invalid Status Notifier Item: " + item->bus_name + "," +
item->object_path
<< std::endl;
item->object_path << std::endl;
return;
}
if (!item->icon_theme_path.empty()) {
GtkIconTheme *icon_theme = gtk_icon_theme_get_default();
gtk_icon_theme_append_search_path(icon_theme,
item->icon_theme_path.c_str());
item->icon_theme_path.c_str());
}
item->updateImage();
item->dp_->emit();
// item->event_box.set_tooltip_text(item->title);
// TODO: handle change
}
@ -167,43 +165,41 @@ waybar::modules::SNI::Item::extractPixBuf(GVariant *variant) {
void waybar::modules::SNI::Item::updateImage()
{
image.set_from_icon_name("image-missing", Gtk::ICON_SIZE_MENU);
image.set_pixel_size(icon_size);
if (!icon_name.empty()) {
auto pixbuf = getIconByName(icon_name, icon_size);
if (pixbuf->gobj() == nullptr) {
try {
// Try to find icons specified by path and filename
try {
pixbuf = Gdk::Pixbuf::create_from_file(icon_name);
#ifdef FILESYSTEM_EXPERIMENTAL
if (std::experimental::filesystem::exists(icon_name)) {
#else
if (std::filesystem::exists(icon_name)) {
#endif
auto pixbuf = Gdk::Pixbuf::create_from_file(icon_name);
if (pixbuf->gobj() != nullptr) {
// An icon specified by path and filename may be the wrong size for
// the tray
pixbuf->scale_simple(icon_size - 2, icon_size - 2,
Gdk::InterpType::INTERP_BILINEAR);
pixbuf = pixbuf->scale_simple(icon_size, icon_size,
Gdk::InterpType::INTERP_BILINEAR);
image.set(pixbuf);
}
} catch (Glib::Error &e) {
std::cerr << "Exception: " << e.what() << std::endl;
pixbuf = getIconByName("image-missing", icon_size);
} else {
image.set(getIconByName(icon_name, icon_size));
}
} catch (Glib::Error &e) {
std::cerr << "Exception: " << e.what() << std::endl;
}
if (pixbuf->gobj() == nullptr) {
pixbuf = getIconByName("image-missing", icon_size);
}
image->set(pixbuf);
} else if (icon_pixmap) {
image->set(icon_pixmap);
} else {
image->set_from_icon_name("image-missing", Gtk::ICON_SIZE_MENU);
image->set_pixel_size(icon_size);
}
if (!menu.empty()) {
auto *dbmenu = dbusmenu_gtkmenu_new(bus_name.data(), menu.data());
if (dbmenu)
gtk_menu = Glib::wrap(GTK_MENU(dbmenu), false);
// An icon extracted may be the wrong size for the tray
icon_pixmap = icon_pixmap->scale_simple(icon_size, icon_size,
Gdk::InterpType::INTERP_BILINEAR);
image.set(icon_pixmap);
}
}
Glib::RefPtr<Gdk::Pixbuf>
waybar::modules::SNI::Item::getIconByName(std::string name, int request_size) {
int icon_size = 0;
int tmp_size = 0;
Glib::RefPtr<Gtk::IconTheme> icon_theme = Gtk::IconTheme::get_default();
icon_theme->rescan_if_needed();
auto sizes = icon_theme->get_icon_sizes(name.c_str());
@ -211,50 +207,59 @@ waybar::modules::SNI::Item::getIconByName(std::string name, int request_size) {
for (auto const &size : sizes) {
// -1 == scalable
if (size == request_size || size == -1) {
icon_size = request_size;
tmp_size = request_size;
break;
} else if (size < request_size || size > icon_size) {
icon_size = size;
} else if (size < request_size || size > tmp_size) {
tmp_size = size;
}
}
if (icon_size == 0) {
icon_size = request_size;
if (tmp_size == 0) {
tmp_size = request_size;
}
return icon_theme->load_icon(name.c_str(), icon_size,
Gtk::IconLookupFlags::ICON_LOOKUP_FORCE_SIZE);
return icon_theme->load_icon(name.c_str(), tmp_size,
Gtk::IconLookupFlags::ICON_LOOKUP_FORCE_SIZE);
}
void waybar::modules::SNI::Item::handleActivate(GObject *src, GAsyncResult *res,
gpointer data) {
auto item = static_cast<SNI::Item *>(data);
sn_item_call_activate_finish(item->proxy_, res,
nullptr);
void waybar::modules::SNI::Item::onMenuDestroyed(Item *self)
{
self->gtk_menu = nullptr;
self->dbus_menu = nullptr;
}
void waybar::modules::SNI::Item::handleSecondaryActivate(GObject *src,
GAsyncResult *res,
gpointer data) {
auto item = static_cast<SNI::Item *>(data);
sn_item_call_secondary_activate_finish(item->proxy_,
res, nullptr);
bool waybar::modules::SNI::Item::makeMenu(GdkEventButton *const &ev)
{
if (gtk_menu == nullptr) {
if (!menu.empty()) {
dbus_menu = dbusmenu_gtkmenu_new(bus_name.data(), menu.data());
if (dbus_menu != nullptr) {
g_object_ref_sink(G_OBJECT(dbus_menu));
g_object_weak_ref(G_OBJECT(dbus_menu), (GWeakNotify)onMenuDestroyed, this);
gtk_menu = Glib::wrap(GTK_MENU(dbus_menu));
gtk_menu->attach_to_widget(event_box);
}
}
}
if (gtk_menu != nullptr) {
#if GTK_CHECK_VERSION(3, 22, 0)
gtk_menu->popup_at_pointer(reinterpret_cast<GdkEvent*>(ev));
#else
gtk_menu->popup(ev->button, ev->time);
#endif
return true;
}
return false;
}
bool waybar::modules::SNI::Item::handleClick(GdkEventButton *const &ev) {
if (ev->type == GDK_BUTTON_PRESS) {
if (gtk_menu) {
if (!gtk_menu->get_attach_widget()) {
gtk_menu->attach_to_widget(event_box);
}
gtk_menu->popup(ev->button, ev->time);
} else {
sn_item_call_activate(
proxy_, ev->x, ev->y, nullptr, &Item::handleActivate, this);
if ((ev->button == 1 && item_is_menu) || ev->button == 3) {
if (!makeMenu(ev)) {
return sn_item_call_context_menu_sync(proxy_, ev->x, ev->y, nullptr, nullptr);
}
} else if (ev->type == GDK_2BUTTON_PRESS) {
sn_item_call_secondary_activate(
proxy_, ev->x, ev->y, nullptr, &Item::handleSecondaryActivate, this);
} else {
return false;
} else if (ev->button == 1) {
return sn_item_call_activate_sync(proxy_, ev->x, ev->y, nullptr, nullptr);
} else if (ev->button == 2) {
return sn_item_call_secondary_activate_sync(proxy_, ev->x, ev->y,
nullptr, nullptr);
}
return true;
return false;
}

View File

@ -2,14 +2,31 @@
#include <iostream>
waybar::modules::SNI::Tray::Tray(const Json::Value &config)
: config_(config), watcher_(), host_(&dp) {}
waybar::modules::SNI::Tray::Tray(const std::string& id, const Json::Value &config)
: config_(config), watcher_(), host_(nb_hosts_, config,
std::bind(&Tray::onAdd, this, std::placeholders::_1),
std::bind(&Tray::onRemove, this, std::placeholders::_1))
{
std::cout << "Tray is in beta, so there may be bugs or even be unusable." << std::endl;
if (config_["spacing"].isUInt()) {
box_.set_spacing(config_["spacing"].asUInt());
}
nb_hosts_ += 1;
}
void waybar::modules::SNI::Tray::onAdd(std::unique_ptr<Item>& item)
{
box_.pack_start(item->event_box);
dp.emit();
}
void waybar::modules::SNI::Tray::onRemove(std::unique_ptr<Item>& item)
{
box_.remove(item->event_box);
dp.emit();
}
auto waybar::modules::SNI::Tray::update() -> void {
for (auto &item : host_.items) {
item.event_box.set_tooltip_text(item.title);
box_.pack_start(item.event_box);
}
if (box_.get_children().size() > 0) {
box_.set_name("tray");
box_.show_all();
@ -18,4 +35,6 @@ auto waybar::modules::SNI::Tray::update() -> void {
}
}
waybar::modules::SNI::Tray::operator Gtk::Widget &() { return box_; }
waybar::modules::SNI::Tray::operator Gtk::Widget &() {
return box_;
}

View File

@ -1,41 +1,32 @@
#include "modules/sni/snw.hpp"
#include "modules/sni/watcher.hpp"
#include <iostream>
using namespace waybar::modules::SNI;
Watcher::Watcher()
{
GBusNameOwnerFlags flags = static_cast<GBusNameOwnerFlags>(
G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT
| G_BUS_NAME_OWNER_FLAGS_REPLACE);
bus_name_id_ = g_bus_own_name(G_BUS_TYPE_SESSION,
"org.kde.StatusNotifierWatcher", flags,
&Watcher::busAcquired, nullptr, nullptr, this, nullptr);
watcher_ = sn_watcher_skeleton_new();
sn_watcher_set_protocol_version(watcher_, 1);
}
Watcher::~Watcher()
: bus_name_id_(Gio::DBus::own_name(Gio::DBus::BusType::BUS_TYPE_SESSION,
"org.kde.StatusNotifierWatcher", sigc::mem_fun(*this, &Watcher::busAcquired),
Gio::DBus::SlotNameAcquired(), Gio::DBus::SlotNameLost(),
Gio::DBus::BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT | Gio::DBus::BUS_NAME_OWNER_FLAGS_REPLACE)),
watcher_(sn_watcher_skeleton_new())
{
}
void Watcher::busAcquired(GDBusConnection* connection, const gchar* name,
gpointer data)
void Watcher::busAcquired(const Glib::RefPtr<Gio::DBus::Connection>& conn, Glib::ustring name)
{
GError* error = nullptr;
auto host = static_cast<SNI::Watcher*>(data);
g_dbus_interface_skeleton_export(G_DBUS_INTERFACE_SKELETON(host->watcher_),
connection, "/StatusNotifierWatcher", &error);
g_dbus_interface_skeleton_export(G_DBUS_INTERFACE_SKELETON(watcher_),
conn->gobj(), "/StatusNotifierWatcher", &error);
if (error != nullptr) {
std::cerr << error->message << std::endl;
g_error_free(error);
return;
}
g_signal_connect_swapped(host->watcher_, "handle-register-item",
G_CALLBACK(&Watcher::handleRegisterItem), data);
g_signal_connect_swapped(host->watcher_, "handle-register-host",
G_CALLBACK(&Watcher::handleRegisterHost), data);
g_signal_connect_swapped(watcher_, "handle-register-item",
G_CALLBACK(&Watcher::handleRegisterItem), this);
g_signal_connect_swapped(watcher_, "handle-register-host",
G_CALLBACK(&Watcher::handleRegisterHost), this);
}
gboolean Watcher::handleRegisterHost(Watcher* obj,

View File

@ -69,8 +69,7 @@ struct waybar::modules::sway::Ipc::ipc_response
size_t total = 0;
while (total < ipc_header_size_) {
ssize_t res =
::recv(fd, header.data() + total, ipc_header_size_ - total, 0);
auto res = ::recv(fd, header.data() + total, ipc_header_size_ - total, 0);
if (res <= 0) {
throw std::runtime_error("Unable to receive IPC response");
}
@ -81,8 +80,7 @@ struct waybar::modules::sway::Ipc::ipc_response
std::string payload;
payload.reserve(data32[0] + 1);
while (total < data32[0]) {
ssize_t res =
::recv(fd, payload.data() + total, data32[0] - total, 0);
auto res = ::recv(fd, payload.data() + total, data32[0] - total, 0);
if (res < 0) {
throw std::runtime_error("Unable to receive IPC response");
}
@ -131,4 +129,4 @@ struct waybar::modules::sway::Ipc::ipc_response
waybar::modules::sway::Ipc::handleEvent() const
{
return recv(fd_event_);
}
}

46
src/modules/sway/mode.cpp Normal file
View File

@ -0,0 +1,46 @@
#include "modules/sway/mode.hpp"
waybar::modules::sway::Mode::Mode(const std::string& id, const Bar& bar, const Json::Value& config)
: ALabel(config, "{}"), bar_(bar)
{
label_.set_name("mode");
if (!id.empty()) {
label_.get_style_context()->add_class(id);
}
ipc_.connect();
ipc_.subscribe("[ \"mode\" ]");
// Launch worker
worker();
dp.emit();
}
void waybar::modules::sway::Mode::worker()
{
thread_ = [this] {
try {
auto res = ipc_.handleEvent();
auto parsed = parser_.parse(res.payload);
if ((parsed["change"]) != "default" ) {
mode_ = parsed["change"].asString();
dp.emit();
}
else if ((parsed["change"]) == "default" ) {
mode_.clear();
dp.emit();
}
} catch (const std::exception& e) {
std::cerr << "Mode: " << e.what() << std::endl;
}
};
}
auto waybar::modules::sway::Mode::update() -> void
{
if (mode_.empty()) {
event_box_.hide();
} else {
label_.set_markup(fmt::format(format_, mode_));
label_.set_tooltip_text(mode_);
event_box_.show();
}
}

View File

@ -1,11 +1,18 @@
#include "modules/sway/window.hpp"
waybar::modules::sway::Window::Window(Bar &bar, const Json::Value& config)
waybar::modules::sway::Window::Window(const std::string& id, const Bar &bar, const Json::Value& config)
: ALabel(config, "{}"), bar_(bar), windowId_(-1)
{
label_.set_name("window");
if (!id.empty()) {
label_.get_style_context()->add_class(id);
}
if (label_.get_max_width_chars() == -1) {
label_.set_hexpand(true);
label_.set_ellipsize(Pango::EllipsizeMode::ELLIPSIZE_END);
}
ipc_.connect();
ipc_.subscribe("[ \"window\" ]");
ipc_.subscribe("[\"window\",\"workspace\"]");
getFocusedWindow();
// Launch worker
worker();
@ -19,25 +26,27 @@ void waybar::modules::sway::Window::worker()
auto parsed = parser_.parse(res.payload);
if ((parsed["change"] == "focus" || parsed["change"] == "title")
&& parsed["container"]["focused"].asBool()) {
window_ = parsed["container"]["name"].asString();
window_ = Glib::Markup::escape_text(parsed["container"]["name"].asString());
windowId_ = parsed["container"]["id"].asInt();
dp.emit();
} else if (parsed["change"] == "close"
} else if ((parsed["change"] == "close"
&& parsed["container"]["focused"].asBool()
&& windowId_ == parsed["container"]["id"].asInt()) {
&& windowId_ == parsed["container"]["id"].asInt())
|| (parsed["change"] == "focus" && parsed["current"]["focus"].isArray()
&& parsed["current"]["focus"].empty())) {
window_.clear();
windowId_ = -1;
dp.emit();
}
} catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
std::cerr << "Window: " << e.what() << std::endl;
}
};
}
auto waybar::modules::sway::Window::update() -> void
{
label_.set_text(fmt::format(format_, window_));
label_.set_markup(fmt::format(format_, window_));
label_.set_tooltip_text(window_);
}

View File

@ -1,10 +1,13 @@
#include "modules/sway/workspaces.hpp"
waybar::modules::sway::Workspaces::Workspaces(Bar& bar,
waybar::modules::sway::Workspaces::Workspaces(const std::string& id, const Bar& bar,
const Json::Value& config)
: bar_(bar), config_(config), scrolling_(false)
{
box_.set_name("workspaces");
if (!id.empty()) {
box_.get_style_context()->add_class(id);
}
ipc_.connect();
ipc_.subscribe("[ \"workspace\" ]");
// Launch worker
@ -20,7 +23,7 @@ void waybar::modules::sway::Workspaces::worker()
while (bar_.output_name.empty()) {
thread_.sleep_for(chrono::milliseconds(150));
}
} else if (!workspaces_.empty()) {
} else if (thread_.isRunnging() && !workspaces_.empty()) {
ipc_.handleEvent();
}
{
@ -30,7 +33,7 @@ void waybar::modules::sway::Workspaces::worker()
}
dp.emit();
} catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
std::cerr << "Workspaces: " << e.what() << std::endl;
}
};
}
@ -41,8 +44,10 @@ auto waybar::modules::sway::Workspaces::update() -> void
std::lock_guard<std::mutex> lock(mutex_);
for (auto it = buttons_.begin(); it != buttons_.end();) {
auto ws = std::find_if(workspaces_.begin(), workspaces_.end(),
[it](auto node) -> bool { return node["num"].asInt() == it->first; });
if (ws == workspaces_.end()) {
[it](auto node) -> bool { return node["name"].asString() == it->first; });
if (ws == workspaces_.end() ||
(!config_["all-outputs"].asBool() &&
(*ws)["output"].asString() != bar_.output_name)) {
it = buttons_.erase(it);
needReorder = true;
} else {
@ -54,7 +59,7 @@ auto waybar::modules::sway::Workspaces::update() -> void
&& bar_.output_name != node["output"].asString()) {
continue;
}
auto it = buttons_.find(node["num"].asInt());
auto it = buttons_.find(node["name"].asString());
if (it == buttons_.end()) {
addWorkspace(node);
needReorder = true;
@ -103,7 +108,7 @@ void waybar::modules::sway::Workspaces::addWorkspace(Json::Value node)
fmt::arg("name", node["name"].asString()),
fmt::arg("index", node["num"].asString()))
: icon;
auto pair = buttons_.emplace(node["num"].asInt(), format);
auto pair = buttons_.emplace(node["name"].asString(), format);
auto &button = pair.first->second;
box_.pack_start(button, false, false, 0);
button.set_relief(Gtk::RELIEF_NONE);
@ -158,73 +163,73 @@ bool waybar::modules::sway::Workspaces::handleScroll(GdkEventScroll *e)
return false;
}
scrolling_ = true;
int id = -1;
std::string name;
uint16_t idx = 0;
{
std::lock_guard<std::mutex> lock(mutex_);
for (; idx < workspaces_.size(); idx += 1) {
if (workspaces_[idx]["focused"].asBool()) {
id = workspaces_[idx]["num"].asInt();
name = workspaces_[idx]["name"].asString();
break;
}
}
}
if (id == -1) {
if (name.empty()) {
scrolling_ = false;
return false;
}
if (e->direction == GDK_SCROLL_UP) {
id = getNextWorkspace();
name = getNextWorkspace();
}
if (e->direction == GDK_SCROLL_DOWN) {
id = getPrevWorkspace();
name = getPrevWorkspace();
}
if (e->direction == GDK_SCROLL_SMOOTH) {
gdouble delta_x, delta_y;
gdk_event_get_scroll_deltas(reinterpret_cast<const GdkEvent *>(e),
&delta_x, &delta_y);
if (delta_y < 0) {
id = getNextWorkspace();
name = getNextWorkspace();
} else if (delta_y > 0) {
id = getPrevWorkspace();
name = getPrevWorkspace();
}
}
{
if (!name.empty()) {
std::lock_guard<std::mutex> lock(mutex_);
if (id == workspaces_[idx]["num"].asInt()) {
if (name == workspaces_[idx]["name"].asString()) {
scrolling_ = false;
return false;
}
ipc_.sendCmd(IPC_COMMAND, fmt::format("workspace \"{}\"", id));
ipc_.sendCmd(IPC_COMMAND, fmt::format("workspace \"{}\"", name));
std::this_thread::sleep_for(std::chrono::milliseconds(150));
}
return true;
}
int waybar::modules::sway::Workspaces::getPrevWorkspace()
std::string waybar::modules::sway::Workspaces::getPrevWorkspace()
{
for (uint16_t i = 0; i != workspaces_.size(); i += 1) {
if (workspaces_[i]["focused"].asBool()) {
if (i > 0) {
return workspaces_[i - 1]["num"].asInt();
return workspaces_[i - 1]["name"].asString();
}
return workspaces_[workspaces_.size() - 1]["num"].asInt();
return workspaces_[workspaces_.size() - 1]["name"].asString();
}
}
return -1;
return "";
}
int waybar::modules::sway::Workspaces::getNextWorkspace()
std::string waybar::modules::sway::Workspaces::getNextWorkspace()
{
for (uint16_t i = 0; i != workspaces_.size(); i += 1) {
if (workspaces_[i]["focused"].asBool()) {
if (i + 1U < workspaces_.size()) {
return workspaces_[i + 1]["num"].asInt();
return workspaces_[i + 1]["name"].asString();
}
return workspaces_[0]["num"].asInt();
return workspaces_[0]["String"].asString();
}
}
return -1;
return "";
}
waybar::modules::sway::Workspaces::operator Gtk::Widget &() {

10
subprojects/fmt.wrap Normal file
View File

@ -0,0 +1,10 @@
[wrap-file]
directory = fmt-5.2.1
source_url = https://github.com/fmtlib/fmt/archive/5.2.1.tar.gz
source_filename = fmt-5.2.1.tar.gz
source_hash = 3c812a18e9f72a88631ab4732a97ce9ef5bcbefb3235e9fd465f059ba204359b
patch_url = https://wrapdb.mesonbuild.com/v1/projects/fmt/5.2.1/1/get_zip
patch_filename = fmt-5.2.1-1-wrap.zip
patch_hash = 7add08bb4e168c0809e88c6aa64ed5c3494b74deb6be12a93e1e4dc5bb3a1fc1

View File

@ -1,10 +0,0 @@
[wrap-file]
directory = fmt-4.1.0
source_url = https://github.com/fmtlib/fmt/archive/4.1.0.tar.gz
source_filename = fmt-4.1.0.tar.gz
source_hash = 46628a2f068d0e33c716be0ed9dcae4370242df135aed663a180b9fd8e36733d
patch_url = https://wrapdb.mesonbuild.com/v1/projects/fmt/4.1.0/1/get_zip
patch_filename = fmt-4.1.0-1-wrap.zip
patch_hash = 741931f01e558491724fc1c67bff996d1df79c0277626fc463de138052c9ecc0