Merge branch 'Alexays:master' into master

This commit is contained in:
hecate cantus
2025-10-16 18:40:58 -07:00
committed by GitHub
162 changed files with 5420 additions and 2447 deletions
+4 -3
View File
@@ -1,14 +1,15 @@
name: Build and Push Docker Image
on:
workflow_dispatch:
schedule:
# run every night at midnight
- cron: '0 0 * * *'
# run monthly
- cron: '0 0 1 * *'
jobs:
build-and-push:
runs-on: ubuntu-latest
if: github.repository == 'Alexays/Waybar'
if: github.event_name != 'schedule' || github.repository == 'Alexays/Waybar'
strategy:
fail-fast: false # don't fail the other jobs if one of the images fails to build
matrix:
+2 -2
View File
@@ -14,14 +14,14 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Test in FreeBSD VM
uses: cross-platform-actions/action@v0.25.0
uses: cross-platform-actions/action@v0.28.0
timeout-minutes: 180
env:
CPPFLAGS: '-isystem/usr/local/include'
LDFLAGS: '-L/usr/local/lib'
with:
operating_system: freebsd
version: "14.1"
version: "14.2"
environment_variables: CPPFLAGS LDFLAGS
sync_files: runner-to-vm
run: |
+1 -1
View File
@@ -1,6 +1,6 @@
# vim: ft=Dockerfile
FROM debian:sid
FROM debian:sid-slim
RUN apt update && \
apt install --no-install-recommends --no-install-suggests -y \
+1 -1
View File
@@ -6,6 +6,6 @@ RUN export FEATURES="-ipc-sandbox -network-sandbox -pid-sandbox -sandbox -usersa
emerge --sync && \
eselect news read --quiet new 1>/dev/null 2>&1 && \
emerge --verbose --update --deep --with-bdeps=y --backtrack=30 --newuse @world && \
USE="wayland gtk3 gtk -doc X pulseaudio minimal" emerge dev-vcs/git dev-libs/wayland dev-libs/wayland-protocols =dev-cpp/gtkmm-3.24.6 x11-libs/libxkbcommon \
USE="wayland gtk3 gtk -doc X pulseaudio minimal" emerge dev-vcs/git dev-libs/wayland dev-libs/wayland-protocols dev-cpp/gtkmm:3.0 x11-libs/libxkbcommon \
x11-libs/gtk+:3 dev-libs/libdbusmenu dev-libs/libnl sys-power/upower media-libs/libpulse dev-libs/libevdev media-libs/libmpdclient \
media-sound/sndio gui-libs/gtk-layer-shell app-text/scdoc media-sound/playerctl dev-libs/iniparser sci-libs/fftw
+5 -1
View File
@@ -20,8 +20,12 @@ debug-run: build-debug
./build/waybar --log-level debug
test:
meson test -C build --no-rebuild --verbose --suite waybar
meson test -C build --verbose --suite waybar
.PHONY: test
test-detailed:
meson test -C build --verbose --print-errorlogs --test-args='--reporter console -s'
.PHONY: test-detailed
clean:
rm -rf build
+30
View File
@@ -116,6 +116,36 @@ sudo apt install \
libxkbregistry-dev
```
On Arch, you can use this command:
```
pacman -S \
gtkmm3 \
jsoncpp \
libsigc++ \
fmt \
wayland \
chrono-date \
spdlog \
gtk3 \
gobject-introspection \
libgirepository \
libpulse \
libnl \
libappindicator-gtk3 \
libdbusmenu-gtk3 \
libmpdclient \
sndio \
libevdev \
libxkbcommon \
upower \
meson \
cmake \
scdoc \
wayland-protocols \
glib2-devel
```
Contributions welcome!<br>
Have fun :)<br>
+5
View File
@@ -0,0 +1,5 @@
# Suppress common address sanitizer issues in dependencies, these are often non-fixable or not an issue.
# Use it like this (when in repo root): ASAN_OPTIONS="suppressions=./asan.supp" ./build/waybar
leak:libpangoft2-1.0.so.0
leak:libgtk-3.so.0
leak:libfontconfig.so.1
Generated
+6 -6
View File
@@ -3,11 +3,11 @@
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1733328505,
"narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=",
"lastModified": 1747046372,
"narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec",
"rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885",
"type": "github"
},
"original": {
@@ -18,11 +18,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1745391562,
"narHash": "sha256-sPwcCYuiEopaafePqlG826tBhctuJsLx/mhKKM5Fmjo=",
"lastModified": 1759036355,
"narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "8a2f738d9d1f1d986b5a4cd2fd2061a7127237d7",
"rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127",
"type": "github"
},
"original": {
+20 -22
View File
@@ -65,7 +65,7 @@
nativeBuildInputs =
pkgs.waybar.nativeBuildInputs
++ (with pkgs; [
nixfmt-rfc-style
nixfmt
clang-tools
gdb
]);
@@ -75,28 +75,26 @@
formatter = genSystems (
pkgs:
pkgs.treefmt.withConfig {
settings = [
{
formatter = {
clang-format = {
options = [ "-i" ];
command = lib.getExe' pkgs.clang-tools "clang-format";
excludes = [ ];
includes = [
"*.c"
"*.cpp"
"*.h"
"*.hpp"
];
};
nixfmt = {
command = lib.getExe pkgs.nixfmt-rfc-style;
includes = [ "*.nix" ];
};
settings = {
formatter = {
clang-format = {
options = [ "-i" ];
command = lib.getExe' pkgs.clang-tools "clang-format";
excludes = [ ];
includes = [
"*.c"
"*.cpp"
"*.h"
"*.hpp"
];
};
tree-root-file = ".git/index";
}
];
nixfmt = {
command = lib.getExe pkgs.nixfmt;
includes = [ "*.nix" ];
};
};
tree-root-file = ".git/index";
};
}
);
+1 -1
View File
@@ -21,7 +21,7 @@ class ALabel : public AModule {
protected:
Gtk::Label label_;
std::string format_;
const std::chrono::seconds interval_;
const std::chrono::milliseconds interval_;
bool alt_ = false;
std::string default_format_;
+1 -1
View File
@@ -36,6 +36,7 @@ class AModule : public IModule {
SCROLL_DIR getScrollDir(GdkEventScroll *e);
bool tooltipEnabled() const;
std::vector<int> pid_children_;
const std::string name_;
const Json::Value &config_;
Gtk::EventBox event_box_;
@@ -54,7 +55,6 @@ class AModule : public IModule {
const bool isTooltip;
const bool isExpand;
bool hasUserEvents_;
std::vector<int> pid_;
gdouble distance_scrolled_y_;
gdouble distance_scrolled_x_;
std::map<std::string, std::string> eventActionMap_;
+8
View File
@@ -14,6 +14,7 @@
#include "AModule.hpp"
#include "group.hpp"
#include "util/kill_signal.hpp"
#include "xdg-output-unstable-v1-client-protocol.h"
namespace waybar {
@@ -68,7 +69,11 @@ class Bar : public sigc::trackable {
void setMode(const std::string &mode);
void setVisible(bool value);
void toggle();
void show();
void hide();
void handleSignal(int);
util::KillSignalAction getOnSigusr1Action();
util::KillSignalAction getOnSigusr2Action();
struct waybar_output *output;
Json::Value config;
@@ -118,6 +123,9 @@ class Bar : public sigc::trackable {
std::unique_ptr<BarIpcClient> _ipc_client;
#endif
std::vector<std::shared_ptr<waybar::AModule>> modules_all_;
waybar::util::KillSignalAction onSigusr1 = util::SIGNALACTION_DEFAULT_SIGUSR1;
waybar::util::KillSignalAction onSigusr2 = util::SIGNALACTION_DEFAULT_SIGUSR2;
};
} // namespace waybar
+2
View File
@@ -35,6 +35,8 @@ class Config {
void setupConfig(Json::Value &dst, const std::string &config_file, int depth);
void resolveConfigIncludes(Json::Value &config, int depth);
void mergeConfig(Json::Value &a_config_, Json::Value &b_config_);
static std::vector<std::string> findIncludePath(
const std::string &name, const std::vector<std::string> &dirs = CONFIG_DIRS);
std::string config_file_;
+2
View File
@@ -35,6 +35,7 @@ class Battery : public ALabel {
std::tuple<uint8_t, float, std::string, float, uint16_t, float> getInfos();
const std::string formatTimeRemaining(float hoursRemaining);
void setBarClass(std::string&);
void processEvents(std::string& state, std::string& status, uint8_t capacity);
int global_watch;
std::map<fs::path, int> batteries_;
@@ -43,6 +44,7 @@ class Battery : public ALabel {
int global_watch_fd_;
std::mutex battery_list_mutex_;
std::string old_status_;
std::string last_event_;
bool warnFirstTime_{true};
const Bar& bar_;
-59
View File
@@ -1,59 +0,0 @@
#pragma once
#include "ALabel.hpp"
#include "util/sleeper_thread.hpp"
namespace cava {
extern "C" {
// Need sdl_glsl output feature to be enabled on libcava
#ifndef SDL_GLSL
#define SDL_GLSL
#endif
#include <cava/common.h>
#ifdef SDL_GLSL
#undef SDL_GLSL
#endif
}
} // namespace cava
namespace waybar::modules {
using namespace std::literals::chrono_literals;
class Cava final : public ALabel {
public:
Cava(const std::string&, const Json::Value&);
virtual ~Cava();
auto update() -> void override;
auto doAction(const std::string& name) -> void override;
private:
util::SleeperThread thread_;
util::SleeperThread thread_fetch_input_;
struct cava::error_s error_{}; // cava errors
struct cava::config_params prm_{}; // cava parameters
struct cava::audio_raw audio_raw_{}; // cava handled raw audio data(is based on audio_data)
struct cava::audio_data audio_data_{}; // cava audio data
struct cava::cava_plan* plan_; //{new cava_plan{}};
// Cava API to read audio source
cava::ptr input_source_;
// Delay to handle audio source
std::chrono::milliseconds frame_time_milsec_{1s};
// Text to display
std::string text_{""};
int rePaint_{1};
std::chrono::seconds fetch_input_delay_{4};
std::chrono::seconds suspend_silence_delay_{0};
bool silence_{false};
bool hide_on_silence_{false};
std::string format_silent_{""};
int sleep_counter_{0};
// Cava method
void pause_resume();
// ModuleActionMap
static inline std::map<const std::string, void (waybar::modules::Cava::* const)()> actionMap_{
{"mode", &waybar::modules::Cava::pause_resume}};
};
} // namespace waybar::modules
+30
View File
@@ -0,0 +1,30 @@
#pragma once
#include "ALabel.hpp"
#include "cava_backend.hpp"
namespace waybar::modules::cava {
class Cava final : public ALabel, public sigc::trackable {
public:
Cava(const std::string&, const Json::Value&);
~Cava() = default;
auto onUpdate(const std::string& input) -> void;
auto onSilence() -> void;
auto doAction(const std::string& name) -> void override;
private:
std::shared_ptr<CavaBackend> backend_;
// Text to display
std::string label_text_{""};
bool hide_on_silence_{false};
std::string format_silent_{""};
int ascii_range_{0};
bool silence_{false};
// Cava method
void pause_resume();
// ModuleActionMap
static inline std::map<const std::string, void (waybar::modules::cava::Cava::* const)()>
actionMap_{{"mode", &waybar::modules::cava::Cava::pause_resume}};
};
} // namespace waybar::modules::cava
+74
View File
@@ -0,0 +1,74 @@
#pragma once
#include <json/json.h>
#include <sigc++/sigc++.h>
#include "util/sleeper_thread.hpp"
namespace cava {
extern "C" {
// Need sdl_glsl output feature to be enabled on libcava
#ifndef SDL_GLSL
#define SDL_GLSL
#endif
#include <cava/common.h>
#ifdef SDL_GLSL
#undef SDL_GLSL
#endif
}
} // namespace cava
namespace waybar::modules::cava {
using namespace std::literals::chrono_literals;
class CavaBackend final {
public:
static std::shared_ptr<CavaBackend> inst(const Json::Value& config);
virtual ~CavaBackend();
// Methods
int getAsciiRange();
void doPauseResume();
void Update();
// Signal accessor
using type_signal_update = sigc::signal<void(const std::string&)>;
type_signal_update signal_update();
using type_signal_silence = sigc::signal<void()>;
type_signal_silence signal_silence();
private:
CavaBackend(const Json::Value& config);
util::SleeperThread thread_;
util::SleeperThread read_thread_;
// Cava API to read audio source
::cava::ptr input_source_;
struct ::cava::error_s error_{}; // cava errors
struct ::cava::config_params prm_{}; // cava parameters
struct ::cava::audio_raw audio_raw_{}; // cava handled raw audio data(is based on audio_data)
struct ::cava::audio_data audio_data_{}; // cava audio data
struct ::cava::cava_plan* plan_; //{new cava_plan{}};
std::chrono::seconds fetch_input_delay_{4};
// Delay to handle audio source
std::chrono::milliseconds frame_time_milsec_{1s};
int re_paint_{0};
bool silence_{false};
bool silence_prev_{false};
std::chrono::seconds suspend_silence_delay_{0};
int sleep_counter_{0};
std::string output_{};
// Methods
void invoke();
void execute();
bool isSilence();
void doUpdate(bool force = false);
// Signal
type_signal_update m_signal_update_;
type_signal_silence m_signal_silence_;
};
} // namespace waybar::modules::cava
+23 -21
View File
@@ -38,39 +38,41 @@ class Clock final : public ALabel {
5 - tooltip-format
*/
std::map<int, std::string const> fmtMap_;
uint cldMonCols_{3}; // calendar count month columns
int cldWnLen_{3}; // calendar week number length
const int cldMonColLen_{20}; // calendar month column length
WS cldWPos_{WS::HIDDEN}; // calendar week side to print
months cldCurrShift_{0}; // calendar months shift
int cldShift_{1}; // calendar months shift factor
year_month_day cldYearShift_; // calendar Year mode. Cached ymd
std::string cldYearCached_; // calendar Year mode. Cached calendar
year_month cldMonShift_; // calendar Month mode. Cached ym
std::string cldMonCached_; // calendar Month mode. Cached calendar
day cldBaseDay_{0}; // calendar Cached day. Is used when today is changing(midnight)
uint cldMonCols_{3}; // calendar count month columns
int cldWnLen_{3}; // calendar week number length
const int cldMonColLen_{20}; // calendar month column length
WS cldWPos_{WS::HIDDEN}; // calendar week side to print
date::months cldCurrShift_{0}; // calendar months shift
int cldShift_{1}; // calendar months shift factor
date::year_month_day cldYearShift_; // calendar Year mode. Cached ymd
std::string cldYearCached_; // calendar Year mode. Cached calendar
date::year_month cldMonShift_; // calendar Month mode. Cached ym
std::string cldMonCached_; // calendar Month mode. Cached calendar
date::day cldBaseDay_{0}; // calendar Cached day. Is used when today is changing(midnight)
std::string cldText_{""}; // calendar text to print
bool iso8601Calendar_{false}; // whether the calendar is in ISO8601
CldMode cldMode_{CldMode::MONTH};
auto get_calendar(const year_month_day& today, const year_month_day& ymd, const time_zone* tz)
-> const std::string;
auto get_calendar(const date::year_month_day& today, const date::year_month_day& ymd,
const date::time_zone* tz) -> const std::string;
// get local time zone
auto local_zone() -> const time_zone*;
auto local_zone() -> const date::time_zone*;
// time zoned time in tooltip
const bool tzInTooltip_; // if need to print time zones text
std::vector<const time_zone*> tzList_; // time zones list
int tzCurrIdx_; // current time zone index for tzList_
std::string tzText_{""}; // time zones text to print
const bool tzInTooltip_; // if need to print time zones text
std::vector<const date::time_zone*> tzList_; // time zones list
int tzCurrIdx_; // current time zone index for tzList_
std::string tzText_{""}; // time zones text to print
std::string tzTooltipFormat_{""}; // optional timezone tooltip format
util::SleeperThread thread_;
// ordinal date in tooltip
const bool ordInTooltip_;
std::string ordText_{""};
auto get_ordinal_date(const year_month_day& today) -> std::string;
auto get_ordinal_date(const date::year_month_day& today) -> std::string;
auto getTZtext(sys_seconds now) -> std::string;
auto first_day_of_week() -> weekday;
auto getTZtext(date::sys_seconds now) -> std::string;
auto first_day_of_week() -> date::weekday;
// Module actions
void cldModeSwitch();
void cldShift_up();
+141
View File
@@ -0,0 +1,141 @@
#pragma once
#include <fmt/format.h>
#include <gtkmm/button.h>
#include <gtkmm/image.h>
#include <gtkmm/label.h>
#include <map>
#include <memory>
#include <vector>
#include "AModule.hpp"
#include "bar.hpp"
#include "ext-workspace-v1-client-protocol.h"
namespace waybar::modules::ext {
class WorkspaceGroup;
class Workspace;
class WorkspaceManager final : public AModule {
public:
WorkspaceManager(const std::string &id, const waybar::Bar &bar, const Json::Value &config);
~WorkspaceManager() override;
void register_manager(wl_registry *registry, uint32_t name, uint32_t version);
void remove_workspace_group(uint32_t id);
void remove_workspace(uint32_t id);
void set_needs_sorting() { needs_sorting_ = true; }
// wl events
void handle_workspace_group(ext_workspace_group_handle_v1 *handle);
void handle_workspace(ext_workspace_handle_v1 *handle);
void handle_done();
void handle_finished();
// wl requests
void commit() const;
private:
void update() override;
bool has_button(const Gtk::Button *button);
void sort_workspaces();
void clear_buttons();
void update_buttons();
static uint32_t group_global_id;
static uint32_t workspace_global_id;
uint32_t workspace_name = 0;
bool sort_by_id_ = false;
bool sort_by_name_ = true;
bool sort_by_coordinates_ = false;
bool all_outputs_ = false;
const waybar::Bar &bar_;
Gtk::Box box_;
ext_workspace_manager_v1 *ext_manager_ = nullptr;
std::vector<std::unique_ptr<WorkspaceGroup>> groups_;
std::vector<std::unique_ptr<Workspace>> workspaces_;
bool needs_sorting_ = false;
};
class WorkspaceGroup {
public:
WorkspaceGroup(WorkspaceManager &manager, ext_workspace_group_handle_v1 *handle, uint32_t id);
~WorkspaceGroup();
u_int32_t id() const { return id_; }
bool has_output(const wl_output *output);
bool has_workspace(const ext_workspace_handle_v1 *workspace);
// wl events
void handle_capabilities(uint32_t capabilities);
void handle_output_enter(wl_output *output);
void handle_output_leave(wl_output *output);
void handle_workspace_enter(ext_workspace_handle_v1 *handle);
void handle_workspace_leave(ext_workspace_handle_v1 *handle);
void handle_removed();
private:
WorkspaceManager &workspaces_manager_;
ext_workspace_group_handle_v1 *ext_handle_;
uint32_t id_;
std::vector<wl_output *> outputs_;
std::vector<ext_workspace_handle_v1 *> workspaces_;
};
class Workspace {
public:
Workspace(const Json::Value &config, WorkspaceManager &manager, ext_workspace_handle_v1 *handle,
uint32_t id, const std::string &name);
~Workspace();
ext_workspace_handle_v1 *handle() const { return ext_handle_; }
u_int32_t id() const { return id_; }
std::string &workspace_id() { return workspace_id_; }
std::string &name() { return name_; }
std::vector<u_int32_t> &coordinates() { return coordinates_; }
Gtk::Button &button() { return button_; }
void update();
// wl events
void handle_id(const std::string &id);
void handle_name(const std::string &name);
void handle_coordinates(const std::vector<uint32_t> &coordinates);
void handle_state(uint32_t state);
void handle_capabilities(uint32_t capabilities);
void handle_removed();
// gdk events
bool handle_clicked(const GdkEventButton *button) const;
private:
bool has_state(uint32_t state) const { return (state_ & state) == state; }
std::string icon();
WorkspaceManager &workspace_manager_;
ext_workspace_handle_v1 *ext_handle_ = nullptr;
uint32_t id_;
uint32_t state_ = 0;
std::string workspace_id_;
std::string name_;
std::vector<uint32_t> coordinates_;
bool active_only_ = false;
bool ignore_hidden_ = true;
std::string format_;
bool with_icon_ = false;
static std::map<std::string, std::string> icon_map_;
std::string on_click_action_;
std::string on_click_middle_action_;
std::string on_click_right_action_;
Gtk::Button button_;
Gtk::Box content_;
Gtk::Label label_;
};
} // namespace waybar::modules::ext
@@ -0,0 +1,10 @@
#include "ext-workspace-v1-client-protocol.h"
namespace waybar::modules::ext {
void add_registry_listener(void *data);
void add_workspace_listener(ext_workspace_handle_v1 *workspace_handle, void *data);
void add_workspace_group_listener(ext_workspace_group_handle_v1 *workspace_group_handle,
void *data);
ext_workspace_manager_v1 *workspace_manager_bind(wl_registry *registry, uint32_t name,
uint32_t version, void *data);
} // namespace waybar::modules::ext
+40
View File
@@ -0,0 +1,40 @@
#pragma once
#include <fmt/format.h>
#include <sys/statvfs.h>
#ifdef WANT_RFKILL
#include "util/rfkill.hpp"
#endif
#include <gps.h>
#include "ALabel.hpp"
#include "util/sleeper_thread.hpp"
namespace waybar::modules {
class Gps : public ALabel {
public:
Gps(const std::string&, const Json::Value&);
virtual ~Gps();
auto update() -> void override;
private:
#ifdef WANT_RFKILL
util::Rfkill rfkill_;
#endif
const std::string getFixModeName() const;
const std::string getFixModeString() const;
const std::string getFixStatusString() const;
util::SleeperThread thread_, gps_thread_;
gps_data_t gps_data_;
std::string state_;
bool hideDisconnected = true;
bool hideNoFix = false;
};
} // namespace waybar::modules
+7 -4
View File
@@ -17,9 +17,13 @@ class EventHandler {
virtual ~EventHandler() = default;
};
/// If you want to use the Hyprland IPC, simply use IPC::inst() to get the singleton instance.
/// Do not create multiple instances.
class IPC {
protected:
IPC(); // use IPC::inst() instead.
public:
IPC();
~IPC();
static IPC& inst();
@@ -42,8 +46,7 @@ class IPC {
util::JsonParser parser_;
std::list<std::pair<std::string, EventHandler*>> callbacks_;
int socketfd_; // the hyprland socket file descriptor
bool running_ = true;
pid_t socketOwnerPid_;
bool running_ = true; // the ipcThread will stop running when this is false
};
inline bool modulesReady = false;
}; // namespace waybar::modules::hyprland
+41
View File
@@ -0,0 +1,41 @@
#pragma once
#include <fmt/format.h>
#include <string>
#include "AAppIconLabel.hpp"
#include "bar.hpp"
#include "modules/hyprland/backend.hpp"
namespace waybar::modules::hyprland {
class WindowCount : public waybar::AAppIconLabel, public EventHandler {
public:
WindowCount(const std::string&, const waybar::Bar&, const Json::Value&);
~WindowCount() override;
auto update() -> void override;
private:
struct Workspace {
int id;
int windows;
bool hasfullscreen;
static auto parse(const Json::Value& value) -> Workspace;
};
auto getActiveWorkspace(const std::string&) -> Workspace;
auto getActiveWorkspace() -> Workspace;
void onEvent(const std::string& ev) override;
void queryActiveWorkspace();
void setClass(const std::string&, bool enable);
bool separateOutputs_;
std::mutex mutex_;
const Bar& bar_;
Workspace workspace_;
IPC& m_ipc;
};
} // namespace waybar::modules::hyprland
@@ -26,34 +26,48 @@ namespace waybar::modules::hyprland {
class Workspaces;
struct WindowRepr {
std::string address;
std::string window_class;
std::string window_title;
std::string repr_rewrite;
bool isActive = false;
public:
bool empty() const { return address.empty(); }
void setActive(bool value) { isActive = value; }
};
class WindowCreationPayload {
public:
WindowCreationPayload(std::string workspace_name, WindowAddress window_address,
std::string window_repr);
WindowRepr window_repr);
WindowCreationPayload(std::string workspace_name, WindowAddress window_address,
std::string window_class, std::string window_title);
std::string window_class, std::string window_title, bool is_active);
WindowCreationPayload(Json::Value const& client_data);
int incrementTimeSpentUncreated();
bool isEmpty(Workspaces& workspace_manager);
bool reprIsReady() const { return std::holds_alternative<Repr>(m_window); }
std::string repr(Workspaces& workspace_manager);
WindowRepr repr(Workspaces& workspace_manager);
void setActive(bool value) { m_isActive = value; }
std::string getWorkspaceName() const { return m_workspaceName; }
WindowAddress getAddress() const { return m_windowAddress; }
void moveToWorksace(std::string& new_workspace_name);
void moveToWorkspace(std::string& new_workspace_name);
private:
void clearAddr();
void clearWorkspaceName();
using Repr = std::string;
using Repr = WindowRepr;
using ClassAndTitle = std::pair<std::string, std::string>;
std::variant<Repr, ClassAndTitle> m_window;
WindowAddress m_windowAddress;
std::string m_workspaceName;
bool m_isActive = false;
int m_timeSpentUncreated = 0;
};
+17 -9
View File
@@ -42,7 +42,6 @@ class Workspace {
bool isPersistentConfig() const { return m_isPersistentConfig; };
bool isPersistentRule() const { return m_isPersistentRule; };
bool isVisible() const { return m_isVisible; };
bool isEmpty() const { return m_windows == 0; };
bool isUrgent() const { return m_isUrgent; };
bool handleClicked(GdkEventButton* bt) const;
@@ -54,15 +53,18 @@ class Workspace {
void setWindows(uint value) { m_windows = value; };
void setName(std::string const& value) { m_name = value; };
void setOutput(std::string const& value) { m_output = value; };
bool containsWindow(WindowAddress const& addr) const { return m_windowMap.contains(addr); }
void insertWindow(WindowCreationPayload create_window_paylod);
std::string removeWindow(WindowAddress const& addr);
bool containsWindow(WindowAddress const& addr) const {
return std::ranges::any_of(m_windowMap,
[&addr](const auto& window) { return window.address == addr; });
};
void insertWindow(WindowCreationPayload create_window_payload);
void initializeWindowMap(const Json::Value& clients_data);
void setActiveWindow(WindowAddress const& addr);
bool onWindowOpened(WindowCreationPayload const& create_window_paylod);
std::optional<std::string> closeWindow(WindowAddress const& addr);
bool onWindowOpened(WindowCreationPayload const& create_window_payload);
std::optional<WindowRepr> closeWindow(WindowAddress const& addr);
void update(const std::string& format, const std::string& icon);
void update(const std::string& workspace_icon);
private:
Workspaces& m_workspaceManager;
@@ -78,11 +80,17 @@ class Workspace {
bool m_isUrgent = false;
bool m_isVisible = false;
std::map<WindowAddress, std::string> m_windowMap;
std::vector<WindowRepr> m_windowMap;
Gtk::Button m_button;
Gtk::Box m_content;
Gtk::Label m_label;
Gtk::Label m_labelBefore;
Gtk::Label m_labelAfter;
bool isEmpty() const;
void updateTaskbar(const std::string& workspace_icon);
bool handleClick(const GdkEventButton* event_button, WindowAddress const& addr) const;
bool shouldSkipWindow(const WindowRepr& window_repr) const;
IPC& m_ipc;
};
+49 -4
View File
@@ -1,6 +1,7 @@
#pragma once
#include <gtkmm/button.h>
#include <gtkmm/enums.h>
#include <gtkmm/label.h>
#include <json/value.h>
@@ -18,6 +19,7 @@
#include "modules/hyprland/windowcreationpayload.hpp"
#include "modules/hyprland/workspace.hpp"
#include "util/enum.hpp"
#include "util/icon_loader.hpp"
#include "util/regex_collection.hpp"
using WindowAddress = std::string;
@@ -37,19 +39,36 @@ class Workspaces : public AModule, public EventHandler {
auto showSpecial() const -> bool { return m_showSpecial; }
auto activeOnly() const -> bool { return m_activeOnly; }
auto specialVisibleOnly() const -> bool { return m_specialVisibleOnly; }
auto persistentOnly() const -> bool { return m_persistentOnly; }
auto moveToMonitor() const -> bool { return m_moveToMonitor; }
auto enableTaskbar() const -> bool { return m_enableTaskbar; }
auto taskbarWithIcon() const -> bool { return m_taskbarWithIcon; }
auto getBarOutput() const -> std::string { return m_bar.output->name; }
auto formatBefore() const -> std::string { return m_formatBefore; }
auto formatAfter() const -> std::string { return m_formatAfter; }
auto taskbarFormatBefore() const -> std::string { return m_taskbarFormatBefore; }
auto taskbarFormatAfter() const -> std::string { return m_taskbarFormatAfter; }
auto taskbarIconSize() const -> int { return m_taskbarIconSize; }
auto taskbarOrientation() const -> Gtk::Orientation { return m_taskbarOrientation; }
auto taskbarReverseDirection() const -> bool { return m_taskbarReverseDirection; }
auto onClickWindow() const -> std::string { return m_onClickWindow; }
auto getIgnoredWindows() const -> std::vector<std::regex> { return m_ignoreWindows; }
enum class ActiveWindowPosition { NONE, FIRST, LAST };
auto activeWindowPosition() const -> ActiveWindowPosition { return m_activeWindowPosition; }
std::string getRewrite(std::string window_class, std::string window_title);
std::string& getWindowSeparator() { return m_formatWindowSeparator; }
bool isWorkspaceIgnored(std::string const& workspace_name);
bool windowRewriteConfigUsesTitle() const { return m_anyWindowRewriteRuleUsesTitle; }
const IconLoader& iconLoader() const { return m_iconLoader; }
private:
void onEvent(const std::string& e) override;
void updateWindowCount();
void sortSpecialCentered();
void sortWorkspaces();
void createWorkspace(Json::Value const& workspace_data,
Json::Value const& clients_data = Json::Value::nullRef);
@@ -68,6 +87,7 @@ class Workspaces : public AModule, public EventHandler {
auto populateIgnoreWorkspacesConfig(const Json::Value& config) -> void;
auto populateFormatWindowSeparatorConfig(const Json::Value& config) -> void;
auto populateWindowRewriteConfig(const Json::Value& config) -> void;
auto populateWorkspaceTaskbarConfig(const Json::Value& config) -> void;
void registerIpc();
@@ -90,6 +110,7 @@ class Workspaces : public AModule, public EventHandler {
void onWindowMoved(std::string const& payload);
void onWindowTitleEvent(std::string const& payload);
void onActiveWindowChanged(WindowAddress const& payload);
void onConfigReloaded();
@@ -122,23 +143,26 @@ class Workspaces : public AModule, public EventHandler {
bool m_showSpecial = false;
bool m_activeOnly = false;
bool m_specialVisibleOnly = false;
bool m_persistentOnly = false;
bool m_moveToMonitor = false;
Json::Value m_persistentWorkspaceConfig;
// Map for windows stored in workspaces not present in the current bar.
// This happens when the user has multiple monitors (hence, multiple bars)
// and doesn't share windows accross bars (a.k.a `all-outputs` = false)
std::map<WindowAddress, std::string> m_orphanWindowMap;
// and doesn't share windows across bars (a.k.a `all-outputs` = false)
std::map<WindowAddress, WindowRepr, std::less<>> m_orphanWindowMap;
enum class SortMethod { ID, NAME, NUMBER, DEFAULT };
enum class SortMethod { ID, NAME, NUMBER, SPECIAL_CENTERED, DEFAULT };
util::EnumParser<SortMethod> m_enumParser;
SortMethod m_sortBy = SortMethod::DEFAULT;
std::map<std::string, SortMethod> m_sortMap = {{"ID", SortMethod::ID},
{"NAME", SortMethod::NAME},
{"NUMBER", SortMethod::NUMBER},
{"SPECIAL-CENTERED", SortMethod::SPECIAL_CENTERED},
{"DEFAULT", SortMethod::DEFAULT}};
std::string m_format;
std::string m_formatBefore;
std::string m_formatAfter;
std::map<std::string, std::string> m_iconsMap;
util::RegexCollection m_windowRewriteRules;
@@ -154,7 +178,28 @@ class Workspaces : public AModule, public EventHandler {
std::vector<std::string> m_workspacesToRemove;
std::vector<WindowCreationPayload> m_windowsToCreate;
IconLoader m_iconLoader;
bool m_enableTaskbar = false;
bool m_updateActiveWindow = false;
bool m_taskbarWithIcon = false;
bool m_taskbarWithTitle = false;
std::string m_taskbarFormatBefore;
std::string m_taskbarFormatAfter;
int m_taskbarIconSize = 16;
Gtk::Orientation m_taskbarOrientation = Gtk::ORIENTATION_HORIZONTAL;
bool m_taskbarReverseDirection = false;
util::EnumParser<ActiveWindowPosition> m_activeWindowEnumParser;
ActiveWindowPosition m_activeWindowPosition = ActiveWindowPosition::NONE;
std::map<std::string, ActiveWindowPosition> m_activeWindowPositionMap = {
{"NONE", ActiveWindowPosition::NONE},
{"FIRST", ActiveWindowPosition::FIRST},
{"LAST", ActiveWindowPosition::LAST},
};
std::string m_onClickWindow;
std::string m_currentActiveWindowAddress;
std::vector<std::regex> m_ignoreWorkspaces;
std::vector<std::regex> m_ignoreWindows;
std::mutex m_mutex;
const Bar& m_bar;
+1 -1
View File
@@ -31,7 +31,7 @@ class Image : public AModule {
std::string path_;
std::string tooltip_;
int size_;
int interval_;
std::chrono::milliseconds interval_;
util::command::res output_;
util::SleeperThread thread_;
+25 -24
View File
@@ -9,6 +9,7 @@
#include <sys/epoll.h>
#include <optional>
#include <vector>
#include "ALabel.hpp"
#include "util/sleeper_thread.hpp"
@@ -27,8 +28,8 @@ class Network : public ALabel {
auto update() -> void override;
private:
static const uint8_t MAX_RETRY = 5;
static const uint8_t EPOLL_MAX = 200;
static const uint8_t MAX_RETRY{5};
static const uint8_t EPOLL_MAX{200};
static int handleEvents(struct nl_msg*, void*);
static int handleEventsDone(struct nl_msg*, void*);
@@ -44,44 +45,44 @@ class Network : public ALabel {
void parseFreq(struct nlattr**);
void parseBssid(struct nlattr**);
bool associatedOrJoined(struct nlattr**);
bool checkInterface(std::string name);
bool matchInterface(const std::string& ifname, const std::vector<std::string>& altnames,
std::string& matched) const;
auto getInfo() -> void;
const std::string getNetworkState() const;
void clearIface();
bool wildcardMatch(const std::string& pattern, const std::string& text) const;
std::optional<std::pair<unsigned long long, unsigned long long>> readBandwidthUsage();
int ifid_;
ip_addr_pref addr_pref_;
struct sockaddr_nl nladdr_ = {0};
struct nl_sock* sock_ = nullptr;
struct nl_sock* ev_sock_ = nullptr;
int efd_;
int ev_fd_;
int nl80211_id_;
int ifid_{-1};
ip_addr_pref addr_pref_{ip_addr_pref::IPV4};
struct sockaddr_nl nladdr_{0};
struct nl_sock* sock_{nullptr};
struct nl_sock* ev_sock_{nullptr};
int efd_{-1};
int ev_fd_{-1};
int nl80211_id_{-1};
std::mutex mutex_;
bool want_route_dump_;
bool want_link_dump_;
bool want_addr_dump_;
bool dump_in_progress_;
bool is_p2p_;
bool want_route_dump_{false};
bool want_link_dump_{false};
bool want_addr_dump_{false};
bool dump_in_progress_{false};
bool is_p2p_{false};
unsigned long long bandwidth_down_total_;
unsigned long long bandwidth_up_total_;
unsigned long long bandwidth_down_total_{0};
unsigned long long bandwidth_up_total_{0};
std::string state_;
std::string essid_;
std::string bssid_;
bool carrier_;
bool carrier_{false};
std::string ifname_;
std::string ipaddr_;
std::string ipaddr6_;
std::string gwaddr_;
std::string netmask_;
std::string netmask6_;
int cidr_;
int cidr6_;
int cidr_{0};
int cidr6_{0};
int32_t signal_strength_dbm_;
uint8_t signal_strength_;
std::string signal_strength_app_;
@@ -90,9 +91,9 @@ class Network : public ALabel {
util::SleeperThread thread_;
util::SleeperThread thread_timer_;
#ifdef WANT_RFKILL
util::Rfkill rfkill_;
util::Rfkill rfkill_{RFKILL_TYPE_WLAN};
#endif
float frequency_;
float frequency_{0};
};
} // namespace waybar::modules
+1
View File
@@ -33,6 +33,7 @@ class Language : public ALabel, public EventHandler {
std::vector<Layout> layouts_;
unsigned current_idx_;
std::string last_short_name_;
};
} // namespace waybar::modules::niri
+2
View File
@@ -31,6 +31,8 @@ class Privacy : public AModule {
uint iconSpacing = 4;
uint iconSize = 20;
uint transition_duration = 250;
std::set<std::pair<PrivacyNodeType, std::string>> ignore;
bool ignore_monitor = true;
std::shared_ptr<util::PipewireBackend::PipewireBackend> backend = nullptr;
};
+1
View File
@@ -24,6 +24,7 @@ class Layout : public waybar::ALabel {
private:
const waybar::Bar &bar_;
std::string name_;
struct wl_output *output_; // stores the output this module belongs to
struct wl_output *focused_output_; // stores the currently focused output
struct zriver_output_status_v1 *output_status_;
+1
View File
@@ -21,6 +21,7 @@ class Tags : public waybar::AModule {
void handle_view_tags(struct wl_array *tags);
void handle_urgent_tags(uint32_t tags);
void handle_show();
void handle_primary_clicked(uint32_t tag);
bool handle_button_press(GdkEventButton *event_button, uint32_t tag);
+1 -1
View File
@@ -26,7 +26,7 @@ struct ToolTip {
class Item : public sigc::trackable {
public:
Item(const std::string&, const std::string&, const Json::Value&, const Bar&);
~Item() = default;
~Item();
std::string bus_name;
std::string object_path;
+1
View File
@@ -21,6 +21,7 @@ class Tray : public AModule {
void onRemove(std::unique_ptr<Item>& item);
static inline std::size_t nb_hosts_ = 0;
bool show_passive_ = false;
Gtk::Box box_;
SNI::Watcher::singleton watcher_;
SNI::Host host_;
+2 -2
View File
@@ -21,7 +21,7 @@ class Language : public ALabel, public sigc::trackable {
auto update() -> void override;
private:
enum class DispayedShortFlag { None = 0, ShortName = 1, ShortDescription = 1 << 1 };
enum class DisplayedShortFlag { None = 0, ShortName = 1, ShortDescription = 1 << 1 };
struct Layout {
std::string full_name;
@@ -58,7 +58,7 @@ class Language : public ALabel, public sigc::trackable {
std::map<std::string, Layout> layouts_map_;
bool hide_single_;
bool is_variant_displayed;
std::byte displayed_short_flag = static_cast<std::byte>(DispayedShortFlag::None);
std::byte displayed_short_flag = static_cast<std::byte>(DisplayedShortFlag::None);
util::JsonParser parser_;
std::mutex mutex_;
+4 -2
View File
@@ -19,10 +19,11 @@ class Window : public AAppIconLabel, public sigc::trackable {
auto update() -> void override;
private:
void setClass(std::string classname, bool enable);
void setClass(const std::string& classname, bool enable);
void onEvent(const struct Ipc::ipc_response&);
void onCmd(const struct Ipc::ipc_response&);
std::tuple<std::size_t, int, int, std::string, std::string, std::string, std::string, std::string>
std::tuple<std::size_t, int, int, std::string, std::string, std::string, std::string, std::string,
std::string>
getFocusedNode(const Json::Value& nodes, std::string& output);
void getTree();
@@ -35,6 +36,7 @@ class Window : public AAppIconLabel, public sigc::trackable {
std::string old_app_id_;
std::size_t app_nb_;
std::string shell_;
std::string marks_;
int floating_count_;
util::JsonParser parser_;
std::mutex mutex_;
+1 -1
View File
@@ -48,7 +48,7 @@ class Workspaces : public AModule, public sigc::trackable {
std::vector<std::string> high_priority_named_;
std::vector<std::string> workspaces_order_;
Gtk::Box box_;
std::string m_formatWindowSeperator;
std::string m_formatWindowSeparator;
util::RegexCollection m_windowRewriteRules;
util::JsonParser parser_;
std::unordered_map<std::string, Gtk::Button> buttons_;
+4 -1
View File
@@ -19,12 +19,15 @@ class SystemdFailedUnits : public ALabel {
std::string format_ok;
bool update_pending;
uint32_t nr_failed_system, nr_failed_user;
std::string system_state, user_state, overall_state;
uint32_t nr_failed_system, nr_failed_user, nr_failed;
std::string last_status;
Glib::RefPtr<Gio::DBus::Proxy> system_proxy, user_proxy;
void notify_cb(const Glib::ustring &sender_name, const Glib::ustring &signal_name,
const Glib::VariantContainerBase &arguments);
void RequestFailedUnits();
void RequestSystemState();
void updateData();
};
+122
View File
@@ -0,0 +1,122 @@
#pragma once
#include <json/json.h>
#include <unistd.h>
#include <functional>
#include <list>
#include <memory>
#include <mutex>
#include <optional>
#include <string>
#include <unordered_map>
#include <utility>
namespace waybar::modules::wayfire {
using EventHandler = std::function<void(const std::string& event)>;
struct State {
/*
┌───────────┐ ┌───────────┐
│ output #1 │ │ output #2 │
└─────┬─────┘ └─────┬─────┘
└─┐ └─────┐─ ─ ─ ─ ─ ─ ─ ─ ┐
┌───────┴───────┐ ┌───────┴──────┐ ┌───────┴───────┐
│ wset #1 │ │ wset #2 │ │ wset #3 │
│┌────────────┐ │ │┌────────────┐│ │┌────────────┐ │
││ workspaces │ │ ││ workspaces ││ ││ workspaces │ │
│└─┬──────────┘ │ │└────────────┘│ │└─┬──────────┘ │
│ │ ┌─────────┐│ └──────────────┘ │ │ ┌─────────┐│
│ ├─┤ view #1 ││ │ └─┤ view #3 ││
│ │ └─────────┘│ │ └─────────┘│
│ │ ┌─────────┐│ └───────────────┘
│ └─┤ view #2 ││
│ └─────────┘│
└───────────────┘
*/
struct Output {
size_t id;
size_t w, h;
size_t wset_idx;
};
struct Workspace {
size_t num_views;
size_t num_sticky_views;
};
struct Wset {
std::optional<std::reference_wrapper<Output>> output;
std::vector<Workspace> wss;
size_t ws_w, ws_h, ws_x, ws_y;
size_t focused_view_id;
auto ws_idx() const { return ws_w * ws_y + ws_x; }
auto count_ws(const Json::Value& pos) -> Workspace&;
auto locate_ws(const Json::Value& geo) -> Workspace&;
auto locate_ws(const Json::Value& geo) const -> const Workspace&;
};
std::unordered_map<std::string, Output> outputs;
std::unordered_map<size_t, Wset> wsets;
std::unordered_map<size_t, Json::Value> views;
std::string focused_output_name;
size_t maybe_empty_focus_wset_idx = {};
size_t vswitch_sticky_view_id = {};
bool new_output_detected = {};
bool vswitching = {};
auto update_view(const Json::Value& view) -> void;
};
struct Sock {
int fd;
Sock(int fd) : fd{fd} {}
~Sock() { close(fd); }
Sock(const Sock&) = delete;
auto operator=(const Sock&) = delete;
Sock(Sock&& rhs) noexcept {
fd = rhs.fd;
rhs.fd = -1;
}
auto& operator=(Sock&& rhs) noexcept {
fd = rhs.fd;
rhs.fd = -1;
return *this;
}
};
class IPC {
static std::weak_ptr<IPC> instance;
Json::CharReaderBuilder reader_builder;
Json::StreamWriterBuilder writer_builder;
std::list<std::pair<std::string, std::reference_wrapper<const EventHandler>>> handlers;
std::mutex handlers_mutex;
State state;
std::mutex state_mutex;
IPC() { start(); }
static auto connect() -> Sock;
auto receive(Sock& sock) -> Json::Value;
auto start() -> void;
auto root_event_handler(const std::string& event, const Json::Value& data) -> void;
auto update_state_handler(const std::string& event, const Json::Value& data) -> void;
public:
static auto get_instance() -> std::shared_ptr<IPC>;
auto send(const std::string& method, Json::Value&& data) -> Json::Value;
auto register_handler(const std::string& event, const EventHandler& handler) -> void;
auto unregister_handler(EventHandler& handler) -> void;
auto lock_state() -> std::lock_guard<std::mutex> { return std::lock_guard{state_mutex}; }
auto& get_outputs() const { return state.outputs; }
auto& get_wsets() const { return state.wsets; }
auto& get_views() const { return state.views; }
auto& get_focused_output_name() const { return state.focused_output_name; }
};
} // namespace waybar::modules::wayfire
+24
View File
@@ -0,0 +1,24 @@
#pragma once
#include "AAppIconLabel.hpp"
#include "bar.hpp"
#include "modules/wayfire/backend.hpp"
namespace waybar::modules::wayfire {
class Window : public AAppIconLabel {
std::shared_ptr<IPC> ipc;
EventHandler handler;
const Bar& bar_;
std::string old_app_id_;
public:
Window(const std::string& id, const Bar& bar, const Json::Value& config);
~Window() override;
auto update() -> void override;
auto update_icon_label() -> void;
};
} // namespace waybar::modules::wayfire
+32
View File
@@ -0,0 +1,32 @@
#pragma once
#include <gtkmm/button.h>
#include <json/json.h>
#include <memory>
#include <vector>
#include "AModule.hpp"
#include "bar.hpp"
#include "modules/wayfire/backend.hpp"
namespace waybar::modules::wayfire {
class Workspaces : public AModule {
std::shared_ptr<IPC> ipc;
EventHandler handler;
const Bar& bar_;
Gtk::Box box_;
std::vector<Gtk::Button> buttons_;
auto handleScroll(GdkEventScroll* e) -> bool override;
auto update() -> void override;
auto update_box() -> void;
public:
Workspaces(const std::string& id, const Bar& bar, const Json::Value& config);
~Workspaces() override;
};
} // namespace waybar::modules::wayfire
+7
View File
@@ -22,6 +22,8 @@ class Wireplumber : public ALabel {
void activatePlugins();
static void updateVolume(waybar::modules::Wireplumber* self, uint32_t id);
static void updateNodeName(waybar::modules::Wireplumber* self, uint32_t id);
static void updateSourceVolume(waybar::modules::Wireplumber* self, uint32_t id);
static void updateSourceName(waybar::modules::Wireplumber* self, uint32_t id); // NEW
static void onPluginActivated(WpObject* p, GAsyncResult* res, waybar::modules::Wireplumber* self);
static void onDefaultNodesApiLoaded(WpObject* p, GAsyncResult* res,
waybar::modules::Wireplumber* self);
@@ -46,7 +48,12 @@ class Wireplumber : public ALabel {
double min_step_;
uint32_t node_id_{0};
std::string node_name_;
std::string source_name_;
gchar* type_;
uint32_t source_node_id_;
bool source_muted_;
double source_volume_;
gchar* default_source_name_;
};
} // namespace waybar::modules
+3 -5
View File
@@ -19,6 +19,7 @@
#include "bar.hpp"
#include "client.hpp"
#include "giomm/desktopappinfo.h"
#include "util/icon_loader.hpp"
#include "util/json.hpp"
#include "wlr-foreign-toplevel-management-unstable-v1-client-protocol.h"
@@ -89,9 +90,6 @@ class Task {
std::string state_string(bool = false) const;
void set_minimize_hint();
void on_button_size_allocated(Gtk::Allocation &alloc);
void set_app_info_from_app_id_list(const std::string &app_id_list);
bool image_load_icon(Gtk::Image &image, const Glib::RefPtr<Gtk::IconTheme> &icon_theme,
Glib::RefPtr<Gio::DesktopAppInfo> app_info, int size);
void hide_if_ignored();
public:
@@ -153,7 +151,7 @@ class Taskbar : public waybar::AModule {
Gtk::Box box_;
std::vector<TaskPtr> tasks_;
std::vector<Glib::RefPtr<Gtk::IconTheme>> icon_themes_;
IconLoader icon_loader_;
std::unordered_set<std::string> ignore_list_;
std::map<std::string, std::string> app_ids_replace_map_;
@@ -178,7 +176,7 @@ class Taskbar : public waybar::AModule {
bool show_output(struct wl_output *) const;
bool all_outputs() const;
const std::vector<Glib::RefPtr<Gtk::IconTheme>> &icon_themes() const;
const IconLoader &icon_loader() const;
const std::unordered_set<std::string> &ignore_list() const;
const std::map<std::string, std::string> &app_ids_replace_map() const;
};
-172
View File
@@ -1,172 +0,0 @@
#pragma once
#include <fmt/format.h>
#include <gtkmm/button.h>
#include <gtkmm/image.h>
#include <gtkmm/label.h>
#include <functional>
#include <map>
#include <memory>
#include <vector>
#include "AModule.hpp"
#include "bar.hpp"
#include "ext-workspace-unstable-v1-client-protocol.h"
namespace waybar::modules::wlr {
class WorkspaceManager;
class WorkspaceGroup;
class Workspace {
public:
Workspace(const waybar::Bar &bar, const Json::Value &config, WorkspaceGroup &workspace_group,
zext_workspace_handle_v1 *workspace, uint32_t id, std::string name);
~Workspace();
auto update() -> void;
auto id() const -> uint32_t { return id_; }
auto is_active() const -> bool { return state_ & static_cast<uint32_t>(State::ACTIVE); }
auto is_urgent() const -> bool { return state_ & static_cast<uint32_t>(State::URGENT); }
auto is_hidden() const -> bool { return state_ & static_cast<uint32_t>(State::HIDDEN); }
auto is_empty() const -> bool { return state_ & static_cast<uint32_t>(State::EMPTY); }
auto is_persistent() const -> bool { return persistent_; }
// wlr stuff
auto handle_name(const std::string &name) -> void;
auto handle_coordinates(const std::vector<uint32_t> &coordinates) -> void;
auto handle_state(const std::vector<uint32_t> &state) -> void;
auto handle_remove() -> void;
auto make_persistent() -> void;
auto handle_duplicate() -> void;
auto handle_done() -> void;
auto handle_clicked(GdkEventButton *bt) -> bool;
auto show() -> void;
auto hide() -> void;
auto get_button_ref() -> Gtk::Button & { return button_; }
auto get_name() -> std::string & { return name_; }
auto get_coords() -> std::vector<uint32_t> & { return coordinates_; }
enum class State {
ACTIVE = (1 << 0),
URGENT = (1 << 1),
HIDDEN = (1 << 2),
EMPTY = (1 << 3),
};
private:
auto get_icon() -> std::string;
const Bar &bar_;
const Json::Value &config_;
WorkspaceGroup &workspace_group_;
// wlr stuff
zext_workspace_handle_v1 *workspace_handle_;
uint32_t state_ = 0;
uint32_t id_;
std::string name_;
std::vector<uint32_t> coordinates_;
static std::map<std::string, std::string> icons_map_;
std::string format_;
bool with_icon_ = false;
bool persistent_ = false;
Gtk::Button button_;
Gtk::Box content_;
Gtk::Label label_;
};
class WorkspaceGroup {
public:
WorkspaceGroup(const waybar::Bar &bar, Gtk::Box &box, const Json::Value &config,
WorkspaceManager &manager, zext_workspace_group_handle_v1 *workspace_group_handle,
uint32_t id);
~WorkspaceGroup();
auto update() -> void;
auto id() const -> uint32_t { return id_; }
auto is_visible() const -> bool;
auto remove_workspace(uint32_t id_) -> void;
auto active_only() const -> bool;
auto creation_delayed() const -> bool;
auto workspaces() -> std::vector<std::unique_ptr<Workspace>> & { return workspaces_; }
auto persistent_workspaces() -> std::vector<std::string> & { return persistent_workspaces_; }
auto sort_workspaces() -> void;
auto set_need_to_sort() -> void { need_to_sort = true; }
auto add_button(Gtk::Button &button) -> void;
auto remove_button(Gtk::Button &button) -> void;
auto fill_persistent_workspaces() -> void;
auto create_persistent_workspaces() -> void;
// wlr stuff
auto handle_workspace_create(zext_workspace_handle_v1 *workspace_handle) -> void;
auto handle_remove() -> void;
auto handle_output_enter(wl_output *output) -> void;
auto handle_output_leave() -> void;
auto handle_done() -> void;
auto commit() -> void;
private:
static uint32_t workspace_global_id;
const waybar::Bar &bar_;
Gtk::Box &box_;
const Json::Value &config_;
WorkspaceManager &workspace_manager_;
// wlr stuff
zext_workspace_group_handle_v1 *workspace_group_handle_;
wl_output *output_ = nullptr;
uint32_t id_;
std::vector<std::unique_ptr<Workspace>> workspaces_;
bool need_to_sort = false;
std::vector<std::string> persistent_workspaces_;
bool persistent_created_ = false;
};
class WorkspaceManager : public AModule {
public:
WorkspaceManager(const std::string &id, const waybar::Bar &bar, const Json::Value &config);
~WorkspaceManager() override;
auto update() -> void override;
auto all_outputs() const -> bool { return all_outputs_; }
auto active_only() const -> bool { return active_only_; }
auto workspace_comparator() const
-> std::function<bool(std::unique_ptr<Workspace> &, std::unique_ptr<Workspace> &)>;
auto creation_delayed() const -> bool { return creation_delayed_; }
auto sort_workspaces() -> void;
auto remove_workspace_group(uint32_t id_) -> void;
// wlr stuff
auto register_manager(wl_registry *registry, uint32_t name, uint32_t version) -> void;
auto handle_workspace_group_create(zext_workspace_group_handle_v1 *workspace_group_handle)
-> void;
auto handle_done() -> void;
auto handle_finished() -> void;
auto commit() -> void;
private:
const waybar::Bar &bar_;
Gtk::Box box_;
std::vector<std::unique_ptr<WorkspaceGroup>> groups_;
// wlr stuff
zext_workspace_manager_v1 *workspace_manager_ = nullptr;
static uint32_t group_global_id;
bool sort_by_name_ = true;
bool sort_by_coordinates_ = true;
bool sort_by_number_ = false;
bool all_outputs_ = false;
bool active_only_ = false;
bool creation_delayed_ = false;
};
} // namespace waybar::modules::wlr
@@ -1,10 +0,0 @@
#include "ext-workspace-unstable-v1-client-protocol.h"
namespace waybar::modules::wlr {
void add_registry_listener(void *data);
void add_workspace_listener(zext_workspace_handle_v1 *workspace_handle, void *data);
void add_workspace_group_listener(zext_workspace_group_handle_v1 *workspace_group_handle,
void *data);
zext_workspace_manager_v1 *workspace_manager_bind(wl_registry *registry, uint32_t name,
uint32_t version, void *data);
} // namespace waybar::modules::wlr
+5
View File
@@ -11,6 +11,11 @@
#include <type_traits>
#include <utility>
#ifdef __OpenBSD__
#define SIGRTMIN SIGUSR1 - 1
#define SIGRTMAX SIGUSR1 + 1
#endif
namespace waybar {
/**
+1 -3
View File
@@ -15,7 +15,7 @@
namespace date {
#if HAVE_CHRONO_TIMEZONES
using namespace std::chrono;
using namespace std;
using std::format;
#else
using system_clock = std::chrono::system_clock;
@@ -73,5 +73,3 @@ struct fmt::formatter<date::zoned_time<Duration, TimeZonePtr>> {
}
};
#endif
using namespace date;
+3 -1
View File
@@ -10,5 +10,7 @@ class DefaultGtkIconThemeWrapper {
public:
static bool has_icon(const std::string&);
static Glib::RefPtr<Gdk::Pixbuf> load_icon(const char*, int, Gtk::IconLookupFlags);
static Glib::RefPtr<Gdk::Pixbuf> load_icon(
const char*, int, Gtk::IconLookupFlags,
Glib::RefPtr<Gtk::StyleContext> style = Glib::RefPtr<Gtk::StyleContext>());
};
+34
View File
@@ -0,0 +1,34 @@
#pragma once
#include <gdkmm/general.h>
#include <gio/gdesktopappinfo.h>
#include <giomm/desktopappinfo.h>
#include <glibmm/fileutils.h>
#include <gtkmm/image.h>
#include <spdlog/spdlog.h>
#include <string>
#include <vector>
#include "util/gtk_icon.hpp"
class IconLoader {
private:
std::vector<Glib::RefPtr<Gtk::IconTheme>> custom_icon_themes_;
Glib::RefPtr<Gtk::IconTheme> default_icon_theme_ = Gtk::IconTheme::get_default();
static std::vector<std::string> search_prefix();
static Glib::RefPtr<Gio::DesktopAppInfo> get_app_info_by_name(const std::string &app_id);
static Glib::RefPtr<Gio::DesktopAppInfo> get_desktop_app_info(const std::string &app_id);
static Glib::RefPtr<Gdk::Pixbuf> load_icon_from_file(std::string const &icon_path, int size);
static std::string get_icon_name_from_icon_theme(const Glib::RefPtr<Gtk::IconTheme> &icon_theme,
const std::string &app_id);
static bool image_load_icon(Gtk::Image &image, const Glib::RefPtr<Gtk::IconTheme> &icon_theme,
Glib::RefPtr<Gio::DesktopAppInfo> app_info, int size);
public:
void add_custom_icon_theme(const std::string &theme_name);
bool image_load_icon(Gtk::Image &image, Glib::RefPtr<Gio::DesktopAppInfo> app_info,
int size) const;
static Glib::RefPtr<Gio::DesktopAppInfo> get_app_info_from_app_id_list(
const std::string &app_id_list);
};
+26
View File
@@ -0,0 +1,26 @@
#pragma once
#include <json/value.h>
#include <cstdint>
namespace waybar::util {
enum class KillSignalAction : std::uint8_t {
TOGGLE,
RELOAD,
SHOW,
HIDE,
NOOP,
};
const std::map<std::string, KillSignalAction> userKillSignalActions = {
{"TOGGLE", KillSignalAction::TOGGLE},
{"RELOAD", KillSignalAction::RELOAD},
{"SHOW", KillSignalAction::SHOW},
{"HIDE", KillSignalAction::HIDE},
{"NOOP", KillSignalAction::NOOP}};
const KillSignalAction SIGNALACTION_DEFAULT_SIGUSR1 = KillSignalAction::TOGGLE;
const KillSignalAction SIGNALACTION_DEFAULT_SIGUSR2 = KillSignalAction::RELOAD;
}; // namespace waybar::util
@@ -25,6 +25,7 @@ class PrivacyNodeInfo {
std::string media_name;
std::string node_name;
std::string application_name;
bool is_monitor = false;
std::string pipewire_access_portal_app_id;
std::string application_icon_name;
+1 -3
View File
@@ -6,14 +6,12 @@
namespace waybar {
using namespace Gio;
enum class Appearance {
UNKNOWN = 0,
DARK = 1,
LIGHT = 2,
};
class Portal : private DBus::Proxy {
class Portal : private Gio::DBus::Proxy {
public:
Portal();
void refreshAppearance();
+1 -1
View File
@@ -4,6 +4,6 @@
namespace waybar::util {
// Get a signal emited with value true when entering sleep, and false when exiting
// Get a signal emitted with value true when entering sleep, and false when exiting
SafeSignal<bool>& prepare_for_sleep();
} // namespace waybar::util
+3 -1
View File
@@ -5,6 +5,8 @@
#include <sigc++/signal.h>
#include <sigc++/trackable.h>
#include <atomic>
namespace waybar::util {
class Rfkill : public sigc::trackable {
@@ -17,7 +19,7 @@ class Rfkill : public sigc::trackable {
private:
enum rfkill_type rfkill_type_;
bool state_ = false;
std::atomic_bool state_ = false;
int fd_ = -1;
bool on_event(Glib::IOCondition cond);
+23
View File
@@ -23,3 +23,26 @@ inline std::string capitalize(const std::string& str) {
[](unsigned char c) { return std::toupper(c); });
return result;
}
inline std::string toLower(const std::string& str) {
std::string result = str;
std::transform(result.begin(), result.end(), result.begin(),
[](unsigned char c) { return std::tolower(c); });
return result;
}
inline std::vector<std::string> split(std::string_view s, std::string_view delimiter,
int max_splits = -1) {
std::vector<std::string> result;
size_t pos = 0;
size_t next_pos = 0;
while ((next_pos = s.find(delimiter, pos)) != std::string::npos) {
result.push_back(std::string(s.substr(pos, next_pos - pos)));
pos = next_pos + delimiter.size();
if (max_splits > 0 && result.size() == static_cast<size_t>(max_splits)) {
break;
}
}
result.push_back(std::string(s.substr(pos)));
return result;
}
+23 -1
View File
@@ -116,7 +116,7 @@ The *battery* module displays the current capacity and state (eg. charging) of y
*menu-file*: ++
typeof: string ++
Location of the menu descriptor file. There need to be an element of type
GtkMenu with id *menu*
GtkMenu with id *menu*.
*menu-actions*: ++
typeof: array ++
@@ -127,6 +127,10 @@ The *battery* module displays the current capacity and state (eg. charging) of y
default: false ++
Enables this module to consume all left over space dynamically.
*events*: ++
typeof: object ++
Specifies commands to be executed on specific battery states. See *EVENTS* section below.
# FORMAT REPLACEMENTS
*{capacity}*: Capacity in percentage
@@ -166,6 +170,19 @@ The *battery* module allows one to define custom formats based on up to two fact
- The state can be addressed as a CSS class in the *style.css*. The name of the CSS class is the *<name>* of the state. Each class gets activated when the current capacity is equal to or below the configured *<value>*.
- Also each state can have its own *format*. Those can be configured via *format-<name>*. Or if you want to differentiate a bit more even as *format-<status>-<state>*. For more information see *custom-formats*.
# EVENTS
Every entry in the *events* object consists of a *<event-name>* (typeof: *string*) and a *<command>* (typeof: *string*). ++
*<event-name>* can be in one of the following formats:
- *on-<status>-<state>*
- *on-<status>-<capacity>*
Where:
- *<status>* is either *charging* or *discharging*,
- *<state>* is the name of one of the states specified in the *states* object,
- *<capacity>* is a battery level value (between *0-100*).
# EXAMPLES
@@ -178,6 +195,11 @@ The *battery* module allows one to define custom formats based on up to two fact
"warning": 30,
"critical": 15
},
"events": {
"on-discharging-warning": "notify-send -u normal 'Low Battery'",
"on-discharging-critical": "notify-send -u critical 'Very Low Battery'",
"on-charging-100": "notify-send -u normal 'Battery Full!'"
},
"format": "{capacity}% {icon}",
"format-icons": ["", "", "", "", ""],
"max-length": 25
+31
View File
@@ -39,6 +39,12 @@ $XDG_CONFIG_HOME/waybar/config ++
:[ A list of timezones (as in *timezone*) to use for time display, changed using
the scroll wheel. Do not specify *timezone* option when *timezones* is specified.
"" represents the system's local timezone
|[ *timezone-tooltip-format*
:[ string
:[
:[ Format to use for displaying timezones in the tooltip. When set, this allows showing
timezone information (like timezone abbreviations) in the tooltip while keeping the
main display clean. Uses the same format options as *format*
|[ *locale*
:[ string
:[
@@ -126,6 +132,12 @@ View all valid format options in *strftime(3)* or have a look https://en.cpprefe
:[ 1
:[ Value to scroll months/years forward/backward. Can be negative. Is
configured under *on-scroll* option
|[ *iso8601*
:[ bool
:[ false
:[ When enabled, the calendar follows the ISO 8601 standard: weeks begin on
Monday, and the first week of the year is numbered 1. The default week format is
'{:%V}'.
3. Addressed by *clock: calendar: format*
[- *Option*
@@ -229,6 +241,25 @@ View all valid format options in *strftime(3)* or have a look https://en.cpprefe
}
```
4. Show timezone in tooltip only
```
"clock": {
"interval": 60,
"format": "{:%H:%M}",
"timezone-tooltip-format": "{:%H:%M %Z}",
"timezones": [
"",
"America/Chicago",
"America/Los_Angeles",
"Europe/Paris",
"UTC"
],
"tooltip": true,
"tooltip-format": "{tz_list}"
}
```
# STYLE
- *#clock*
+3 -2
View File
@@ -11,9 +11,10 @@ The *cpu* module displays the current CPU utilization.
# CONFIGURATION
*interval*: ++
typeof: integer ++
typeof: integer or float ++
default: 10 ++
The interval in which the information gets polled.
The interval in which the information gets polled. ++
Minimum value is 0.001 (1ms). Values smaller than 1ms will be set to 1ms.
*format*: ++
typeof: string ++
+4 -2
View File
@@ -35,15 +35,17 @@ Addressed by *custom/<name>*
See *return-type*
*interval*: ++
typeof: integer ++
typeof: integer or float ++
The interval (in seconds) in which the information gets polled. ++
Minimum value is 0.001 (1ms). Values smaller than 1ms will be set to 1ms. ++
Use *once* if you want to execute the module only on startup. ++
You can update it manually with a signal. If no *interval* or *signal* is defined, it is assumed that the out script loops itself. ++
If a *signal* is defined then the script will run once on startup and will only update with a signal.
*restart-interval*: ++
typeof: integer ++
typeof: integer or float ++
The restart interval (in seconds). ++
Minimum value is 0.001 (1ms). Values smaller than 1ms will be set to 1ms. ++
Can't be used with the *interval* option, so only with continuous scripts. ++
Once the script exits, it'll be re-executed after the *restart-interval*.
@@ -10,7 +10,7 @@ The *workspaces* module displays the currently used workspaces in wayland compos
# CONFIGURATION
Addressed by *wlr/workspaces*
Addressed by *ext/workspaces*
*format*: ++
typeof: string ++
@@ -24,18 +24,18 @@ Addressed by *wlr/workspaces*
*sort-by-name*: ++
typeof: bool ++
default: true ++
Should workspaces be sorted by name.
Should workspaces be sorted by name. Workspace names will be sorted numerically when all names are numbers.
*sort-by-coordinates*: ++
typeof: bool ++
default: true ++
default: false ++
Should workspaces be sorted by coordinates. ++
Note that if both *sort-by-name* and *sort-by-coordinates* are true sort-by name will be first. If both are false - sort by id will be performed.
*sort-by-number*: ++
*sort-by-id*: ++
typeof: bool ++
default: false ++
If set to true, workspace names will be sorted numerically. Takes precedence over any other sort-by option.
Should workspaces be sorted by ID. Workspace ID will be sorted numerically when all ID are numbers. Takes precedence over any other sort-by option.
*all-outputs*: ++
typeof: bool ++
@@ -47,9 +47,16 @@ Addressed by *wlr/workspaces*
default: false ++
If set to true only active or urgent workspaces will be shown.
*ignore-hidden*: ++
typeof: bool ++
default: true ++
If set to false hidden workspaces will be shown.
# FORMAT REPLACEMENTS
*{name}*: Name of workspace assigned by compositor
*{name}*: Name of workspace assigned by compositor.
*{id}*: ID of workspace assigned by compositor.
*{icon}*: Icon, as defined in *format-icons*.
@@ -57,6 +64,8 @@ Addressed by *wlr/workspaces*
*activate*: Switch to workspace.
*deactivate*: Deactivate the workspace.
*close*: Close the workspace.
# ICONS
@@ -69,18 +78,18 @@ In addition to workspace name matching, the following *format-icons* can be set.
# EXAMPLES
```
"wlr/workspaces": {
"ext/workspaces": {
"format": "{name}: {icon}",
"on-click": "activate",
"format-icons": {
"1": "",
"2": "",
"3": "",
"4": "",
"5": "",
"Workspace 1": "",
"Workspace 2": "",
"Workspace 3": "",
"Workspace 4": "",
"active": "",
"default": ""
},
"sort-by-number": true
"sort-by-id": true
}
```
+111
View File
@@ -0,0 +1,111 @@
waybar-gps(5) "waybar-gps" "User Manual"
# NAME
waybar - gps module
# DESCRIPTION
*gps* module for gpsd.
# FILES
$XDG_CONFIG_HOME/waybar/config ++
Per user configuration file
# ADDITIONAL FILES
libgps lives in:
. /usr/lib/libgps.so or /usr/lib64/libgps.so
. /usr/lib/pkgconfig/libgps.pc or /usr/lib64/pkgconfig/libgps.pc
. /usr/include/gps
# CONFIGURATION
*format*: ++
typeof: string ++
default: {glyph} ++
The text format.
*tooltip*: ++
typeof: bool ++
default: true ++
Option to disable tooltip on hover.
*tooltip-format*: ++
typeof: string ++
default: Games running: {glyph} ++
The text format of the tooltip.
*interval*: ++
typeof: integer ++
default: 5 ++
The interval in which the GPS information gets polled (e.g. current speed).
Significant updates (e.g. the current fix mode) are updated immediately.
*hide-disconnected*: ++
typeof: bool ++
default: true ++
Defines if the module should be hidden if there is no GPS receiver.
*hide-no-fix*: ++
typeof: bool ++
default: false ++
Defines if the module should be hidden if there is no GPS fix.
# FORMAT REPLACEMENTS
*{mode}*: Fix mode
*{status}*: Technology used for GPS fix. Not all GPS receivers report this.
*{latitude}*: Latitude, decimal degrees. Can be NaN.
*{latitude_error}*: Latitude uncertainty, meters. Can be NaN.
*{longitude}*: Longitude, decimal degrees. Can be NaN.
*{longitude_error}*: Longitude uncertainty, meters. Can be NaN.
*{altitude_hae}*: Altitude, height above ellipsoid, meters. Can be NaN.
*{altitude_msl}*: Longitude, MSL, meters. Can be NaN.
*{altitude_error}*: Altitude uncertainty, meters. Can be NaN.
*{speed}*: Speed over ground, meters/sec. Can be NaN.
*{speed_error}*: Speed uncertainty, meters/sec. Can be NaN.
*{climb}*: Vertical speed, meters/sec. Can be NaN.
*{climb_error}*: Vertical speed uncertainty, meters/sec. Can be NaN.
*{satellites_visible}*: Number of satellites visible from the GPS receiver.
*{satellites_used}*: Number of satellites used for the GPS fix.
# EXAMPLES
```
"gps": {
"format": "{mode}",
"format-disabled": "", // an empty format will hide the module
"format-no-fix": "No fix",
"format-fix-3d": "{status}",
"tooltip-format": "{mode}",
"tooltip-format-no-fix": "{satellites_visible} satellites visible",
"tooltip-format-fix-2d": "{satellites_used}/{satellites_visible} satellites used",
"tooltip-format-fix-3d": "Altitude: {altitude_hae}m",
"hide-disconnected": false
}
```
# STYLE
- *#gps*
- *#gps.disabled* Applied when GPS is disabled.
- *#gps.fix-none* Applied when GPS is present, but there is no fix.
- *#gps.fix-2d* Applied when there is a 2D fix.
- *#gps.fix-3d* Applied when there is a 3D fix.
+46
View File
@@ -0,0 +1,46 @@
waybar-hyprland-windowcount(5)
# NAME
waybar - hyprland window count module
# DESCRIPTION
The *windowcount* module displays the number of windows in the current Hyprland workspace.
# CONFIGURATION
Addressed by *hyprland/windowcount*
*format*: ++
typeof: string ++
default: {} ++
The format for how information should be displayed. On {} the current workspace window count is displayed.
*format-empty*: ++
typeof: string ++
Override the format when the workspace contains no windows window
*format-windowed*: ++
typeof: string ++
Override the format when the workspace contains no fullscreen windows
*format-fullscreen*: ++
typeof: string ++
Override the format when the workspace contains a fullscreen window
*separate-outputs*: ++
typeof: bool ++
default: true ++
Show the active workspace window count of the monitor the bar belongs to, instead of the focused workspace.
# STYLE
- *#windowcount*
The following classes are applied to the entire Waybar rather than just the
windowcount widget:
- *window#waybar.empty* When no windows are in the workspace
- *window#waybar.fullscreen* When there is a fullscreen window in the workspace;
useful with Hyprland's *fullscreen, 1* mode
+73 -4
View File
@@ -26,17 +26,77 @@ Addressed by *hyprland/workspaces*
Regex rules to map window class to an icon or preferred method of representation for a workspace's window.
Keys are the rules, while the values are the methods of representation. Values may use the placeholders {class} and {title} to use the window's original class and/or title respectively.
Rules may specify `class<...>`, `title<...>`, or both in order to fine-tune the matching.
You may assign an empty value to a rule to have it ignored from generating any representation in workspaces.
You may assign an empty value to a rule to have it ignored from generating any representation in workspaces. ++
This setting is ignored if *workspace-taskbar.enable* is set to true.
*window-rewrite-default*:
*window-rewrite-default*: ++
typeof: string ++
default: "?" ++
The default method of representation for a workspace's window. This will be used for windows whose classes do not match any of the rules in *window-rewrite*.
The default method of representation for a workspace's window. This will be used for windows whose classes do not match any of the rules in *window-rewrite*. ++
This setting is ignored if *workspace-taskbar.enable* is set to true.
*format-window-separator*: ++
typeof: string ++
default: " " ++
The separator to be used between windows in a workspace.
The separator to be used between windows in a workspace. ++
This setting is ignored if *workspace-taskbar.enable* is set to true.
*workspace-taskbar*: ++
typeof: object ++
Contains settings for the workspace taskbar, an alternative mode for the workspaces module which displays the window icons as images instead of text.
*enable*: ++
typeof: bool ++
default: false ++
Enables the workspace taskbar mode.
*update-active-window*: ++
typeof: bool ++
default: false ++
If true, the active/focused window will have an 'active' class. Could cause higher CPU usage due to more frequent redraws.
*reverse-direction*: ++
typeof: bool ++
default: false ++
If true, the taskbar windows will be added in reverse order (right to left if orientation is horizontal, bottom to top if vertical).
*active-window-position*: ++
typeof: "none" | "first" | "last" ++
default: "none" ++
If set to "first", the active window will be moved at the beginning of the taskbar. If set to "last", it will be moved at the end. It will only work if *update-active-window* is set to true.
*format*: ++
typeof: string ++
default: {icon} ++
Format to use for each window in the workspace taskbar. Available placeholders are {icon} and {title}.
*icon-size*: ++
typeof: int ++
default: 16 ++
Size of the icons in the workspace taskbar.
*icon-theme*: ++
typeof: string | array ++
default: [] ++
Icon theme to use for the workspace taskbar. If an array is provided, the first theme that is found for a given icon will be used. If no theme is found (or the array is empty), the default icon theme is used.
*orientation*: ++
typeof: "horizontal" | "vertical" ++
default: horizontal ++
Direction in which the workspace taskbar is displayed.
*ignore-list*: ++
typeof: array ++
default: [] ++
Regex patterns to match against window class or window title. If a window's class OR title matches any of the patterns, it will not be shown.
*on-click-window*: ++
typeof: string ++
default: "" ++
Command to run when a window is clicked. Available placeholders are: ++
- {address} Hyprland address of the clicked window. ++
- {button} Pressed button number, see https://api.gtkd.org/gdk.c.types.GdkEventButton.button.html. ++
See https://github.com/Alexays/Waybar/wiki/Module:-Hyprland#workspace-taskbars-example for a full example.
*show-special*: ++
typeof: bool ++
@@ -48,6 +108,11 @@ Addressed by *hyprland/workspaces*
default: false ++
If this and show-special are to true, special workspaces will be shown only if visible.
*persistent-only*: ++
typeof: bool ++
default: false ++
If set to true, only persistent workspaces will be shown on bar.
*all-outputs*: ++
typeof: bool ++
default: false ++
@@ -76,6 +141,7 @@ Addressed by *hyprland/workspaces*
If set to number, workspaces will sort by number.
If set to name, workspaces will sort by name.
If set to id, workspaces will sort by id.
If set to special-centered, workspaces will sort by default with special workspaces in the center.
If none of those, workspaces will sort with default behavior.
*expand*: ++
@@ -178,3 +244,6 @@ Additional to workspace name matching, the following *format-icons* can be set.
- *#workspaces button.special*
- *#workspaces button.urgent*
- *#workspaces button.hosting-monitor* (gets applied if workspace-monitor == waybar-monitor)
- *#workspaces .workspace-label*
- *#workspaces .taskbar-window* (each window in the taskbar, only if 'workspace-taskbar.enable' is true)
- *#workspaces .taskbar-window.active* (applied to the focused window, only if 'workspace-taskbar.update-active-window' is true)
+6
View File
@@ -125,3 +125,9 @@ screensaver, also known as "presentation mode".
"timeout": 30.5
}
```
# STYLE
- *#idle_inhibitor*
- *#idle_inhibitor.activated*
- *#idle_inhibitor.deactivated*
+2 -1
View File
@@ -24,8 +24,9 @@ The *image* module displays an image from a path.
The width/height to render the image.
*interval*: ++
typeof: integer ++
typeof: integer or float ++
The interval (in seconds) to re-render the image. ++
Minimum value is 0.001 (1ms). Values smaller than 1ms will be set to 1ms. ++
This is useful if the contents of *path* changes. ++
If no *interval* is defined, the image will only be rendered once.
+1 -1
View File
@@ -45,7 +45,7 @@ Addressed by *jack*
The format of information displayed in the tooltip.
*interval*: ++
typeof: integer ++
typeof: integer or float ++
default: 1 ++
The interval in which the information gets polled.
+2
View File
@@ -120,6 +120,8 @@ Addressed by *memory*
*{swapAvail}*: Amount of available swap in GiB.
*{swapState}*: Signals if swap is activated or not
# EXAMPLES
```
+26 -26
View File
@@ -7,7 +7,7 @@ waybar - menu property
# OVERVIEW
Some modules support a 'menu', which allows to have a popup menu whan a defined
Some modules support a 'menu', which allows to have a popup menu when a defined
click is done over the module.
# PROPERTIES
@@ -87,15 +87,15 @@ Module config :
```
"custom/power": {
"format" : "⏻ ",
"tooltip": false,
"menu": "on-click",
"menu-file": "~/.config/waybar/power_menu.xml",
"menu-actions": {
"shutdown": "shutdown",
"reboot": "reboot",
"suspend": "systemctl suspend",
"hibernate": "systemctl hibernate",
},
"tooltip": false,
"menu": "on-click",
"menu-file": "~/.config/waybar/power_menu.xml",
"menu-actions": {
"shutdown": "shutdown",
"reboot": "reboot",
"suspend": "systemctl suspend",
"hibernate": "systemctl hibernate",
},
},
```
@@ -104,28 +104,28 @@ Module config :
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<object class="GtkMenu" id="menu">
<child>
<object class="GtkMenuItem" id="suspend">
<property name="label">Suspend</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="hibernat">
<property name="label">Hibernate</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="suspend">
<property name="label">Suspend</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="hibernate">
<property name="label">Hibernate</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="shutdown">
<property name="label">Shutdown</property>
</object>
<object class="GtkMenuItem" id="shutdown">
<property name="label">Shutdown</property>
</object>
</child>
<child>
<object class="GtkSeparatorMenuItem" id="delimiter1"/>
</child>
<child>
<object class="GtkMenuItem" id="reboot">
<property name="label">Reboot</property>
</object>
<object class="GtkMenuItem" id="reboot">
<property name="label">Reboot</property>
</object>
</child>
</object>
</interface>
+4
View File
@@ -204,6 +204,10 @@ Addressed by *mpd*
*{queueLength}*: The length of the current queue.
*{uri}*: The URI of the song relative to the MPD music directory.
*{filename}* The last part of the URI.
*{stateIcon}*: The icon corresponding to the playing or paused status of the player (see *state-icons* option)
*{consumeIcon}*: The icon corresponding the "consume" option (see *consume-icons* option)
+9 -4
View File
@@ -16,6 +16,11 @@ Addressed by *network*
typeof: string ++
Use the defined interface instead of auto-detection. Accepts wildcard.
*rfkill*: ++
typeof: bool ++
default: true ++
If enabled, the *disabled* format will be used when rfkill is blocking wlan interfaces.
*interval*: ++
typeof: integer ++
default: 60 ++
@@ -49,7 +54,7 @@ Addressed by *network*
*format-disabled*: ++
typeof: string ++
This format is used when the displayed interface is disabled.
This format is used when rfkill is blocking wlan interfaces.
*format-icons*: ++
typeof: array/object ++
@@ -127,7 +132,7 @@ Addressed by *network*
*tooltip-format-disabled*: ++
typeof: string ++
This format is used when the displayed interface is disabled.
This format is used when rfkill is blocking wlan interfaces.
*menu*: ++
typeof: string ++
@@ -157,7 +162,7 @@ Addressed by *network*
*{netmask}*: The subnetmask corresponding to the IP(V4).
*{netmask6}*: The subnetmask corresponding to the IP(V6).
*{netmask6}*: The subnetmask corresponding to the IP(V6).
*{cidr}*: The subnetmask corresponding to the IP(V4) in CIDR notation.
@@ -171,7 +176,7 @@ Addressed by *network*
*{signaldBm}*: Signal strength of the wireless network in dBm.
*{frequency}*: Frequency of the wireless network in MHz.
*{frequency}*: Frequency of the wireless network in GHz.
*{bandwidthUpBits}*: Instant up speed in bits/seconds.
+11 -2
View File
@@ -52,8 +52,8 @@ Addressed by *niri/language*
```
"niri/language": {
"format": "Lang: {long}"
"format-en": "AMERICA, HELL YEAH!"
"format": "Lang: {long}",
"format-en": "AMERICA, HELL YEAH!",
"format-tr": "As bayrakları"
}
```
@@ -61,3 +61,12 @@ Addressed by *niri/language*
# STYLE
- *#language*
Additionally, a CSS class matching the current layout's short name is added to the widget. This
allows per-language styling, for example:
```
#language.us { color: #00ff00; }
#language.de { color: #ff0000; }
#language.fr { color: #0000ff; }
```
+3
View File
@@ -70,6 +70,8 @@ Additional to workspace name matching, the following *format-icons* can be set.
- *default*: Will be shown, when no string matches are found.
- *focused*: Will be shown, when workspace is focused.
- *active*: Will be shown, when workspace is active on its output.
- *urgent*: Will be shown, when workspace has urgent windows.
- *empty*: Will be shown, when workspace is empty.
# EXAMPLES
@@ -95,6 +97,7 @@ Additional to workspace name matching, the following *format-icons* can be set.
- *#workspaces button*
- *#workspaces button.focused*: The single focused workspace.
- *#workspaces button.active*: The workspace is active (visible) on its output.
- *#workspaces button.urgent*: The workspace has one or more urgent windows.
- *#workspaces button.empty*: The workspace is empty.
- *#workspaces button.current_output*: The workspace is from the same output as
the bar that it is displayed on.
+30
View File
@@ -37,6 +37,17 @@ the screen or playing audio.
default: false ++
Enables this module to consume all left over space dynamically.
*ignore-monitor* ++
typeof: bool ++
default: true ++
Ignore streams with *stream.monitor* property.
*ignore* ++
typeof: array of objects ++
default: [] ++
Additional streams to be ignored. See *IGNORE CONFIGURATION* for++
more information.
# MODULES CONFIGURATION
*type*: ++
@@ -54,6 +65,14 @@ the screen or playing audio.
default: 24 ++
The size of each icon in the tooltip.
# IGNORE CONFIGURATION
*type*: ++
typeof: string
*name*: ++
typeof: string
# EXAMPLES
```
@@ -77,6 +96,17 @@ the screen or playing audio.
"tooltip": true,
"tooltip-icon-size": 24
}
],
"ignore-monitor": true,
"ignore": [
{
"type": "audio-in",
"name": "cava"
},
{
"type": "screenshare",
"name": "obs"
}
]
},
```
+1
View File
@@ -83,6 +83,7 @@ Addressed by *river/layout*
- *#layout*
- *#layout.focused* Applied when the output this module's bar belongs to is focused.
- *#layout.<layout>* Applied when the output this module's bar belongs uses this layout.
# SEE ALSO
+7
View File
@@ -89,6 +89,11 @@ Addressed by *sway/window*
default: false ++
If the workspace itself is focused and the workspace contains nodes or floating_nodes, show the workspace name. If not set, text remains empty but styles according to nodes in the workspace are still applied.
*show-hidden-marks*: ++
typeof: bool ++
default: false ++
For the *{marks}* format replacement, include hidden marks that start with an underscore.
*rewrite*: ++
typeof: object ++
Rules to rewrite the module format output. See *rewrite rules*.
@@ -117,6 +122,8 @@ Addressed by *sway/window*
*{shell}*: The shell of the focused window. It's 'xwayland' when the window is
running through xwayland, otherwise, it's 'xdg-shell'.
*{marks}*: Marks of the window.
# REWRITE RULES
*rewrite* is an object where keys are regular expressions and values are
+5
View File
@@ -31,6 +31,11 @@ Addressed by *sway/workspaces*
default: false ++
If set to false, you can scroll to cycle through workspaces. If set to true this behaviour is disabled.
*reverse-scroll*: ++
typeof: bool ++
default: false ++
If set to false, scrolling up will switch to the previous workspace and scrolling down will switch to the next workspace. If set to true, the behavior will be reversed.
*disable-click*: ++
typeof: bool ++
default: false ++
+6
View File
@@ -62,6 +62,12 @@ Addressed by *systemd-failed-units*
*{nr_failed}*: Number of total failed units.
*{systemd_state}:* State of the systemd system session
*{user_state}:* State of the systemd user session
*{overall_state}:* Overall state of the systemd and user session. ("Ok" or "Degraded")
# EXAMPLES
```
+2 -1
View File
@@ -40,7 +40,7 @@ Addressed by *temperature*
The threshold before it is considered critical (Celsius).
*interval*: ++
typeof: integer ++
typeof: integer or float ++
default: 10 ++
The interval in which the information gets polled.
@@ -160,4 +160,5 @@ Addressed by *temperature*
# STYLE
- *#temperature*
- *#temperature.warning*
- *#temperature.critical*
+82
View File
@@ -0,0 +1,82 @@
waybar-wayfire-window(5)
# NAME
waybar - wayfire window module
# DESCRIPTION
The *window* module displays the title of the currently focused window in wayfire.
# CONFIGURATION
Addressed by *wayfire/window*
*format*: ++
typeof: string ++
default: {title} ++
The format, how information should be displayed. On {} the current window title is displayed.
*rewrite*: ++
typeof: object ++
Rules to rewrite window title. See *rewrite rules*.
*icon*: ++
typeof: bool ++
default: false ++
Option to hide the application icon.
*icon-size*: ++
typeof: integer ++
default: 24 ++
Option to change the size of the application icon.
*expand*: ++
typeof: bool ++
default: false ++
Enables this module to consume all left over space dynamically.
# FORMAT REPLACEMENTS
See the output of "wayfire msg windows" for examples
*{title}*: The current title of the focused window.
*{app_id}*: The current app ID of the focused window.
# REWRITE RULES
*rewrite* is an object where keys are regular expressions and values are
rewrite rules if the expression matches. Rules may contain references to
captures of the expression.
Regular expression and replacement follow ECMA-script rules.
If no expression matches, the title is left unchanged.
Invalid expressions (e.g., mismatched parentheses) are skipped.
# EXAMPLES
```
"wayfire/window": {
"format": "{}",
"rewrite": {
"(.*) - Mozilla Firefox": "🌎 $1",
"(.*) - zsh": "> [$1]"
}
}
```
# STYLE
- *#window*
- *window#waybar.empty #window* When no windows are on the workspace
The following classes are applied to the entire Waybar rather than just the
window widget:
- *window#waybar.empty* When no windows are in the workspace
- *window#waybar.solo* When only one window is on the workspace
- *window#waybar.<app-id>* Where *app-id* is the app ID of the only window on
the workspace
+86
View File
@@ -0,0 +1,86 @@
waybar-wayfire-workspaces(5)
# NAME
waybar - wayfire workspaces module
# DESCRIPTION
The *workspaces* module displays the currently used workspaces in wayfire.
# CONFIGURATION
Addressed by *wayfire/workspaces*
*format*: ++
typeof: string ++
default: {value} ++
The format, how information should be displayed.
*format-icons*: ++
typeof: array ++
Based on the workspace name, index and state, the corresponding icon gets selected. See *icons*.
*disable-click*: ++
typeof: bool ++
default: false ++
If set to false, you can click to change workspace. If set to true this behaviour is disabled.
*disable-markup*: ++
typeof: bool ++
default: false ++
If set to true, button label will escape pango markup.
*current-only*: ++
typeof: bool ++
default: false ++
If set to true, only the active or focused workspace will be shown.
*on-update*: ++
typeof: string ++
Command to execute when the module is updated.
*expand*: ++
typeof: bool ++
default: false ++
Enables this module to consume all left over space dynamically.
# FORMAT REPLACEMENTS
*{icon}*: Icon, as defined in *format-icons*.
*{index}*: Index of the workspace on its output.
*{output}*: Output where the workspace is located.
# ICONS
Additional to workspace name matching, the following *format-icons* can be set.
- *default*: Will be shown, when no string matches are found.
- *focused*: Will be shown, when workspace is focused.
# EXAMPLES
```
"wayfire/workspaces": {
"format": "{icon}",
"format-icons": {
"1": "",
"2": "",
"3": "",
"4": "",
"5": "",
"focused": "",
"default": ""
}
}
```
# Style
- *#workspaces button*
- *#workspaces button.focused*: The single focused workspace.
- *#workspaces button.empty*: The workspace is empty.
- *#workspaces button.current_output*: The workspace is from the same output as
the bar that it is displayed on.
+51 -4
View File
@@ -86,7 +86,7 @@ The visual display elements for waybar use a CSS stylesheet, see *waybar-styles(
*no-center* ++
typeof: bool ++
default: false ++
Option to disable the center modules fully usefull together with expand-\*.
Option to disable the center modules fully useful together with expand-\*.
*spacing* ++
typeof: integer ++
@@ -151,6 +151,20 @@ The visual display elements for waybar use a CSS stylesheet, see *waybar-styles(
default: *false* ++
Option to enable reloading the css style if a modification is detected on the style sheet file or any imported css files.
*on-sigusr1* ++
typeof: string ++
default: *toggle* ++
Action that is performed when receiving SIGUSR1 kill signal. ++
Possible values: *show*, *hide*, *toggle*, *reload*, *noop*. ++
Default value: *toggle*.
*on-sigusr2* ++
typeof: string ++
default: *reload* ++
Action that is performed when receiving SIGUSR2 kill signal. ++
Possible values: *show*, *hide*, *toggle*, *reload*, *noop*. ++
Default value: *reload*.
# MODULE FORMAT
You can use PangoMarkupFormat (See https://developer.gnome.org/pango/stable/PangoMarkupFormat.html#PangoMarkupFormat).
@@ -206,14 +220,36 @@ A minimal *config* file could look like this:
Waybar accepts the following signals:
*SIGUSR1*
Toggles the bar visibility (hides if shown, shows if hidden)
By default toggles the bar visibility (hides if shown, shows if hidden)
*SIGUSR2*
Reloads (resets) the bar
By default reloads (resets) the bar
*SIGINT*
Quits the bar
For example, to toggle the bar programmatically, you can invoke `killall -SIGUSR1 waybar`.
## User signal configuration
Config parameters *on-sigusr1* and *on-sigusr2* change what happens when bars receive
*SIGUSR1* and *SIGUSR2* signals.
This means that commands `killall -SIGUSR1 waybar` and `killall -SIGUSR2 waybar`
can perform user-configured action.
It also means that if an external script has the PID of the bar then it can
perform more complex `show`/`hide`/`reload` logic for each instance of Waybar.
One can find the PID e.g. by doing `pgrep -a waybar` which could then match
by config name or other parameters.
## Kill parameter meanings
*show* Switches state to visible (per bar).
*hide* Switches state to hidden (per bar).
*toggle* Switches state between visible and hidden (per bar).
*reload* Reloads all waybars of current waybar process (basically equivalent to
restarting with updated config which sets initial visibility values).
*noop* Does nothing when the kill signal is received.
# MULTI OUTPUT CONFIGURATION
## Limit a configuration to some outputs
@@ -272,6 +308,17 @@ When positioning Waybar on the left or right side of the screen, sometimes it's
Valid options for the "rotate" property are: 0, 90, 180, and 270.
## Swapping icon and label
If a module displays both a label and an icon, it might be desirable to swap them (for instance, for panels on the left or right of the screen, or for user adopting a right-to-left script). This can be achieved with the "swap-icon-label" property, taking a boolean. Example:
```
{
"sway/window": {
"swap-icon-label": true
}
}
```
## Grouping modules
Module groups allow stacking modules in any direction. By default, when the bar is positioned on the top or bottom of the screen, modules in a group are stacked vertically. Likewise, when positioned on the left or right, modules in a group are stacked horizontally. This can be changed with the "orientation" property.
@@ -390,4 +437,4 @@ A group may hide all but one element, showing them only on mouse hover. In order
# SEE ALSO
*sway-output(5)*
*waybar-styles(5)"
*waybar-styles(5)*
+34 -15
View File
@@ -1,6 +1,6 @@
project(
'waybar', 'cpp', 'c',
version: '0.12.0',
version: '0.14.0',
license: 'MIT',
meson_version: '>= 0.59.0',
default_options : [
@@ -69,7 +69,7 @@ is_openbsd = host_machine.system() == 'openbsd'
thread_dep = dependency('threads')
fmt = dependency('fmt', version : ['>=8.1.1'], fallback : ['fmt', 'fmt_dep'])
spdlog = dependency('spdlog', version : ['>=1.10.0'], fallback : ['spdlog', 'spdlog_dep'], default_options : ['external_fmt=enabled', 'std_format=disabled', 'tests=disabled'])
spdlog = dependency('spdlog', version : ['>=1.15.2'], fallback : ['spdlog', 'spdlog_dep'], default_options : ['std_format=disabled', 'tests=disabled'])
wayland_client = dependency('wayland-client')
wayland_cursor = dependency('wayland-cursor')
wayland_protos = dependency('wayland-protocols')
@@ -93,6 +93,7 @@ libmpdclient = dependency('libmpdclient', required: get_option('mpd'))
xkbregistry = dependency('xkbregistry')
libjack = dependency('jack', required: get_option('jack'))
libwireplumber = dependency('wireplumber-0.5', required: get_option('wireplumber'))
libgps = dependency('libgps', required: get_option('gps'))
libsndio = compiler.find_library('sndio', required: get_option('sndio'))
if libsndio.found()
@@ -182,6 +183,7 @@ src_files = files(
'src/util/sanitize_str.cpp',
'src/util/rewrite_string.cpp',
'src/util/gtk_icon.cpp',
'src/util/icon_loader.cpp',
'src/util/regex_collection.cpp',
'src/util/css_reload_helper.cpp'
)
@@ -275,6 +277,17 @@ if true
man_files += files('man/waybar-wlr-taskbar.5.scd')
endif
if wayland_protos.version().version_compare('>=1.39')
add_project_arguments('-DHAVE_EXT_WORKSPACES', language: 'cpp')
src_files += files(
'src/modules/ext/workspace_manager.cpp',
'src/modules/ext/workspace_manager_binding.cpp',
)
man_files += files(
'man/waybar-ext-workspaces.5.scd',
)
endif
if true
add_project_arguments('-DHAVE_RIVER', language: 'cpp')
src_files += files(
@@ -306,6 +319,7 @@ if true
'src/modules/hyprland/language.cpp',
'src/modules/hyprland/submap.cpp',
'src/modules/hyprland/window.cpp',
'src/modules/hyprland/windowcount.cpp',
'src/modules/hyprland/workspace.cpp',
'src/modules/hyprland/workspaces.cpp',
'src/modules/hyprland/windowcreationpayload.cpp',
@@ -333,6 +347,15 @@ if get_option('niri')
)
endif
if true
add_project_arguments('-DHAVE_WAYFIRE', language: 'cpp')
src_files += files(
'src/modules/wayfire/backend.cpp',
'src/modules/wayfire/window.cpp',
'src/modules/wayfire/workspaces.cpp',
)
endif
if get_option('login-proxy')
add_project_arguments('-DHAVE_LOGIN_PROXY', language: 'cpp')
endif
@@ -474,17 +497,6 @@ else
man_files += files('man/waybar-clock.5.scd')
endif
if get_option('experimental')
add_project_arguments('-DHAVE_WLR_WORKSPACES', language: 'cpp')
src_files += files(
'src/modules/wlr/workspace_manager.cpp',
'src/modules/wlr/workspace_manager_binding.cpp',
)
man_files += files(
'man/waybar-wlr-workspaces.5.scd',
)
endif
cava = dependency('cava',
version : '>=0.10.4',
required: get_option('cava'),
@@ -493,10 +505,16 @@ cava = dependency('cava',
if cava.found()
add_project_arguments('-DHAVE_LIBCAVA', language: 'cpp')
src_files += files('src/modules/cava.cpp')
src_files += files('src/modules/cava/cava.cpp', 'src/modules/cava/cava_backend.cpp')
man_files += files('man/waybar-cava.5.scd')
endif
if libgps.found()
add_project_arguments('-DHAVE_LIBGPS', language: 'cpp')
src_files += files('src/modules/gps.cpp')
man_files += files('man/waybar-gps.5.scd')
endif
subdir('protocol')
app_resources = []
@@ -535,7 +553,8 @@ executable(
libsndio,
tz_dep,
xkbregistry,
cava
cava,
libgps
],
include_directories: inc_dirs,
install: true,
+1
View File
@@ -21,3 +21,4 @@ option('wireplumber', type: 'feature', value: 'auto', description: 'Enable suppo
option('cava', type: 'feature', value: 'auto', description: 'Enable support for Cava')
option('niri', type: 'boolean', description: 'Enable support for niri')
option('login-proxy', type: 'boolean', description: 'Enable interfacing with dbus login interface')
option('gps', type: 'feature', value: 'auto', description: 'Enable support for gps')
+6 -3
View File
@@ -30,9 +30,12 @@ in
# nixpkgs checks version, no need when building locally
nativeInstallCheckInputs = [ ];
buildInputs = (builtins.filter (p: p.pname != "wireplumber") oldAttrs.buildInputs) ++ [
pkgs.wireplumber
];
buildInputs =
(builtins.filter (p: p.pname != "wireplumber" && p.pname != "gps") oldAttrs.buildInputs)
++ [
pkgs.wireplumber
pkgs.gpsd
];
postUnpack = ''
pushd "$sourceRoot"
-306
View File
@@ -1,306 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="ext_workspace_unstable_v1">
<copyright>
Copyright © 2019 Christopher Billington
Copyright © 2020 Ilia Bozhinov
Permission to use, copy, modify, distribute, and sell this
software and its documentation for any purpose is hereby granted
without fee, provided that the above copyright notice appear in
all copies and that both that copyright notice and this permission
notice appear in supporting documentation, and that the name of
the copyright holders not be used in advertising or publicity
pertaining to distribution of the software without specific,
written prior permission. The copyright holders make no
representations about the suitability of this software for any
purpose. It is provided "as is" without express or implied
warranty.
THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
</copyright>
<interface name="zext_workspace_manager_v1" version="1">
<description summary="list and control workspaces">
Workspaces, also called virtual desktops, are groups of surfaces. A
compositor with a concept of workspaces may only show some such groups of
surfaces (those of 'active' workspaces) at a time. 'Activating' a
workspace is a request for the compositor to display that workspace's
surfaces as normal, whereas the compositor may hide or otherwise
de-emphasise surfaces that are associated only with 'inactive' workspaces.
Workspaces are grouped by which sets of outputs they correspond to, and
may contain surfaces only from those outputs. In this way, it is possible
for each output to have its own set of workspaces, or for all outputs (or
any other arbitrary grouping) to share workspaces. Compositors may
optionally conceptually arrange each group of workspaces in an
N-dimensional grid.
The purpose of this protocol is to enable the creation of taskbars and
docks by providing them with a list of workspaces and their properties,
and allowing them to activate and deactivate workspaces.
After a client binds the zext_workspace_manager_v1, each workspace will be
sent via the workspace event.
</description>
<event name="workspace_group">
<description summary="a workspace group has been created">
This event is emitted whenever a new workspace group has been created.
All initial details of the workspace group (workspaces, outputs) will be
sent immediately after this event via the corresponding events in
zext_workspace_group_handle_v1.
</description>
<arg name="workspace_group" type="new_id" interface="zext_workspace_group_handle_v1"/>
</event>
<request name="commit">
<description summary="all requests about the workspaces have been sent">
The client must send this request after it has finished sending other
requests. The compositor must process a series of requests preceding a
commit request atomically.
This allows changes to the workspace properties to be seen as atomic,
even if they happen via multiple events, and even if they involve
multiple zext_workspace_handle_v1 objects, for example, deactivating one
workspace and activating another.
</description>
</request>
<event name="done">
<description summary="all information about the workspace groups has been sent">
This event is sent after all changes in all workspace groups have been
sent.
This allows changes to one or more zext_workspace_group_handle_v1
properties to be seen as atomic, even if they happen via multiple
events. In particular, an output moving from one workspace group to
another sends an output_enter event and an output_leave event to the two
zext_workspace_group_handle_v1 objects in question. The compositor sends
the done event only after updating the output information in both
workspace groups.
</description>
</event>
<event name="finished">
<description summary="the compositor has finished with the workspace_manager">
This event indicates that the compositor is done sending events to the
zext_workspace_manager_v1. The server will destroy the object
immediately after sending this request, so it will become invalid and
the client should free any resources associated with it.
</description>
</event>
<request name="stop">
<description summary="stop sending events">
Indicates the client no longer wishes to receive events for new
workspace groups. However the compositor may emit further workspace
events, until the finished event is emitted.
The client must not send any more requests after this one.
</description>
</request>
</interface>
<interface name="zext_workspace_group_handle_v1" version="1">
<description summary="a workspace group assigned to a set of outputs">
A zext_workspace_group_handle_v1 object represents a a workspace group
that is assigned a set of outputs and contains a number of workspaces.
The set of outputs assigned to the workspace group is conveyed to the client via
output_enter and output_leave events, and its workspaces are conveyed with
workspace events.
For example, a compositor which has a set of workspaces for each output may
advertise a workspace group (and its workspaces) per output, whereas a compositor
where a workspace spans all outputs may advertise a single workspace group for all
outputs.
</description>
<event name="output_enter">
<description summary="output assigned to workspace group">
This event is emitted whenever an output is assigned to the workspace
group.
</description>
<arg name="output" type="object" interface="wl_output"/>
</event>
<event name="output_leave">
<description summary="output removed from workspace group">
This event is emitted whenever an output is removed from the workspace
group.
</description>
<arg name="output" type="object" interface="wl_output"/>
</event>
<event name="workspace">
<description summary="workspace added to workspace group">
This event is emitted whenever a new workspace has been created.
All initial details of the workspace (name, coordinates, state) will
be sent immediately after this event via the corresponding events in
zext_workspace_handle_v1.
</description>
<arg name="workspace" type="new_id" interface="zext_workspace_handle_v1"/>
</event>
<event name="remove">
<description summary="this workspace group has been destroyed">
This event means the zext_workspace_group_handle_v1 has been destroyed.
It is guaranteed there won't be any more events for this
zext_workspace_group_handle_v1. The zext_workspace_group_handle_v1 becomes
inert so any requests will be ignored except the destroy request.
The compositor must remove all workspaces belonging to a workspace group
before removing the workspace group.
</description>
</event>
<request name="create_workspace">
<description summary="create a new workspace">
Request that the compositor create a new workspace with the given name.
There is no guarantee that the compositor will create a new workspace,
or that the created workspace will have the provided name.
</description>
<arg name="workspace" type="string"/>
</request>
<request name="destroy" type="destructor">
<description summary="destroy the zext_workspace_handle_v1 object">
Destroys the zext_workspace_handle_v1 object.
This request should be called either when the client does not want to
use the workspace object any more or after the remove event to finalize
the destruction of the object.
</description>
</request>
</interface>
<interface name="zext_workspace_handle_v1" version="1">
<description summary="a workspace handing a group of surfaces">
A zext_workspace_handle_v1 object represents a a workspace that handles a
group of surfaces.
Each workspace has a name, conveyed to the client with the name event; a
list of states, conveyed to the client with the state event; and
optionally a set of coordinates, conveyed to the client with the
coordinates event. The client may request that the compositor activate or
deactivate the workspace.
Each workspace can belong to only a single workspace group.
Depepending on the compositor policy, there might be workspaces with
the same name in different workspace groups, but these workspaces are still
separate (e.g. one of them might be active while the other is not).
</description>
<event name="name">
<description summary="workspace name changed">
This event is emitted immediately after the zext_workspace_handle_v1 is
created and whenever the name of the workspace changes.
</description>
<arg name="name" type="string"/>
</event>
<event name="coordinates">
<description summary="workspace coordinates changed">
This event is used to organize workspaces into an N-dimensional grid
within a workspace group, and if supported, is emitted immediately after
the zext_workspace_handle_v1 is created and whenever the coordinates of
the workspace change. Compositors may not send this event if they do not
conceptually arrange workspaces in this way. If compositors simply
number workspaces, without any geometric interpretation, they may send
1D coordinates, which clients should not interpret as implying any
geometry. Sending an empty array means that the compositor no longer
orders the workspace geometrically.
Coordinates have an arbitrary number of dimensions N with an uint32
position along each dimension. By convention if N > 1, the first
dimension is X, the second Y, the third Z, and so on. The compositor may
chose to utilize these events for a more novel workspace layout
convention, however. No guarantee is made about the grid being filled or
bounded; there may be a workspace at coordinate 1 and another at
coordinate 1000 and none in between. Within a workspace group, however,
workspaces must have unique coordinates of equal dimensionality.
</description>
<arg name="coordinates" type="array"/>
</event>
<event name="state">
<description summary="the state of the workspace changed">
This event is emitted immediately after the zext_workspace_handle_v1 is
created and each time the workspace state changes, either because of a
compositor action or because of a request in this protocol.
</description>
<arg name="state" type="array"/>
</event>
<enum name="state">
<description summary="types of states on the workspace">
The different states that a workspace can have.
</description>
<entry name="active" value="0" summary="the workspace is active"/>
<entry name="urgent" value="1" summary="the workspace requests attention"/>
<entry name="hidden" value="2">
<description summary="the workspace is not visible">
The workspace is not visible in its workspace group, and clients
attempting to visualize the compositor workspace state should not
display such workspaces.
</description>
</entry>
</enum>
<event name="remove">
<description summary="this workspace has been destroyed">
This event means the zext_workspace_handle_v1 has been destroyed. It is
guaranteed there won't be any more events for this
zext_workspace_handle_v1. The zext_workspace_handle_v1 becomes inert so
any requests will be ignored except the destroy request.
</description>
</event>
<request name="destroy" type="destructor">
<description summary="destroy the zext_workspace_handle_v1 object">
Destroys the zext_workspace_handle_v1 object.
This request should be called either when the client does not want to
use the workspace object any more or after the remove event to finalize
the destruction of the object.
</description>
</request>
<request name="activate">
<description summary="activate the workspace">
Request that this workspace be activated.
There is no guarantee the workspace will be actually activated, and
behaviour may be compositor-dependent. For example, activating a
workspace may or may not deactivate all other workspaces in the same
group.
</description>
</request>
<request name="deactivate">
<description summary="activate the workspace">
Request that this workspace be deactivated.
There is no guarantee the workspace will be actually deactivated.
</description>
</request>
<request name="remove">
<description summary="remove the workspace">
Request that this workspace be removed.
There is no guarantee the workspace will be actually removed.
</description>
</request>
</interface>
</protocol>
+6 -1
View File
@@ -26,12 +26,17 @@ client_protocols = [
[wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'],
[wl_protocol_dir, 'unstable/idle-inhibit/idle-inhibit-unstable-v1.xml'],
['wlr-foreign-toplevel-management-unstable-v1.xml'],
['ext-workspace-unstable-v1.xml'],
['river-status-unstable-v1.xml'],
['river-control-unstable-v1.xml'],
['dwl-ipc-unstable-v2.xml'],
]
if wayland_protos.version().version_compare('>=1.39')
client_protocols += [
[wl_protocol_dir, 'staging/ext-workspace/ext-workspace-v1.xml']
]
endif
client_protos_src = []
client_protos_headers = []
+1
View File
@@ -112,6 +112,7 @@ class PlayerManager:
logger.debug(f"Metadata changed for player {player.props.player_name}")
player_name = player.props.player_name
artist = player.get_artist()
artist = artist.replace("&", "&amp;")
title = player.get_title()
title = title.replace("&", "&amp;")
+1 -1
View File
@@ -63,7 +63,7 @@ button:hover {
background: rgba(0, 0, 0, 0.2);
}
#workspaces button.focused {
#workspaces button.focused, #workspaces button.active {
background-color: #64727D;
box-shadow: inset 0 -3px #ffffff;
}
+1 -1
View File
@@ -1,5 +1,5 @@
[Unit]
Description=Highly customizable Wayland bar for Sway and Wlroots based compositors.
Description=Highly customizable Wayland bar for Sway and Wlroots based compositors
Documentation=https://github.com/Alexays/Waybar/wiki/
PartOf=graphical-session.target
After=graphical-session.target
+2 -1
View File
@@ -63,7 +63,8 @@ std::optional<std::string> getDesktopFilePath(const std::string& app_identifier,
return {};
}
const auto data_dirs = Glib::get_system_data_dirs();
auto data_dirs = Glib::get_system_data_dirs();
data_dirs.insert(data_dirs.begin(), Glib::get_user_data_dir());
for (const auto& data_dir : data_dirs) {
const auto data_app_dir = data_dir + "/applications/";
auto desktop_file_suffix = app_identifier + ".desktop";
+29 -3
View File
@@ -1,6 +1,7 @@
#include "AIconLabel.hpp"
#include <gdkmm/pixbuf.h>
#include <spdlog/spdlog.h>
namespace waybar {
@@ -17,14 +18,39 @@ AIconLabel::AIconLabel(const Json::Value &config, const std::string &name, const
box_.get_style_context()->add_class(id);
}
box_.set_orientation(Gtk::Orientation::ORIENTATION_HORIZONTAL);
int rot = 0;
if (config_["rotate"].isUInt()) {
rot = config["rotate"].asUInt() % 360;
if ((rot % 90) != 00) rot = 0;
rot /= 90;
}
if ((rot % 2) == 0)
box_.set_orientation(Gtk::Orientation::ORIENTATION_HORIZONTAL);
else
box_.set_orientation(Gtk::Orientation::ORIENTATION_VERTICAL);
box_.set_name(name);
int spacing = config_["icon-spacing"].isInt() ? config_["icon-spacing"].asInt() : 8;
box_.set_spacing(spacing);
box_.add(image_);
box_.add(label_);
bool swap_icon_label = false;
if (config_["swap-icon-label"].isNull()) {
} else if (config_["swap-icon-label"].isBool()) {
swap_icon_label = config_["swap-icon-label"].asBool();
} else {
spdlog::warn("'swap-icon-label' must be a bool, found '{}'. Using default value (false).",
config_["swap-icon-label"].asString());
}
if ((rot == 0 || rot == 3) ^ swap_icon_label) {
box_.add(image_);
box_.add(label_);
} else {
box_.add(label_);
box_.add(image_);
}
event_box_.add(box_);
}
+11 -4
View File
@@ -17,10 +17,17 @@ ALabel::ALabel(const Json::Value& config, const std::string& name, const std::st
config["format-alt"].isString() || config["menu"].isString() || enable_click,
enable_scroll),
format_(config_["format"].isString() ? config_["format"].asString() : format),
// Leave the default option outside of the std::max(1L, ...), because the zero value
// (default) is used in modules/custom.cpp to make the difference between
// two types of custom scripts. Fixes #4521.
interval_(config_["interval"] == "once"
? std::chrono::seconds::max()
: std::chrono::seconds(
config_["interval"].isUInt() ? config_["interval"].asUInt() : interval)),
? std::chrono::milliseconds::max()
: std::chrono::milliseconds(
(config_["interval"].isNumeric()
? std::max(1L, // Minimum 1ms due to millisecond precision
static_cast<long>(config_["interval"].asDouble()) * 1000)
: 1000 * (long)interval))),
default_format_(format_) {
label_.set_name(name);
if (!id.empty()) {
@@ -200,7 +207,7 @@ std::string ALabel::getState(uint8_t value, bool lesser) {
}
}
// Sort states
std::sort(states.begin(), states.end(), [&lesser](auto& a, auto& b) {
std::ranges::sort(states.begin(), states.end(), [&lesser](auto& a, auto& b) {
return lesser ? a.second < b.second : a.second > b.second;
});
std::string valid_state;
+10 -6
View File
@@ -83,7 +83,7 @@ AModule::AModule(const Json::Value& config, const std::string& name, const std::
}
AModule::~AModule() {
for (const auto& pid : pid_) {
for (const auto& pid : pid_children_) {
if (pid != -1) {
killpg(pid, SIGTERM);
}
@@ -93,15 +93,15 @@ AModule::~AModule() {
auto AModule::update() -> void {
// Run user-provided update handler if configured
if (config_["on-update"].isString()) {
pid_.push_back(util::command::forkExec(config_["on-update"].asString()));
pid_children_.push_back(util::command::forkExec(config_["on-update"].asString()));
}
}
// Get mapping between event name and module action name
// Then call overrided doAction in order to call appropriate module action
// Then call overridden doAction in order to call appropriate module action
auto AModule::doAction(const std::string& name) -> void {
if (!name.empty()) {
const std::map<std::string, std::string>::const_iterator& recA{eventActionMap_.find(name)};
// Call overrided action if derrived class has implemented it
// Call overridden action if derived class has implemented it
if (recA != eventActionMap_.cend() && name != recA->second) this->doAction(recA->second);
}
}
@@ -172,6 +172,10 @@ bool AModule::handleUserEvent(GdkEventButton* const& e) {
// Popup the menu
gtk_widget_show_all(GTK_WIDGET(menu_));
gtk_menu_popup_at_pointer(GTK_MENU(menu_), reinterpret_cast<GdkEvent*>(e));
// Manually reset prelight to make sure the module doesn't stay in a hover state
if (auto* module = event_box_.get_child(); module != nullptr) {
module->unset_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT);
}
}
}
// Second call user scripts
@@ -182,7 +186,7 @@ bool AModule::handleUserEvent(GdkEventButton* const& e) {
format.clear();
}
if (!format.empty()) {
pid_.push_back(util::command::forkExec(format));
pid_children_.push_back(util::command::forkExec(format));
}
dp.emit();
return true;
@@ -267,7 +271,7 @@ bool AModule::handleScroll(GdkEventScroll* e) {
this->AModule::doAction(eventName);
// Second call user scripts
if (config_[eventName].isString())
pid_.push_back(util::command::forkExec(config_[eventName].asString()));
pid_children_.push_back(util::command::forkExec(config_[eventName].asString()));
dp.emit();
return true;
+52 -5
View File
@@ -8,6 +8,8 @@
#include "client.hpp"
#include "factory.hpp"
#include "group.hpp"
#include "util/enum.hpp"
#include "util/kill_signal.hpp"
#ifdef HAVE_SWAY
#include "modules/sway/bar.hpp"
@@ -277,6 +279,32 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config)
}
#endif
waybar::util::EnumParser<util::KillSignalAction> m_signalActionEnumParser;
const auto& configSigusr1 = config["on-sigusr1"];
if (configSigusr1.isString()) {
auto strSigusr1 = configSigusr1.asString();
try {
onSigusr1 =
m_signalActionEnumParser.parseStringToEnum(strSigusr1, util::userKillSignalActions);
} catch (const std::invalid_argument& e) {
onSigusr1 = util::SIGNALACTION_DEFAULT_SIGUSR1;
spdlog::warn(
"Invalid string representation for on-sigusr1. Falling back to default mode (toggle).");
}
}
const auto& configSigusr2 = config["on-sigusr2"];
if (configSigusr2.isString()) {
auto strSigusr2 = configSigusr2.asString();
try {
onSigusr2 =
m_signalActionEnumParser.parseStringToEnum(strSigusr2, util::userKillSignalActions);
} catch (const std::invalid_argument& e) {
onSigusr2 = util::SIGNALACTION_DEFAULT_SIGUSR2;
spdlog::warn(
"Invalid string representation for on-sigusr2. Falling back to default mode (reload).");
}
}
setupWidgets();
window.show_all();
@@ -405,7 +433,18 @@ void waybar::Bar::onMap(GdkEventAny* /*unused*/) {
/*
* Obtain a pointer to the custom layer surface for modules that require it (idle_inhibitor).
*/
auto* gdk_window = window.get_window()->gobj();
auto gdk_window_ref = window.get_window();
if (!gdk_window_ref) {
spdlog::warn("Failed to get GDK window during onMap, deferring surface initialization");
return;
}
auto* gdk_window = gdk_window_ref->gobj();
if (!gdk_window) {
spdlog::warn("GDK window object is null during onMap, deferring surface initialization");
return;
}
surface = gdk_wayland_window_get_wl_surface(gdk_window);
configureGlobalOffset(gdk_window_get_width(gdk_window), gdk_window_get_height(gdk_window));
@@ -422,6 +461,8 @@ void waybar::Bar::setVisible(bool value) {
}
void waybar::Bar::toggle() { setVisible(!visible); }
void waybar::Bar::show() { setVisible(true); }
void waybar::Bar::hide() { setVisible(false); }
// Converting string to button code rn as to avoid doing it later
void waybar::Bar::setupAltFormatKeyForModule(const std::string& module_name) {
@@ -479,6 +520,9 @@ void waybar::Bar::handleSignal(int signal) {
}
}
waybar::util::KillSignalAction waybar::Bar::getOnSigusr1Action() { return this->onSigusr1; }
waybar::util::KillSignalAction waybar::Bar::getOnSigusr2Action() { return this->onSigusr2; }
void waybar::Bar::getModules(const Factory& factory, const std::string& pos,
waybar::Group* group = nullptr) {
auto module_list = group != nullptr ? config[pos]["modules"] : config[pos];
@@ -496,7 +540,11 @@ void waybar::Bar::getModules(const Factory& factory, const std::string& pos,
auto vertical = (group != nullptr ? group->getBox().get_orientation()
: box_.get_orientation()) == Gtk::ORIENTATION_VERTICAL;
auto* group_module = new waybar::Group(id_name, class_name, config[ref], vertical);
auto group_config = config[ref];
if (group_config["modules"].isNull()) {
spdlog::warn("Group definition '{}' has not been found, group will be hidden", ref);
}
auto* group_module = new waybar::Group(id_name, class_name, group_config, vertical);
getModules(factory, ref, group_module);
module = group_module;
} else {
@@ -545,7 +593,6 @@ auto waybar::Bar::setupWidgets() -> void {
if (config["fixed-center"].isBool() ? config["fixed-center"].asBool() : true) {
box_.set_center_widget(center_);
} else {
spdlog::error("No fixed center_");
box_.pack_start(center_, true, expand_center);
}
}
@@ -569,13 +616,13 @@ auto waybar::Bar::setupWidgets() -> void {
if (!no_center) {
for (auto const& module : modules_center_) {
center_.pack_start(*module, false, false);
center_.pack_start(*module, module->expandEnabled(), module->expandEnabled());
}
}
std::reverse(modules_right_.begin(), modules_right_.end());
for (auto const& module : modules_right_) {
right_.pack_end(*module, false, false);
right_.pack_end(*module, module->expandEnabled(), module->expandEnabled());
}
}
+12 -6
View File
@@ -86,7 +86,7 @@ void waybar::Client::handleOutputDone(void *data, struct zxdg_output_v1 * /*xdg_
}
}
} catch (const std::exception &e) {
std::cerr << e.what() << '\n';
spdlog::warn("caught exception in zxdg_output_v1_listener::done: {}", e.what());
}
}
@@ -97,7 +97,7 @@ void waybar::Client::handleOutputName(void *data, struct zxdg_output_v1 * /*xdg_
auto &output = client->getOutput(data);
output.name = name;
} catch (const std::exception &e) {
std::cerr << e.what() << '\n';
spdlog::warn("caught exception in zxdg_output_v1_listener::name: {}", e.what());
}
}
@@ -106,13 +106,13 @@ void waybar::Client::handleOutputDescription(void *data, struct zxdg_output_v1 *
auto *client = waybar::Client::inst();
try {
auto &output = client->getOutput(data);
const char *open_paren = strrchr(description, '(');
// Description format: "identifier (name)"
size_t identifier_length = open_paren - description;
output.identifier = std::string(description, identifier_length - 1);
auto s = std::string(description);
auto pos = s.find(" (");
output.identifier = pos != std::string::npos ? s.substr(0, pos) : s;
} catch (const std::exception &e) {
std::cerr << e.what() << '\n';
spdlog::warn("caught exception in zxdg_output_v1_listener::description: {}", e.what());
}
}
@@ -151,15 +151,19 @@ void waybar::Client::handleDeferredMonitorRemoval(Glib::RefPtr<Gdk::Monitor> mon
const std::string waybar::Client::getStyle(const std::string &style,
std::optional<Appearance> appearance = std::nullopt) {
auto gtk_settings = Gtk::Settings::get_default();
std::optional<std::string> css_file;
if (style.empty()) {
std::vector<std::string> search_files;
switch (appearance.value_or(portal->getAppearance())) {
case waybar::Appearance::LIGHT:
search_files.emplace_back("style-light.css");
gtk_settings->property_gtk_application_prefer_dark_theme() = false;
break;
case waybar::Appearance::DARK:
search_files.emplace_back("style-dark.css");
gtk_settings->property_gtk_application_prefer_dark_theme() = true;
break;
case waybar::Appearance::UNKNOWN:
break;
@@ -169,9 +173,11 @@ const std::string waybar::Client::getStyle(const std::string &style,
} else {
css_file = style;
}
if (!css_file) {
throw std::runtime_error("Missing required resource files");
}
spdlog::info("Using CSS file {}", css_file.value());
return css_file.value();
};
+51 -4
View File
@@ -2,7 +2,11 @@
#include <spdlog/spdlog.h>
#include <unistd.h>
#ifndef __OpenBSD__
#include <wordexp.h>
#else
#include <glob.h>
#endif
#include <filesystem>
#include <fstream>
@@ -34,6 +38,7 @@ std::vector<std::string> Config::tryExpandPath(const std::string &base,
spdlog::debug("Try expanding: {}", path.string());
std::vector<std::string> results;
#ifndef __OpenBSD__
wordexp_t p;
if (wordexp(path.c_str(), &p, 0) == 0) {
for (size_t i = 0; i < p.we_wordc; i++) {
@@ -44,6 +49,18 @@ std::vector<std::string> Config::tryExpandPath(const std::string &base,
}
wordfree(&p);
}
#else
glob_t p;
if (glob(path.c_str(), 0, NULL, &p) == 0) {
for (size_t i = 0; i < p.gl_pathc; i++) {
if (access(p.gl_pathv[i], F_OK) == 0) {
results.emplace_back(p.gl_pathv[i]);
spdlog::debug("Found config file: {}", p.gl_pathv[i]);
}
}
globfree(&p);
}
#endif
return results;
}
@@ -89,19 +106,49 @@ void Config::setupConfig(Json::Value &dst, const std::string &config_file, int d
mergeConfig(dst, tmp_config);
}
std::vector<std::string> Config::findIncludePath(const std::string &name,
const std::vector<std::string> &dirs) {
auto match1 = tryExpandPath(name, "");
if (!match1.empty()) {
return match1;
}
if (const char *dir = std::getenv(Config::CONFIG_PATH_ENV)) {
if (auto res = tryExpandPath(dir, name); !res.empty()) {
return res;
}
}
for (const auto &dir : dirs) {
if (auto res = tryExpandPath(dir, name); !res.empty()) {
return res;
}
}
return {};
}
void Config::resolveConfigIncludes(Json::Value &config, int depth) {
Json::Value includes = config["include"];
if (includes.isArray()) {
for (const auto &include : includes) {
spdlog::info("Including resource file: {}", include.asString());
for (const auto &match : tryExpandPath(include.asString(), "")) {
setupConfig(config, match, depth + 1);
auto matches = findIncludePath(include.asString());
if (!matches.empty()) {
for (const auto &match : matches) {
setupConfig(config, match, depth + 1);
}
} else {
spdlog::warn("Unable to find resource file: {}", include.asString());
}
}
} else if (includes.isString()) {
spdlog::info("Including resource file: {}", includes.asString());
for (const auto &match : tryExpandPath(includes.asString(), "")) {
setupConfig(config, match, depth + 1);
auto matches = findIncludePath(includes.asString());
if (!matches.empty()) {
for (const auto &match : matches) {
setupConfig(config, match, depth + 1);
}
} else {
spdlog::warn("Unable to find resource file: {}", includes.asString());
}
}
}
+31 -7
View File
@@ -17,8 +17,8 @@
#ifdef HAVE_WLR_TASKBAR
#include "modules/wlr/taskbar.hpp"
#endif
#ifdef HAVE_WLR_WORKSPACES
#include "modules/wlr/workspace_manager.hpp"
#ifdef HAVE_EXT_WORKSPACES
#include "modules/ext/workspace_manager.hpp"
#endif
#ifdef HAVE_RIVER
#include "modules/river/layout.hpp"
@@ -34,6 +34,7 @@
#include "modules/hyprland/language.hpp"
#include "modules/hyprland/submap.hpp"
#include "modules/hyprland/window.hpp"
#include "modules/hyprland/windowcount.hpp"
#include "modules/hyprland/workspaces.hpp"
#endif
#ifdef HAVE_NIRI
@@ -41,6 +42,10 @@
#include "modules/niri/window.hpp"
#include "modules/niri/workspaces.hpp"
#endif
#ifdef HAVE_WAYFIRE
#include "modules/wayfire/window.hpp"
#include "modules/wayfire/workspaces.hpp"
#endif
#if defined(__FreeBSD__) || defined(__linux__)
#include "modules/battery.hpp"
#endif
@@ -104,11 +109,14 @@
#include "modules/wireplumber.hpp"
#endif
#ifdef HAVE_LIBCAVA
#include "modules/cava.hpp"
#include "modules/cava/cava.hpp"
#endif
#ifdef HAVE_SYSTEMD_MONITOR
#include "modules/systemd_failed_units.hpp"
#endif
#ifdef HAVE_LIBGPS
#include "modules/gps.hpp"
#endif
#include "modules/cffi.hpp"
#include "modules/custom.hpp"
#include "modules/image.hpp"
@@ -170,9 +178,9 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name,
return new waybar::modules::wlr::Taskbar(id, bar_, config_[name]);
}
#endif
#ifdef HAVE_WLR_WORKSPACES
if (ref == "wlr/workspaces") {
return new waybar::modules::wlr::WorkspaceManager(id, bar_, config_[name]);
#ifdef HAVE_EXT_WORKSPACES
if (ref == "ext/workspaces") {
return new waybar::modules::ext::WorkspaceManager(id, bar_, config_[name]);
}
#endif
#ifdef HAVE_RIVER
@@ -201,6 +209,9 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name,
if (ref == "hyprland/window") {
return new waybar::modules::hyprland::Window(id, bar_, config_[name]);
}
if (ref == "hyprland/windowcount") {
return new waybar::modules::hyprland::WindowCount(id, bar_, config_[name]);
}
if (ref == "hyprland/language") {
return new waybar::modules::hyprland::Language(id, bar_, config_[name]);
}
@@ -221,6 +232,14 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name,
if (ref == "niri/workspaces") {
return new waybar::modules::niri::Workspaces(id, bar_, config_[name]);
}
#endif
#ifdef HAVE_WAYFIRE
if (ref == "wayfire/window") {
return new waybar::modules::wayfire::Window(id, bar_, config_[name]);
}
if (ref == "wayfire/workspaces") {
return new waybar::modules::wayfire::Workspaces(id, bar_, config_[name]);
}
#endif
if (ref == "idle_inhibitor") {
return new waybar::modules::IdleInhibitor(id, bar_, config_[name]);
@@ -324,13 +343,18 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name,
#endif
#ifdef HAVE_LIBCAVA
if (ref == "cava") {
return new waybar::modules::Cava(id, config_[name]);
return new waybar::modules::cava::Cava(id, config_[name]);
}
#endif
#ifdef HAVE_SYSTEMD_MONITOR
if (ref == "systemd-failed-units") {
return new waybar::modules::SystemdFailedUnits(id, config_[name]);
}
#endif
#ifdef HAVE_LIBGPS
if (ref == "gps") {
return new waybar::modules::Gps(id, config_[name]);
}
#endif
if (ref == "temperature") {
return new waybar::modules::Temperature(id, config_[name]);
+136 -68
View File
@@ -1,3 +1,4 @@
#include <fcntl.h>
#include <spdlog/spdlog.h>
#include <sys/types.h>
#include <sys/wait.h>
@@ -6,67 +7,150 @@
#include <list>
#include <mutex>
#include "bar.hpp"
#include "client.hpp"
#include "util/SafeSignal.hpp"
std::mutex reap_mtx;
std::list<pid_t> reap;
volatile bool reload;
void* signalThread(void* args) {
int err;
int signum;
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGCHLD);
static int signal_pipe_write_fd;
// Write a single signal to `signal_pipe_write_fd`.
// This function is set as a signal handler, so it must be async-signal-safe.
static void writeSignalToPipe(int signum) {
ssize_t amt = write(signal_pipe_write_fd, &signum, sizeof(int));
// There's not much we can safely do inside of a signal handler.
// Let's just ignore any errors.
(void)amt;
}
// This initializes `signal_pipe_write_fd`, and sets up signal handlers.
//
// This function will run forever, emitting every `SIGUSR1`, `SIGUSR2`,
// `SIGINT`, `SIGCHLD`, and `SIGRTMIN + 1`...`SIGRTMAX` signal received
// to `signal_handler`.
static void catchSignals(waybar::SafeSignal<int>& signal_handler) {
int fd[2];
pipe(fd);
int signal_pipe_read_fd = fd[0];
signal_pipe_write_fd = fd[1];
// This pipe should be able to buffer ~thousands of signals. If it fills up,
// we'll drop signals instead of blocking.
// We can't allow the write end to block because we'll be writing to it in a
// signal handler, which could interrupt the loop that's reading from it and
// deadlock.
fcntl(signal_pipe_write_fd, F_SETFL, O_NONBLOCK);
std::signal(SIGUSR1, writeSignalToPipe);
std::signal(SIGUSR2, writeSignalToPipe);
std::signal(SIGINT, writeSignalToPipe);
std::signal(SIGCHLD, writeSignalToPipe);
for (int sig = SIGRTMIN + 1; sig <= SIGRTMAX; ++sig) {
std::signal(sig, writeSignalToPipe);
}
while (true) {
err = sigwait(&mask, &signum);
if (err != 0) {
spdlog::error("sigwait failed: {}", strerror(errno));
int signum;
ssize_t amt = read(signal_pipe_read_fd, &signum, sizeof(int));
if (amt < 0) {
spdlog::error("read from signal pipe failed with error {}, closing thread", strerror(errno));
break;
}
if (amt != sizeof(int)) {
continue;
}
switch (signum) {
case SIGCHLD:
spdlog::debug("Received SIGCHLD in signalThread");
if (!reap.empty()) {
reap_mtx.lock();
for (auto it = reap.begin(); it != reap.end(); ++it) {
if (waitpid(*it, nullptr, WNOHANG) == *it) {
spdlog::debug("Reaped child with PID: {}", *it);
it = reap.erase(it);
}
}
reap_mtx.unlock();
}
break;
default:
spdlog::debug("Received signal with number {}, but not handling", signum);
break;
}
signal_handler.emit(signum);
}
}
void startSignalThread() {
int err;
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGCHLD);
waybar::util::KillSignalAction getActionForBar(waybar::Bar* bar, int signal) {
switch (signal) {
case SIGUSR1:
return bar->getOnSigusr1Action();
case SIGUSR2:
return bar->getOnSigusr2Action();
default:
return waybar::util::KillSignalAction::NOOP;
}
}
// Block SIGCHLD so it can be handled by the signal thread
// Any threads created by this one (the main thread) should not
// modify their signal mask to unblock SIGCHLD
err = pthread_sigmask(SIG_BLOCK, &mask, nullptr);
if (err != 0) {
spdlog::error("pthread_sigmask failed in startSignalThread: {}", strerror(err));
exit(1);
void handleUserSignal(int signal, bool& reload) {
int i = 0;
for (auto& bar : waybar::Client::inst()->bars) {
switch (getActionForBar(bar.get(), signal)) {
case waybar::util::KillSignalAction::HIDE:
spdlog::debug("Visibility 'hide' for bar ", i);
bar->hide();
break;
case waybar::util::KillSignalAction::SHOW:
spdlog::debug("Visibility 'show' for bar ", i);
bar->show();
break;
case waybar::util::KillSignalAction::TOGGLE:
spdlog::debug("Visibility 'toggle' for bar ", i);
bar->toggle();
break;
case waybar::util::KillSignalAction::RELOAD:
spdlog::info("Reloading...");
reload = true;
waybar::Client::inst()->reset();
return;
case waybar::util::KillSignalAction::NOOP:
break;
}
i++;
}
}
// Must be called on the main thread.
//
// If this signal should restart or close the bar, this function will write
// `true` or `false`, respectively, into `reload`.
static void handleSignalMainThread(int signum, bool& reload) {
if (signum >= SIGRTMIN + 1 && signum <= SIGRTMAX) {
for (auto& bar : waybar::Client::inst()->bars) {
bar->handleSignal(signum);
}
return;
}
pthread_t thread_id;
err = pthread_create(&thread_id, nullptr, signalThread, nullptr);
if (err != 0) {
spdlog::error("pthread_create failed in startSignalThread: {}", strerror(err));
exit(1);
switch (signum) {
case SIGUSR1:
handleUserSignal(SIGUSR1, reload);
break;
case SIGUSR2:
handleUserSignal(SIGUSR2, reload);
break;
case SIGINT:
spdlog::info("Quitting.");
reload = false;
waybar::Client::inst()->reset();
break;
case SIGCHLD:
spdlog::debug("Received SIGCHLD in signalThread");
if (!reap.empty()) {
reap_mtx.lock();
for (auto it = reap.begin(); it != reap.end(); ++it) {
if (waitpid(*it, nullptr, WNOHANG) == *it) {
spdlog::debug("Reaped child with PID: {}", *it);
it = reap.erase(it);
}
}
reap_mtx.unlock();
}
break;
default:
spdlog::debug("Received signal with number {}, but not handling", signum);
break;
}
}
@@ -74,32 +158,16 @@ int main(int argc, char* argv[]) {
try {
auto* client = waybar::Client::inst();
std::signal(SIGUSR1, [](int /*signal*/) {
for (auto& bar : waybar::Client::inst()->bars) {
bar->toggle();
}
});
bool reload;
std::signal(SIGUSR2, [](int /*signal*/) {
spdlog::info("Reloading...");
reload = true;
waybar::Client::inst()->reset();
});
waybar::SafeSignal<int> posix_signal_received;
posix_signal_received.connect([&](int signum) { handleSignalMainThread(signum, reload); });
std::signal(SIGINT, [](int /*signal*/) {
spdlog::info("Quitting.");
reload = false;
waybar::Client::inst()->reset();
});
std::thread signal_thread([&]() { catchSignals(posix_signal_received); });
for (int sig = SIGRTMIN + 1; sig <= SIGRTMAX; ++sig) {
std::signal(sig, [](int sig) {
for (auto& bar : waybar::Client::inst()->bars) {
bar->handleSignal(sig);
}
});
}
startSignalThread();
// Every `std::thread` must be joined or detached.
// This thread should run forever, so detach it.
signal_thread.detach();
auto ret = 0;
do {
+12 -2
View File
@@ -38,10 +38,20 @@ auto waybar::modules::Backlight::update() -> void {
event_box_.show();
const uint8_t percent =
best->get_max() == 0 ? 100 : round(best->get_actual() * 100.0f / best->get_max());
std::string desc = fmt::format(fmt::runtime(format_), fmt::arg("percent", percent),
// Get the state and apply state-specific format if available
auto state = getState(percent);
std::string current_format = format_;
if (!state.empty()) {
std::string state_format_name = "format-" + state;
if (config_[state_format_name].isString()) {
current_format = config_[state_format_name].asString();
}
}
std::string desc = fmt::format(fmt::runtime(current_format), fmt::arg("percent", percent),
fmt::arg("icon", getIcon(percent)));
label_.set_markup(desc);
getState(percent);
if (tooltipEnabled()) {
std::string tooltip_format;
if (config_["tooltip-format"].isString()) {
+36 -7
View File
@@ -1,14 +1,16 @@
#include "modules/battery.hpp"
#include <algorithm>
#include <cctype>
#include "util/command.hpp"
#if defined(__FreeBSD__)
#include <sys/sysctl.h>
#endif
#include <spdlog/spdlog.h>
#include <iostream>
waybar::modules::Battery::Battery(const std::string& id, const Bar& bar, const Json::Value& config)
: ALabel(config, "battery", id, "{capacity}%", 60), bar_(bar) {
: ALabel(config, "battery", id, "{capacity}%", 60), last_event_(""), bar_(bar) {
#if defined(__linux__)
battery_watch_fd_ = inotify_init1(IN_CLOEXEC);
if (battery_watch_fd_ == -1) {
@@ -26,6 +28,7 @@ waybar::modules::Battery::Battery(const std::string& id, const Bar& bar, const J
throw std::runtime_error("Could not watch for battery plug/unplug");
}
#endif
spdlog::debug("battery: worker interval is {}", interval_.count());
worker();
}
@@ -677,18 +680,22 @@ auto waybar::modules::Battery::update() -> void {
}
auto status_pretty = status;
// Transform to lowercase and replace space with dash
std::transform(status.begin(), status.end(), status.begin(),
[](char ch) { return ch == ' ' ? '-' : std::tolower(ch); });
std::ranges::transform(status.begin(), status.end(), status.begin(),
[](char ch) { return ch == ' ' ? '-' : std::tolower(ch); });
auto format = format_;
auto state = getState(capacity, true);
processEvents(state, status, capacity);
setBarClass(state);
auto time_remaining_formatted = formatTimeRemaining(time_remaining);
if (tooltipEnabled()) {
std::string tooltip_text_default;
std::string tooltip_format = "{timeTo}";
if (time_remaining != 0) {
std::string time_to = std::string("Time to ") + ((time_remaining > 0) ? "empty" : "full");
tooltip_text_default = time_to + ": " + time_remaining_formatted;
if (time_remaining > 0) {
tooltip_text_default = std::string("Empty in ") + time_remaining_formatted;
} else {
tooltip_text_default = std::string("Full in ") + time_remaining_formatted;
}
} else {
tooltip_text_default = status_pretty;
}
@@ -701,7 +708,7 @@ auto waybar::modules::Battery::update() -> void {
} else if (config_["tooltip-format"].isString()) {
tooltip_format = config_["tooltip-format"].asString();
}
label_.set_tooltip_text(
label_.set_tooltip_markup(
fmt::format(fmt::runtime(tooltip_format), fmt::arg("timeTo", tooltip_text_default),
fmt::arg("power", power), fmt::arg("capacity", capacity),
fmt::arg("time", time_remaining_formatted), fmt::arg("cycles", cycles),
@@ -767,3 +774,25 @@ void waybar::modules::Battery::setBarClass(std::string& state) {
bar_.window.get_style_context()->add_class(new_class);
}
}
void waybar::modules::Battery::processEvents(std::string& state, std::string& status,
uint8_t capacity) {
// There are no events specified, skip
auto events = config_["events"];
if (!events.isObject() || events.empty()) {
return;
}
std::string event_name = fmt::format("on-{}-{}", status == "discharging" ? status : "charging",
state.empty() ? std::to_string(capacity) : state);
if (last_event_ != event_name) {
spdlog::debug("battery: triggering event {}", event_name);
if (events[event_name].isString()) {
std::string exec = events[event_name].asString();
// Execute the command if it is not empty
if (!exec.empty()) {
util::command::exec(exec, "");
}
}
last_event_ = event_name;
}
}

Some files were not shown because too many files have changed in this diff Show More