Merge branch 'master' into river-tags-monitor-focus
This commit is contained in:
@@ -1,14 +1,15 @@
|
|||||||
name: Build and Push Docker Image
|
name: Build and Push Docker Image
|
||||||
|
|
||||||
on:
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
schedule:
|
schedule:
|
||||||
# run every night at midnight
|
# run monthly
|
||||||
- cron: '0 0 * * *'
|
- cron: '0 0 1 * *'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-and-push:
|
build-and-push:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: github.repository == 'Alexays/Waybar'
|
if: github.event_name != 'schedule' || github.repository == 'Alexays/Waybar'
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false # don't fail the other jobs if one of the images fails to build
|
fail-fast: false # don't fail the other jobs if one of the images fails to build
|
||||||
matrix:
|
matrix:
|
||||||
|
|||||||
@@ -14,14 +14,14 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: Test in FreeBSD VM
|
- name: Test in FreeBSD VM
|
||||||
uses: cross-platform-actions/action@v0.25.0
|
uses: cross-platform-actions/action@v0.28.0
|
||||||
timeout-minutes: 180
|
timeout-minutes: 180
|
||||||
env:
|
env:
|
||||||
CPPFLAGS: '-isystem/usr/local/include'
|
CPPFLAGS: '-isystem/usr/local/include'
|
||||||
LDFLAGS: '-L/usr/local/lib'
|
LDFLAGS: '-L/usr/local/lib'
|
||||||
with:
|
with:
|
||||||
operating_system: freebsd
|
operating_system: freebsd
|
||||||
version: "14.1"
|
version: "14.2"
|
||||||
environment_variables: CPPFLAGS LDFLAGS
|
environment_variables: CPPFLAGS LDFLAGS
|
||||||
sync_files: runner-to-vm
|
sync_files: runner-to-vm
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
# vim: ft=Dockerfile
|
# vim: ft=Dockerfile
|
||||||
|
|
||||||
FROM debian:sid
|
FROM debian:sid-slim
|
||||||
|
|
||||||
RUN apt update && \
|
RUN apt update && \
|
||||||
apt install --no-install-recommends --no-install-suggests -y \
|
apt install --no-install-recommends --no-install-suggests -y \
|
||||||
|
|||||||
+1
-1
@@ -6,6 +6,6 @@ RUN export FEATURES="-ipc-sandbox -network-sandbox -pid-sandbox -sandbox -usersa
|
|||||||
emerge --sync && \
|
emerge --sync && \
|
||||||
eselect news read --quiet new 1>/dev/null 2>&1 && \
|
eselect news read --quiet new 1>/dev/null 2>&1 && \
|
||||||
emerge --verbose --update --deep --with-bdeps=y --backtrack=30 --newuse @world && \
|
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 \
|
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
|
media-sound/sndio gui-libs/gtk-layer-shell app-text/scdoc media-sound/playerctl dev-libs/iniparser sci-libs/fftw
|
||||||
|
|||||||
@@ -116,6 +116,36 @@ sudo apt install \
|
|||||||
libxkbregistry-dev
|
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>
|
Contributions welcome!<br>
|
||||||
Have fun :)<br>
|
Have fun :)<br>
|
||||||
|
|||||||
+5
-6
@@ -1,10 +1,9 @@
|
|||||||
(import
|
(import (
|
||||||
(
|
let
|
||||||
let lock = builtins.fromJSON (builtins.readFile ./flake.lock); in
|
lock = builtins.fromJSON (builtins.readFile ./flake.lock);
|
||||||
|
in
|
||||||
fetchTarball {
|
fetchTarball {
|
||||||
url = "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz";
|
url = "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz";
|
||||||
sha256 = lock.nodes.flake-compat.locked.narHash;
|
sha256 = lock.nodes.flake-compat.locked.narHash;
|
||||||
}
|
}
|
||||||
)
|
) { src = ./.; }).defaultNix
|
||||||
{ src = ./.; }
|
|
||||||
).defaultNix
|
|
||||||
|
|||||||
Generated
+6
-6
@@ -3,11 +3,11 @@
|
|||||||
"flake-compat": {
|
"flake-compat": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1733328505,
|
"lastModified": 1747046372,
|
||||||
"narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=",
|
"narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=",
|
||||||
"owner": "edolstra",
|
"owner": "edolstra",
|
||||||
"repo": "flake-compat",
|
"repo": "flake-compat",
|
||||||
"rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec",
|
"rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -18,11 +18,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1743315132,
|
"lastModified": 1753694789,
|
||||||
"narHash": "sha256-6hl6L/tRnwubHcA4pfUUtk542wn2Om+D4UnDhlDW9BE=",
|
"narHash": "sha256-cKgvtz6fKuK1Xr5LQW/zOUiAC0oSQoA9nOISB0pJZqM=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "52faf482a3889b7619003c0daec593a1912fddc1",
|
"rev": "dc9637876d0dcc8c9e5e22986b857632effeb727",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|||||||
@@ -9,48 +9,97 @@
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = { self, nixpkgs, ... }:
|
outputs =
|
||||||
|
{ self, nixpkgs, ... }:
|
||||||
let
|
let
|
||||||
inherit (nixpkgs) lib;
|
inherit (nixpkgs) lib;
|
||||||
genSystems = func: lib.genAttrs [
|
genSystems =
|
||||||
|
func:
|
||||||
|
lib.genAttrs
|
||||||
|
[
|
||||||
"x86_64-linux"
|
"x86_64-linux"
|
||||||
"aarch64-linux"
|
"aarch64-linux"
|
||||||
]
|
]
|
||||||
(system: func (import nixpkgs {
|
(
|
||||||
|
system:
|
||||||
|
func (
|
||||||
|
import nixpkgs {
|
||||||
inherit system;
|
inherit system;
|
||||||
overlays = with self.overlays; [
|
overlays = with self.overlays; [
|
||||||
waybar
|
waybar
|
||||||
];
|
];
|
||||||
}));
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
mkDate = longDate: (lib.concatStringsSep "-" [
|
mkDate =
|
||||||
|
longDate:
|
||||||
|
(lib.concatStringsSep "-" [
|
||||||
(builtins.substring 0 4 longDate)
|
(builtins.substring 0 4 longDate)
|
||||||
(builtins.substring 4 2 longDate)
|
(builtins.substring 4 2 longDate)
|
||||||
(builtins.substring 6 2 longDate)
|
(builtins.substring 6 2 longDate)
|
||||||
]);
|
]);
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
devShells = genSystems
|
devShells = genSystems (pkgs: {
|
||||||
(pkgs:
|
default = pkgs.mkShell {
|
||||||
{
|
|
||||||
default =
|
|
||||||
pkgs.mkShell
|
|
||||||
{
|
|
||||||
name = "waybar-shell";
|
name = "waybar-shell";
|
||||||
|
|
||||||
# inherit attributes from upstream nixpkgs derivation
|
# inherit attributes from upstream nixpkgs derivation
|
||||||
inherit (pkgs.waybar) buildInputs depsBuildBuild depsBuildBuildPropagated depsBuildTarget
|
inherit (pkgs.waybar)
|
||||||
depsBuildTargetPropagated depsHostHost depsHostHostPropagated depsTargetTarget
|
buildInputs
|
||||||
depsTargetTargetPropagated propagatedBuildInputs propagatedNativeBuildInputs strictDeps;
|
depsBuildBuild
|
||||||
|
depsBuildBuildPropagated
|
||||||
|
depsBuildTarget
|
||||||
|
depsBuildTargetPropagated
|
||||||
|
depsHostHost
|
||||||
|
depsHostHostPropagated
|
||||||
|
depsTargetTarget
|
||||||
|
depsTargetTargetPropagated
|
||||||
|
propagatedBuildInputs
|
||||||
|
propagatedNativeBuildInputs
|
||||||
|
strictDeps
|
||||||
|
;
|
||||||
|
|
||||||
# overrides for local development
|
# overrides for local development
|
||||||
nativeBuildInputs = pkgs.waybar.nativeBuildInputs ++ (with pkgs; [
|
nativeBuildInputs =
|
||||||
|
pkgs.waybar.nativeBuildInputs
|
||||||
|
++ (with pkgs; [
|
||||||
|
nixfmt-rfc-style
|
||||||
clang-tools
|
clang-tools
|
||||||
gdb
|
gdb
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
tree-root-file = ".git/index";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
overlays = {
|
overlays = {
|
||||||
default = self.overlays.waybar;
|
default = self.overlays.waybar;
|
||||||
waybar = final: prev: {
|
waybar = final: prev: {
|
||||||
@@ -58,11 +107,15 @@
|
|||||||
waybar = prev.waybar;
|
waybar = prev.waybar;
|
||||||
# take the first "version: '...'" from meson.build
|
# take the first "version: '...'" from meson.build
|
||||||
version =
|
version =
|
||||||
(builtins.head (builtins.split "'"
|
(builtins.head (
|
||||||
(builtins.elemAt
|
builtins.split "'" (
|
||||||
(builtins.split " version: '" (builtins.readFile ./meson.build))
|
builtins.elemAt (builtins.split " version: '" (builtins.readFile ./meson.build)) 2
|
||||||
2)))
|
)
|
||||||
+ "+date=" + (mkDate (self.lastModifiedDate or "19700101")) + "_" + (self.shortRev or "dirty");
|
))
|
||||||
|
+ "+date="
|
||||||
|
+ (mkDate (self.lastModifiedDate or "19700101"))
|
||||||
|
+ "_"
|
||||||
|
+ (self.shortRev or "dirty");
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
+1
-1
@@ -36,6 +36,7 @@ class AModule : public IModule {
|
|||||||
SCROLL_DIR getScrollDir(GdkEventScroll *e);
|
SCROLL_DIR getScrollDir(GdkEventScroll *e);
|
||||||
bool tooltipEnabled() const;
|
bool tooltipEnabled() const;
|
||||||
|
|
||||||
|
std::vector<int> pid_children_;
|
||||||
const std::string name_;
|
const std::string name_;
|
||||||
const Json::Value &config_;
|
const Json::Value &config_;
|
||||||
Gtk::EventBox event_box_;
|
Gtk::EventBox event_box_;
|
||||||
@@ -54,7 +55,6 @@ class AModule : public IModule {
|
|||||||
const bool isTooltip;
|
const bool isTooltip;
|
||||||
const bool isExpand;
|
const bool isExpand;
|
||||||
bool hasUserEvents_;
|
bool hasUserEvents_;
|
||||||
std::vector<int> pid_;
|
|
||||||
gdouble distance_scrolled_y_;
|
gdouble distance_scrolled_y_;
|
||||||
gdouble distance_scrolled_x_;
|
gdouble distance_scrolled_x_;
|
||||||
std::map<std::string, std::string> eventActionMap_;
|
std::map<std::string, std::string> eventActionMap_;
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
#include "AModule.hpp"
|
#include "AModule.hpp"
|
||||||
#include "group.hpp"
|
#include "group.hpp"
|
||||||
|
#include "util/kill_signal.hpp"
|
||||||
#include "xdg-output-unstable-v1-client-protocol.h"
|
#include "xdg-output-unstable-v1-client-protocol.h"
|
||||||
|
|
||||||
namespace waybar {
|
namespace waybar {
|
||||||
@@ -68,7 +69,11 @@ class Bar : public sigc::trackable {
|
|||||||
void setMode(const std::string &mode);
|
void setMode(const std::string &mode);
|
||||||
void setVisible(bool value);
|
void setVisible(bool value);
|
||||||
void toggle();
|
void toggle();
|
||||||
|
void show();
|
||||||
|
void hide();
|
||||||
void handleSignal(int);
|
void handleSignal(int);
|
||||||
|
util::KillSignalAction getOnSigusr1Action();
|
||||||
|
util::KillSignalAction getOnSigusr2Action();
|
||||||
|
|
||||||
struct waybar_output *output;
|
struct waybar_output *output;
|
||||||
Json::Value config;
|
Json::Value config;
|
||||||
@@ -118,6 +123,9 @@ class Bar : public sigc::trackable {
|
|||||||
std::unique_ptr<BarIpcClient> _ipc_client;
|
std::unique_ptr<BarIpcClient> _ipc_client;
|
||||||
#endif
|
#endif
|
||||||
std::vector<std::shared_ptr<waybar::AModule>> modules_all_;
|
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
|
} // namespace waybar
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ class Config {
|
|||||||
void setupConfig(Json::Value &dst, const std::string &config_file, int depth);
|
void setupConfig(Json::Value &dst, const std::string &config_file, int depth);
|
||||||
void resolveConfigIncludes(Json::Value &config, int depth);
|
void resolveConfigIncludes(Json::Value &config, int depth);
|
||||||
void mergeConfig(Json::Value &a_config_, Json::Value &b_config_);
|
void mergeConfig(Json::Value &a_config_, Json::Value &b_config_);
|
||||||
|
static std::optional<std::string> findIncludePath(const std::string &name);
|
||||||
|
|
||||||
std::string config_file_;
|
std::string config_file_;
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ class Battery : public ALabel {
|
|||||||
std::tuple<uint8_t, float, std::string, float, uint16_t, float> getInfos();
|
std::tuple<uint8_t, float, std::string, float, uint16_t, float> getInfos();
|
||||||
const std::string formatTimeRemaining(float hoursRemaining);
|
const std::string formatTimeRemaining(float hoursRemaining);
|
||||||
void setBarClass(std::string&);
|
void setBarClass(std::string&);
|
||||||
|
void processEvents(std::string& state, std::string& status, uint8_t capacity);
|
||||||
|
|
||||||
int global_watch;
|
int global_watch;
|
||||||
std::map<fs::path, int> batteries_;
|
std::map<fs::path, int> batteries_;
|
||||||
@@ -43,6 +44,7 @@ class Battery : public ALabel {
|
|||||||
int global_watch_fd_;
|
int global_watch_fd_;
|
||||||
std::mutex battery_list_mutex_;
|
std::mutex battery_list_mutex_;
|
||||||
std::string old_status_;
|
std::string old_status_;
|
||||||
|
std::string last_event_;
|
||||||
bool warnFirstTime_{true};
|
bool warnFirstTime_{true};
|
||||||
const Bar& bar_;
|
const Bar& bar_;
|
||||||
|
|
||||||
|
|||||||
+11
-11
@@ -42,24 +42,24 @@ class Clock final : public ALabel {
|
|||||||
int cldWnLen_{3}; // calendar week number length
|
int cldWnLen_{3}; // calendar week number length
|
||||||
const int cldMonColLen_{20}; // calendar month column length
|
const int cldMonColLen_{20}; // calendar month column length
|
||||||
WS cldWPos_{WS::HIDDEN}; // calendar week side to print
|
WS cldWPos_{WS::HIDDEN}; // calendar week side to print
|
||||||
months cldCurrShift_{0}; // calendar months shift
|
date::months cldCurrShift_{0}; // calendar months shift
|
||||||
int cldShift_{1}; // calendar months shift factor
|
int cldShift_{1}; // calendar months shift factor
|
||||||
year_month_day cldYearShift_; // calendar Year mode. Cached ymd
|
date::year_month_day cldYearShift_; // calendar Year mode. Cached ymd
|
||||||
std::string cldYearCached_; // calendar Year mode. Cached calendar
|
std::string cldYearCached_; // calendar Year mode. Cached calendar
|
||||||
year_month cldMonShift_; // calendar Month mode. Cached ym
|
date::year_month cldMonShift_; // calendar Month mode. Cached ym
|
||||||
std::string cldMonCached_; // calendar Month mode. Cached calendar
|
std::string cldMonCached_; // calendar Month mode. Cached calendar
|
||||||
day cldBaseDay_{0}; // calendar Cached day. Is used when today is changing(midnight)
|
date::day cldBaseDay_{0}; // calendar Cached day. Is used when today is changing(midnight)
|
||||||
std::string cldText_{""}; // calendar text to print
|
std::string cldText_{""}; // calendar text to print
|
||||||
CldMode cldMode_{CldMode::MONTH};
|
CldMode cldMode_{CldMode::MONTH};
|
||||||
auto get_calendar(const year_month_day& today, const year_month_day& ymd, const time_zone* tz)
|
auto get_calendar(const date::year_month_day& today, const date::year_month_day& ymd,
|
||||||
-> const std::string;
|
const date::time_zone* tz) -> const std::string;
|
||||||
|
|
||||||
// get local time zone
|
// get local time zone
|
||||||
auto local_zone() -> const time_zone*;
|
auto local_zone() -> const date::time_zone*;
|
||||||
|
|
||||||
// time zoned time in tooltip
|
// time zoned time in tooltip
|
||||||
const bool tzInTooltip_; // if need to print time zones text
|
const bool tzInTooltip_; // if need to print time zones text
|
||||||
std::vector<const time_zone*> tzList_; // time zones list
|
std::vector<const date::time_zone*> tzList_; // time zones list
|
||||||
int tzCurrIdx_; // current time zone index for tzList_
|
int tzCurrIdx_; // current time zone index for tzList_
|
||||||
std::string tzText_{""}; // time zones text to print
|
std::string tzText_{""}; // time zones text to print
|
||||||
util::SleeperThread thread_;
|
util::SleeperThread thread_;
|
||||||
@@ -67,10 +67,10 @@ class Clock final : public ALabel {
|
|||||||
// ordinal date in tooltip
|
// ordinal date in tooltip
|
||||||
const bool ordInTooltip_;
|
const bool ordInTooltip_;
|
||||||
std::string ordText_{""};
|
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 getTZtext(date::sys_seconds now) -> std::string;
|
||||||
auto first_day_of_week() -> weekday;
|
auto first_day_of_week() -> date::weekday;
|
||||||
// Module actions
|
// Module actions
|
||||||
void cldModeSwitch();
|
void cldModeSwitch();
|
||||||
void cldShift_up();
|
void cldShift_up();
|
||||||
|
|||||||
@@ -0,0 +1,143 @@
|
|||||||
|
#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_;
|
||||||
|
|
||||||
|
bool needs_updating_ = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // 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
|
||||||
@@ -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
|
||||||
@@ -46,4 +46,5 @@ class IPC {
|
|||||||
};
|
};
|
||||||
|
|
||||||
inline bool modulesReady = false;
|
inline bool modulesReady = false;
|
||||||
|
inline std::unique_ptr<IPC> gIPC;
|
||||||
}; // namespace waybar::modules::hyprland
|
}; // namespace waybar::modules::hyprland
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "AAppIconLabel.hpp"
|
||||||
|
#include "bar.hpp"
|
||||||
|
#include "modules/hyprland/backend.hpp"
|
||||||
|
#include "util/json.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;
|
||||||
|
};
|
||||||
|
|
||||||
|
static auto getActiveWorkspace(const std::string&) -> Workspace;
|
||||||
|
static 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_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace waybar::modules::hyprland
|
||||||
@@ -26,34 +26,48 @@ namespace waybar::modules::hyprland {
|
|||||||
|
|
||||||
class Workspaces;
|
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 {
|
class WindowCreationPayload {
|
||||||
public:
|
public:
|
||||||
WindowCreationPayload(std::string workspace_name, WindowAddress window_address,
|
WindowCreationPayload(std::string workspace_name, WindowAddress window_address,
|
||||||
std::string window_repr);
|
WindowRepr window_repr);
|
||||||
WindowCreationPayload(std::string workspace_name, WindowAddress window_address,
|
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);
|
WindowCreationPayload(Json::Value const& client_data);
|
||||||
|
|
||||||
int incrementTimeSpentUncreated();
|
int incrementTimeSpentUncreated();
|
||||||
bool isEmpty(Workspaces& workspace_manager);
|
bool isEmpty(Workspaces& workspace_manager);
|
||||||
bool reprIsReady() const { return std::holds_alternative<Repr>(m_window); }
|
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; }
|
std::string getWorkspaceName() const { return m_workspaceName; }
|
||||||
WindowAddress getAddress() const { return m_windowAddress; }
|
WindowAddress getAddress() const { return m_windowAddress; }
|
||||||
|
|
||||||
void moveToWorksace(std::string& new_workspace_name);
|
void moveToWorkspace(std::string& new_workspace_name);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void clearAddr();
|
void clearAddr();
|
||||||
void clearWorkspaceName();
|
void clearWorkspaceName();
|
||||||
|
|
||||||
using Repr = std::string;
|
using Repr = WindowRepr;
|
||||||
using ClassAndTitle = std::pair<std::string, std::string>;
|
using ClassAndTitle = std::pair<std::string, std::string>;
|
||||||
std::variant<Repr, ClassAndTitle> m_window;
|
std::variant<Repr, ClassAndTitle> m_window;
|
||||||
|
|
||||||
WindowAddress m_windowAddress;
|
WindowAddress m_windowAddress;
|
||||||
std::string m_workspaceName;
|
std::string m_workspaceName;
|
||||||
|
bool m_isActive = false;
|
||||||
|
|
||||||
int m_timeSpentUncreated = 0;
|
int m_timeSpentUncreated = 0;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -54,15 +54,18 @@ class Workspace {
|
|||||||
void setWindows(uint value) { m_windows = value; };
|
void setWindows(uint value) { m_windows = value; };
|
||||||
void setName(std::string const& value) { m_name = value; };
|
void setName(std::string const& value) { m_name = value; };
|
||||||
void setOutput(std::string const& value) { m_output = value; };
|
void setOutput(std::string const& value) { m_output = value; };
|
||||||
bool containsWindow(WindowAddress const& addr) const { return m_windowMap.contains(addr); }
|
bool containsWindow(WindowAddress const& addr) const {
|
||||||
void insertWindow(WindowCreationPayload create_window_paylod);
|
return std::ranges::any_of(m_windowMap,
|
||||||
std::string removeWindow(WindowAddress const& addr);
|
[&addr](const auto& window) { return window.address == addr; });
|
||||||
|
};
|
||||||
|
void insertWindow(WindowCreationPayload create_window_payload);
|
||||||
void initializeWindowMap(const Json::Value& clients_data);
|
void initializeWindowMap(const Json::Value& clients_data);
|
||||||
|
void setActiveWindow(WindowAddress const& addr);
|
||||||
|
|
||||||
bool onWindowOpened(WindowCreationPayload const& create_window_paylod);
|
bool onWindowOpened(WindowCreationPayload const& create_window_payload);
|
||||||
std::optional<std::string> closeWindow(WindowAddress const& addr);
|
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:
|
private:
|
||||||
Workspaces& m_workspaceManager;
|
Workspaces& m_workspaceManager;
|
||||||
@@ -78,11 +81,16 @@ class Workspace {
|
|||||||
bool m_isUrgent = false;
|
bool m_isUrgent = false;
|
||||||
bool m_isVisible = false;
|
bool m_isVisible = false;
|
||||||
|
|
||||||
std::map<WindowAddress, std::string> m_windowMap;
|
std::vector<WindowRepr> m_windowMap;
|
||||||
|
|
||||||
Gtk::Button m_button;
|
Gtk::Button m_button;
|
||||||
Gtk::Box m_content;
|
Gtk::Box m_content;
|
||||||
Gtk::Label m_label;
|
Gtk::Label m_labelBefore;
|
||||||
|
Gtk::Label m_labelAfter;
|
||||||
|
|
||||||
|
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;
|
IPC& m_ipc;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <gtkmm/button.h>
|
#include <gtkmm/button.h>
|
||||||
|
#include <gtkmm/enums.h>
|
||||||
#include <gtkmm/label.h>
|
#include <gtkmm/label.h>
|
||||||
#include <json/value.h>
|
#include <json/value.h>
|
||||||
|
|
||||||
@@ -18,6 +19,7 @@
|
|||||||
#include "modules/hyprland/windowcreationpayload.hpp"
|
#include "modules/hyprland/windowcreationpayload.hpp"
|
||||||
#include "modules/hyprland/workspace.hpp"
|
#include "modules/hyprland/workspace.hpp"
|
||||||
#include "util/enum.hpp"
|
#include "util/enum.hpp"
|
||||||
|
#include "util/icon_loader.hpp"
|
||||||
#include "util/regex_collection.hpp"
|
#include "util/regex_collection.hpp"
|
||||||
|
|
||||||
using WindowAddress = std::string;
|
using WindowAddress = std::string;
|
||||||
@@ -37,19 +39,32 @@ class Workspaces : public AModule, public EventHandler {
|
|||||||
auto showSpecial() const -> bool { return m_showSpecial; }
|
auto showSpecial() const -> bool { return m_showSpecial; }
|
||||||
auto activeOnly() const -> bool { return m_activeOnly; }
|
auto activeOnly() const -> bool { return m_activeOnly; }
|
||||||
auto specialVisibleOnly() const -> bool { return m_specialVisibleOnly; }
|
auto specialVisibleOnly() const -> bool { return m_specialVisibleOnly; }
|
||||||
|
auto persistentOnly() const -> bool { return m_persistentOnly; }
|
||||||
auto moveToMonitor() const -> bool { return m_moveToMonitor; }
|
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 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 onClickWindow() const -> std::string { return m_onClickWindow; }
|
||||||
|
auto getIgnoredWindows() const -> std::vector<std::regex> { return m_ignoreWindows; }
|
||||||
|
|
||||||
std::string getRewrite(std::string window_class, std::string window_title);
|
std::string getRewrite(std::string window_class, std::string window_title);
|
||||||
std::string& getWindowSeparator() { return m_formatWindowSeparator; }
|
std::string& getWindowSeparator() { return m_formatWindowSeparator; }
|
||||||
bool isWorkspaceIgnored(std::string const& workspace_name);
|
bool isWorkspaceIgnored(std::string const& workspace_name);
|
||||||
|
|
||||||
bool windowRewriteConfigUsesTitle() const { return m_anyWindowRewriteRuleUsesTitle; }
|
bool windowRewriteConfigUsesTitle() const { return m_anyWindowRewriteRuleUsesTitle; }
|
||||||
|
const IconLoader& iconLoader() const { return m_iconLoader; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onEvent(const std::string& e) override;
|
void onEvent(const std::string& e) override;
|
||||||
void updateWindowCount();
|
void updateWindowCount();
|
||||||
|
void sortSpecialCentered();
|
||||||
void sortWorkspaces();
|
void sortWorkspaces();
|
||||||
void createWorkspace(Json::Value const& workspace_data,
|
void createWorkspace(Json::Value const& workspace_data,
|
||||||
Json::Value const& clients_data = Json::Value::nullRef);
|
Json::Value const& clients_data = Json::Value::nullRef);
|
||||||
@@ -68,6 +83,7 @@ class Workspaces : public AModule, public EventHandler {
|
|||||||
auto populateIgnoreWorkspacesConfig(const Json::Value& config) -> void;
|
auto populateIgnoreWorkspacesConfig(const Json::Value& config) -> void;
|
||||||
auto populateFormatWindowSeparatorConfig(const Json::Value& config) -> void;
|
auto populateFormatWindowSeparatorConfig(const Json::Value& config) -> void;
|
||||||
auto populateWindowRewriteConfig(const Json::Value& config) -> void;
|
auto populateWindowRewriteConfig(const Json::Value& config) -> void;
|
||||||
|
auto populateWorkspaceTaskbarConfig(const Json::Value& config) -> void;
|
||||||
|
|
||||||
void registerIpc();
|
void registerIpc();
|
||||||
|
|
||||||
@@ -90,6 +106,7 @@ class Workspaces : public AModule, public EventHandler {
|
|||||||
void onWindowMoved(std::string const& payload);
|
void onWindowMoved(std::string const& payload);
|
||||||
|
|
||||||
void onWindowTitleEvent(std::string const& payload);
|
void onWindowTitleEvent(std::string const& payload);
|
||||||
|
void onActiveWindowChanged(WindowAddress const& payload);
|
||||||
|
|
||||||
void onConfigReloaded();
|
void onConfigReloaded();
|
||||||
|
|
||||||
@@ -122,23 +139,26 @@ class Workspaces : public AModule, public EventHandler {
|
|||||||
bool m_showSpecial = false;
|
bool m_showSpecial = false;
|
||||||
bool m_activeOnly = false;
|
bool m_activeOnly = false;
|
||||||
bool m_specialVisibleOnly = false;
|
bool m_specialVisibleOnly = false;
|
||||||
|
bool m_persistentOnly = false;
|
||||||
bool m_moveToMonitor = false;
|
bool m_moveToMonitor = false;
|
||||||
Json::Value m_persistentWorkspaceConfig;
|
Json::Value m_persistentWorkspaceConfig;
|
||||||
|
|
||||||
// Map for windows stored in workspaces not present in the current bar.
|
// Map for windows stored in workspaces not present in the current bar.
|
||||||
// This happens when the user has multiple monitors (hence, multiple bars)
|
// This happens when the user has multiple monitors (hence, multiple bars)
|
||||||
// and doesn't share windows accross bars (a.k.a `all-outputs` = false)
|
// and doesn't share windows across bars (a.k.a `all-outputs` = false)
|
||||||
std::map<WindowAddress, std::string> m_orphanWindowMap;
|
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;
|
util::EnumParser<SortMethod> m_enumParser;
|
||||||
SortMethod m_sortBy = SortMethod::DEFAULT;
|
SortMethod m_sortBy = SortMethod::DEFAULT;
|
||||||
std::map<std::string, SortMethod> m_sortMap = {{"ID", SortMethod::ID},
|
std::map<std::string, SortMethod> m_sortMap = {{"ID", SortMethod::ID},
|
||||||
{"NAME", SortMethod::NAME},
|
{"NAME", SortMethod::NAME},
|
||||||
{"NUMBER", SortMethod::NUMBER},
|
{"NUMBER", SortMethod::NUMBER},
|
||||||
|
{"SPECIAL-CENTERED", SortMethod::SPECIAL_CENTERED},
|
||||||
{"DEFAULT", SortMethod::DEFAULT}};
|
{"DEFAULT", SortMethod::DEFAULT}};
|
||||||
|
|
||||||
std::string m_format;
|
std::string m_formatBefore;
|
||||||
|
std::string m_formatAfter;
|
||||||
|
|
||||||
std::map<std::string, std::string> m_iconsMap;
|
std::map<std::string, std::string> m_iconsMap;
|
||||||
util::RegexCollection m_windowRewriteRules;
|
util::RegexCollection m_windowRewriteRules;
|
||||||
@@ -154,7 +174,20 @@ class Workspaces : public AModule, public EventHandler {
|
|||||||
std::vector<std::string> m_workspacesToRemove;
|
std::vector<std::string> m_workspacesToRemove;
|
||||||
std::vector<WindowCreationPayload> m_windowsToCreate;
|
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;
|
||||||
|
std::string m_onClickWindow;
|
||||||
|
std::string m_currentActiveWindowAddress;
|
||||||
|
|
||||||
std::vector<std::regex> m_ignoreWorkspaces;
|
std::vector<std::regex> m_ignoreWorkspaces;
|
||||||
|
std::vector<std::regex> m_ignoreWindows;
|
||||||
|
|
||||||
std::mutex m_mutex;
|
std::mutex m_mutex;
|
||||||
const Bar& m_bar;
|
const Bar& m_bar;
|
||||||
|
|||||||
+25
-24
@@ -9,6 +9,7 @@
|
|||||||
#include <sys/epoll.h>
|
#include <sys/epoll.h>
|
||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "ALabel.hpp"
|
#include "ALabel.hpp"
|
||||||
#include "util/sleeper_thread.hpp"
|
#include "util/sleeper_thread.hpp"
|
||||||
@@ -27,8 +28,8 @@ class Network : public ALabel {
|
|||||||
auto update() -> void override;
|
auto update() -> void override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const uint8_t MAX_RETRY = 5;
|
static const uint8_t MAX_RETRY{5};
|
||||||
static const uint8_t EPOLL_MAX = 200;
|
static const uint8_t EPOLL_MAX{200};
|
||||||
|
|
||||||
static int handleEvents(struct nl_msg*, void*);
|
static int handleEvents(struct nl_msg*, void*);
|
||||||
static int handleEventsDone(struct nl_msg*, void*);
|
static int handleEventsDone(struct nl_msg*, void*);
|
||||||
@@ -44,44 +45,44 @@ class Network : public ALabel {
|
|||||||
void parseFreq(struct nlattr**);
|
void parseFreq(struct nlattr**);
|
||||||
void parseBssid(struct nlattr**);
|
void parseBssid(struct nlattr**);
|
||||||
bool associatedOrJoined(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;
|
auto getInfo() -> void;
|
||||||
const std::string getNetworkState() const;
|
const std::string getNetworkState() const;
|
||||||
void clearIface();
|
void clearIface();
|
||||||
bool wildcardMatch(const std::string& pattern, const std::string& text) const;
|
|
||||||
std::optional<std::pair<unsigned long long, unsigned long long>> readBandwidthUsage();
|
std::optional<std::pair<unsigned long long, unsigned long long>> readBandwidthUsage();
|
||||||
|
|
||||||
int ifid_;
|
int ifid_{-1};
|
||||||
ip_addr_pref addr_pref_;
|
ip_addr_pref addr_pref_{ip_addr_pref::IPV4};
|
||||||
struct sockaddr_nl nladdr_ = {0};
|
struct sockaddr_nl nladdr_{0};
|
||||||
struct nl_sock* sock_ = nullptr;
|
struct nl_sock* sock_{nullptr};
|
||||||
struct nl_sock* ev_sock_ = nullptr;
|
struct nl_sock* ev_sock_{nullptr};
|
||||||
int efd_;
|
int efd_{-1};
|
||||||
int ev_fd_;
|
int ev_fd_{-1};
|
||||||
int nl80211_id_;
|
int nl80211_id_{-1};
|
||||||
std::mutex mutex_;
|
std::mutex mutex_;
|
||||||
|
|
||||||
bool want_route_dump_;
|
bool want_route_dump_{false};
|
||||||
bool want_link_dump_;
|
bool want_link_dump_{false};
|
||||||
bool want_addr_dump_;
|
bool want_addr_dump_{false};
|
||||||
bool dump_in_progress_;
|
bool dump_in_progress_{false};
|
||||||
bool is_p2p_;
|
bool is_p2p_{false};
|
||||||
|
|
||||||
unsigned long long bandwidth_down_total_;
|
unsigned long long bandwidth_down_total_{0};
|
||||||
unsigned long long bandwidth_up_total_;
|
unsigned long long bandwidth_up_total_{0};
|
||||||
|
|
||||||
std::string state_;
|
std::string state_;
|
||||||
std::string essid_;
|
std::string essid_;
|
||||||
std::string bssid_;
|
std::string bssid_;
|
||||||
bool carrier_;
|
bool carrier_{false};
|
||||||
std::string ifname_;
|
std::string ifname_;
|
||||||
std::string ipaddr_;
|
std::string ipaddr_;
|
||||||
std::string ipaddr6_;
|
std::string ipaddr6_;
|
||||||
std::string gwaddr_;
|
std::string gwaddr_;
|
||||||
std::string netmask_;
|
std::string netmask_;
|
||||||
std::string netmask6_;
|
std::string netmask6_;
|
||||||
int cidr_;
|
int cidr_{0};
|
||||||
int cidr6_;
|
int cidr6_{0};
|
||||||
int32_t signal_strength_dbm_;
|
int32_t signal_strength_dbm_;
|
||||||
uint8_t signal_strength_;
|
uint8_t signal_strength_;
|
||||||
std::string signal_strength_app_;
|
std::string signal_strength_app_;
|
||||||
@@ -90,9 +91,9 @@ class Network : public ALabel {
|
|||||||
util::SleeperThread thread_;
|
util::SleeperThread thread_;
|
||||||
util::SleeperThread thread_timer_;
|
util::SleeperThread thread_timer_;
|
||||||
#ifdef WANT_RFKILL
|
#ifdef WANT_RFKILL
|
||||||
util::Rfkill rfkill_;
|
util::Rfkill rfkill_{RFKILL_TYPE_WLAN};
|
||||||
#endif
|
#endif
|
||||||
float frequency_;
|
float frequency_{0};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace waybar::modules
|
} // namespace waybar::modules
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ class Privacy : public AModule {
|
|||||||
uint iconSpacing = 4;
|
uint iconSpacing = 4;
|
||||||
uint iconSize = 20;
|
uint iconSize = 20;
|
||||||
uint transition_duration = 250;
|
uint transition_duration = 250;
|
||||||
|
std::set<std::pair<PrivacyNodeType, std::string>> ignore;
|
||||||
|
bool ignore_monitor = true;
|
||||||
|
|
||||||
std::shared_ptr<util::PipewireBackend::PipewireBackend> backend = nullptr;
|
std::shared_ptr<util::PipewireBackend::PipewireBackend> backend = nullptr;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ class Layout : public waybar::ALabel {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
const waybar::Bar &bar_;
|
const waybar::Bar &bar_;
|
||||||
|
std::string name_;
|
||||||
struct wl_output *output_; // stores the output this module belongs to
|
struct wl_output *output_; // stores the output this module belongs to
|
||||||
struct wl_output *focused_output_; // stores the currently focused output
|
struct wl_output *focused_output_; // stores the currently focused output
|
||||||
struct zriver_output_status_v1 *output_status_;
|
struct zriver_output_status_v1 *output_status_;
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ class Tray : public AModule {
|
|||||||
void onRemove(std::unique_ptr<Item>& item);
|
void onRemove(std::unique_ptr<Item>& item);
|
||||||
|
|
||||||
static inline std::size_t nb_hosts_ = 0;
|
static inline std::size_t nb_hosts_ = 0;
|
||||||
|
bool show_passive_ = false;
|
||||||
Gtk::Box box_;
|
Gtk::Box box_;
|
||||||
SNI::Watcher::singleton watcher_;
|
SNI::Watcher::singleton watcher_;
|
||||||
SNI::Host host_;
|
SNI::Host host_;
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ class Language : public ALabel, public sigc::trackable {
|
|||||||
auto update() -> void override;
|
auto update() -> void override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum class DispayedShortFlag { None = 0, ShortName = 1, ShortDescription = 1 << 1 };
|
enum class DisplayedShortFlag { None = 0, ShortName = 1, ShortDescription = 1 << 1 };
|
||||||
|
|
||||||
struct Layout {
|
struct Layout {
|
||||||
std::string full_name;
|
std::string full_name;
|
||||||
@@ -58,7 +58,7 @@ class Language : public ALabel, public sigc::trackable {
|
|||||||
std::map<std::string, Layout> layouts_map_;
|
std::map<std::string, Layout> layouts_map_;
|
||||||
bool hide_single_;
|
bool hide_single_;
|
||||||
bool is_variant_displayed;
|
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_;
|
util::JsonParser parser_;
|
||||||
std::mutex mutex_;
|
std::mutex mutex_;
|
||||||
|
|||||||
@@ -19,10 +19,11 @@ class Window : public AAppIconLabel, public sigc::trackable {
|
|||||||
auto update() -> void override;
|
auto update() -> void override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setClass(std::string classname, bool enable);
|
void setClass(const std::string& classname, bool enable);
|
||||||
void onEvent(const struct Ipc::ipc_response&);
|
void onEvent(const struct Ipc::ipc_response&);
|
||||||
void onCmd(const struct Ipc::ipc_response&);
|
void onCmd(const struct Ipc::ipc_response&);
|
||||||
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);
|
getFocusedNode(const Json::Value& nodes, std::string& output);
|
||||||
void getTree();
|
void getTree();
|
||||||
|
|
||||||
@@ -35,6 +36,7 @@ class Window : public AAppIconLabel, public sigc::trackable {
|
|||||||
std::string old_app_id_;
|
std::string old_app_id_;
|
||||||
std::size_t app_nb_;
|
std::size_t app_nb_;
|
||||||
std::string shell_;
|
std::string shell_;
|
||||||
|
std::string marks_;
|
||||||
int floating_count_;
|
int floating_count_;
|
||||||
util::JsonParser parser_;
|
util::JsonParser parser_;
|
||||||
std::mutex mutex_;
|
std::mutex mutex_;
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ class Workspaces : public AModule, public sigc::trackable {
|
|||||||
std::vector<std::string> high_priority_named_;
|
std::vector<std::string> high_priority_named_;
|
||||||
std::vector<std::string> workspaces_order_;
|
std::vector<std::string> workspaces_order_;
|
||||||
Gtk::Box box_;
|
Gtk::Box box_;
|
||||||
std::string m_formatWindowSeperator;
|
std::string m_formatWindowSeparator;
|
||||||
util::RegexCollection m_windowRewriteRules;
|
util::RegexCollection m_windowRewriteRules;
|
||||||
util::JsonParser parser_;
|
util::JsonParser parser_;
|
||||||
std::unordered_map<std::string, Gtk::Button> buttons_;
|
std::unordered_map<std::string, Gtk::Button> buttons_;
|
||||||
|
|||||||
@@ -19,12 +19,15 @@ class SystemdFailedUnits : public ALabel {
|
|||||||
std::string format_ok;
|
std::string format_ok;
|
||||||
|
|
||||||
bool update_pending;
|
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;
|
std::string last_status;
|
||||||
Glib::RefPtr<Gio::DBus::Proxy> system_proxy, user_proxy;
|
Glib::RefPtr<Gio::DBus::Proxy> system_proxy, user_proxy;
|
||||||
|
|
||||||
void notify_cb(const Glib::ustring &sender_name, const Glib::ustring &signal_name,
|
void notify_cb(const Glib::ustring &sender_name, const Glib::ustring &signal_name,
|
||||||
const Glib::VariantContainerBase &arguments);
|
const Glib::VariantContainerBase &arguments);
|
||||||
|
void RequestFailedUnits();
|
||||||
|
void RequestSystemState();
|
||||||
void updateData();
|
void updateData();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -22,6 +22,8 @@ class Wireplumber : public ALabel {
|
|||||||
void activatePlugins();
|
void activatePlugins();
|
||||||
static void updateVolume(waybar::modules::Wireplumber* self, uint32_t id);
|
static void updateVolume(waybar::modules::Wireplumber* self, uint32_t id);
|
||||||
static void updateNodeName(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 onPluginActivated(WpObject* p, GAsyncResult* res, waybar::modules::Wireplumber* self);
|
||||||
static void onDefaultNodesApiLoaded(WpObject* p, GAsyncResult* res,
|
static void onDefaultNodesApiLoaded(WpObject* p, GAsyncResult* res,
|
||||||
waybar::modules::Wireplumber* self);
|
waybar::modules::Wireplumber* self);
|
||||||
@@ -46,7 +48,12 @@ class Wireplumber : public ALabel {
|
|||||||
double min_step_;
|
double min_step_;
|
||||||
uint32_t node_id_{0};
|
uint32_t node_id_{0};
|
||||||
std::string node_name_;
|
std::string node_name_;
|
||||||
|
std::string source_name_;
|
||||||
gchar* type_;
|
gchar* type_;
|
||||||
|
uint32_t source_node_id_;
|
||||||
|
bool source_muted_;
|
||||||
|
double source_volume_;
|
||||||
|
gchar* default_source_name_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace waybar::modules
|
} // namespace waybar::modules
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
#include "bar.hpp"
|
#include "bar.hpp"
|
||||||
#include "client.hpp"
|
#include "client.hpp"
|
||||||
#include "giomm/desktopappinfo.h"
|
#include "giomm/desktopappinfo.h"
|
||||||
|
#include "util/icon_loader.hpp"
|
||||||
#include "util/json.hpp"
|
#include "util/json.hpp"
|
||||||
#include "wlr-foreign-toplevel-management-unstable-v1-client-protocol.h"
|
#include "wlr-foreign-toplevel-management-unstable-v1-client-protocol.h"
|
||||||
|
|
||||||
@@ -89,9 +90,6 @@ class Task {
|
|||||||
std::string state_string(bool = false) const;
|
std::string state_string(bool = false) const;
|
||||||
void set_minimize_hint();
|
void set_minimize_hint();
|
||||||
void on_button_size_allocated(Gtk::Allocation &alloc);
|
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();
|
void hide_if_ignored();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -153,7 +151,7 @@ class Taskbar : public waybar::AModule {
|
|||||||
Gtk::Box box_;
|
Gtk::Box box_;
|
||||||
std::vector<TaskPtr> tasks_;
|
std::vector<TaskPtr> tasks_;
|
||||||
|
|
||||||
std::vector<Glib::RefPtr<Gtk::IconTheme>> icon_themes_;
|
IconLoader icon_loader_;
|
||||||
std::unordered_set<std::string> ignore_list_;
|
std::unordered_set<std::string> ignore_list_;
|
||||||
std::map<std::string, std::string> app_ids_replace_map_;
|
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 show_output(struct wl_output *) const;
|
||||||
bool all_outputs() 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::unordered_set<std::string> &ignore_list() const;
|
||||||
const std::map<std::string, std::string> &app_ids_replace_map() const;
|
const std::map<std::string, std::string> &app_ids_replace_map() const;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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
|
|
||||||
@@ -11,6 +11,11 @@
|
|||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
#ifdef __OpenBSD__
|
||||||
|
#define SIGRTMIN SIGUSR1 - 1
|
||||||
|
#define SIGRTMAX SIGUSR1 + 1
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace waybar {
|
namespace waybar {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
namespace date {
|
namespace date {
|
||||||
#if HAVE_CHRONO_TIMEZONES
|
#if HAVE_CHRONO_TIMEZONES
|
||||||
using namespace std::chrono;
|
using namespace std::chrono;
|
||||||
using namespace std;
|
using std::format;
|
||||||
#else
|
#else
|
||||||
|
|
||||||
using system_clock = std::chrono::system_clock;
|
using system_clock = std::chrono::system_clock;
|
||||||
@@ -73,5 +73,3 @@ struct fmt::formatter<date::zoned_time<Duration, TimeZonePtr>> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
using namespace date;
|
|
||||||
|
|||||||
@@ -10,5 +10,7 @@ class DefaultGtkIconThemeWrapper {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
static bool has_icon(const std::string&);
|
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>());
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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);
|
||||||
|
};
|
||||||
@@ -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 media_name;
|
||||||
std::string node_name;
|
std::string node_name;
|
||||||
std::string application_name;
|
std::string application_name;
|
||||||
|
bool is_monitor = false;
|
||||||
|
|
||||||
std::string pipewire_access_portal_app_id;
|
std::string pipewire_access_portal_app_id;
|
||||||
std::string application_icon_name;
|
std::string application_icon_name;
|
||||||
|
|||||||
@@ -6,14 +6,12 @@
|
|||||||
|
|
||||||
namespace waybar {
|
namespace waybar {
|
||||||
|
|
||||||
using namespace Gio;
|
|
||||||
|
|
||||||
enum class Appearance {
|
enum class Appearance {
|
||||||
UNKNOWN = 0,
|
UNKNOWN = 0,
|
||||||
DARK = 1,
|
DARK = 1,
|
||||||
LIGHT = 2,
|
LIGHT = 2,
|
||||||
};
|
};
|
||||||
class Portal : private DBus::Proxy {
|
class Portal : private Gio::DBus::Proxy {
|
||||||
public:
|
public:
|
||||||
Portal();
|
Portal();
|
||||||
void refreshAppearance();
|
void refreshAppearance();
|
||||||
|
|||||||
@@ -4,6 +4,6 @@
|
|||||||
|
|
||||||
namespace waybar::util {
|
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();
|
SafeSignal<bool>& prepare_for_sleep();
|
||||||
} // namespace waybar::util
|
} // namespace waybar::util
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
#include <sigc++/signal.h>
|
#include <sigc++/signal.h>
|
||||||
#include <sigc++/trackable.h>
|
#include <sigc++/trackable.h>
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
namespace waybar::util {
|
namespace waybar::util {
|
||||||
|
|
||||||
class Rfkill : public sigc::trackable {
|
class Rfkill : public sigc::trackable {
|
||||||
@@ -17,7 +19,7 @@ class Rfkill : public sigc::trackable {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
enum rfkill_type rfkill_type_;
|
enum rfkill_type rfkill_type_;
|
||||||
bool state_ = false;
|
std::atomic_bool state_ = false;
|
||||||
int fd_ = -1;
|
int fd_ = -1;
|
||||||
|
|
||||||
bool on_event(Glib::IOCondition cond);
|
bool on_event(Glib::IOCondition cond);
|
||||||
|
|||||||
@@ -23,3 +23,26 @@ inline std::string capitalize(const std::string& str) {
|
|||||||
[](unsigned char c) { return std::toupper(c); });
|
[](unsigned char c) { return std::toupper(c); });
|
||||||
return result;
|
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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ The *battery* module displays the current capacity and state (eg. charging) of y
|
|||||||
*menu-file*: ++
|
*menu-file*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
Location of the menu descriptor file. There need to be an element of type
|
Location of the menu descriptor file. There need to be an element of type
|
||||||
GtkMenu with id *menu*
|
GtkMenu with id *menu*.
|
||||||
|
|
||||||
*menu-actions*: ++
|
*menu-actions*: ++
|
||||||
typeof: array ++
|
typeof: array ++
|
||||||
@@ -127,6 +127,10 @@ The *battery* module displays the current capacity and state (eg. charging) of y
|
|||||||
default: false ++
|
default: false ++
|
||||||
Enables this module to consume all left over space dynamically.
|
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
|
# FORMAT REPLACEMENTS
|
||||||
|
|
||||||
*{capacity}*: Capacity in percentage
|
*{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>*.
|
- 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*.
|
- 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
|
# EXAMPLES
|
||||||
@@ -178,6 +195,11 @@ The *battery* module allows one to define custom formats based on up to two fact
|
|||||||
"warning": 30,
|
"warning": 30,
|
||||||
"critical": 15
|
"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": "{capacity}% {icon}",
|
||||||
"format-icons": ["", "", "", "", ""],
|
"format-icons": ["", "", "", "", ""],
|
||||||
"max-length": 25
|
"max-length": 25
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ The *workspaces* module displays the currently used workspaces in wayland compos
|
|||||||
|
|
||||||
# CONFIGURATION
|
# CONFIGURATION
|
||||||
|
|
||||||
Addressed by *wlr/workspaces*
|
Addressed by *ext/workspaces*
|
||||||
|
|
||||||
*format*: ++
|
*format*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
@@ -24,18 +24,18 @@ Addressed by *wlr/workspaces*
|
|||||||
*sort-by-name*: ++
|
*sort-by-name*: ++
|
||||||
typeof: bool ++
|
typeof: bool ++
|
||||||
default: true ++
|
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*: ++
|
*sort-by-coordinates*: ++
|
||||||
typeof: bool ++
|
typeof: bool ++
|
||||||
default: true ++
|
default: false ++
|
||||||
Should workspaces be sorted by coordinates. ++
|
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.
|
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 ++
|
typeof: bool ++
|
||||||
default: false ++
|
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*: ++
|
*all-outputs*: ++
|
||||||
typeof: bool ++
|
typeof: bool ++
|
||||||
@@ -47,9 +47,16 @@ Addressed by *wlr/workspaces*
|
|||||||
default: false ++
|
default: false ++
|
||||||
If set to true only active or urgent workspaces will be shown.
|
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
|
# 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*.
|
*{icon}*: Icon, as defined in *format-icons*.
|
||||||
|
|
||||||
@@ -69,18 +76,18 @@ In addition to workspace name matching, the following *format-icons* can be set.
|
|||||||
# EXAMPLES
|
# EXAMPLES
|
||||||
|
|
||||||
```
|
```
|
||||||
"wlr/workspaces": {
|
"ext/workspaces": {
|
||||||
"format": "{name}: {icon}",
|
"format": "{name}: {icon}",
|
||||||
|
"on-click": "activate",
|
||||||
"format-icons": {
|
"format-icons": {
|
||||||
"1": "",
|
"Workspace 1": "",
|
||||||
"2": "",
|
"Workspace 2": "",
|
||||||
"3": "",
|
"Workspace 3": "",
|
||||||
"4": "",
|
"Workspace 4": "",
|
||||||
"5": "",
|
|
||||||
"active": "",
|
"active": "",
|
||||||
"default": ""
|
"default": ""
|
||||||
},
|
},
|
||||||
"sort-by-number": true
|
"sort-by-id": true
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -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.
|
||||||
@@ -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
|
||||||
@@ -26,17 +26,49 @@ Addressed by *hyprland/workspaces*
|
|||||||
Regex rules to map window class to an icon or preferred method of representation for a workspace's window.
|
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.
|
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.
|
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 ++
|
typeof: string ++
|
||||||
default: "?" ++
|
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*: ++
|
*format-window-separator*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
default: " " ++
|
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.
|
||||||
|
|
||||||
|
*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.
|
||||||
|
|
||||||
*show-special*: ++
|
*show-special*: ++
|
||||||
typeof: bool ++
|
typeof: bool ++
|
||||||
@@ -48,6 +80,11 @@ Addressed by *hyprland/workspaces*
|
|||||||
default: false ++
|
default: false ++
|
||||||
If this and show-special are to true, special workspaces will be shown only if visible.
|
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*: ++
|
*all-outputs*: ++
|
||||||
typeof: bool ++
|
typeof: bool ++
|
||||||
default: false ++
|
default: false ++
|
||||||
@@ -76,6 +113,7 @@ Addressed by *hyprland/workspaces*
|
|||||||
If set to number, workspaces will sort by number.
|
If set to number, workspaces will sort by number.
|
||||||
If set to name, workspaces will sort by name.
|
If set to name, workspaces will sort by name.
|
||||||
If set to id, workspaces will sort by id.
|
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.
|
If none of those, workspaces will sort with default behavior.
|
||||||
|
|
||||||
*expand*: ++
|
*expand*: ++
|
||||||
@@ -178,3 +216,4 @@ Additional to workspace name matching, the following *format-icons* can be set.
|
|||||||
- *#workspaces button.special*
|
- *#workspaces button.special*
|
||||||
- *#workspaces button.urgent*
|
- *#workspaces button.urgent*
|
||||||
- *#workspaces button.hosting-monitor* (gets applied if workspace-monitor == waybar-monitor)
|
- *#workspaces button.hosting-monitor* (gets applied if workspace-monitor == waybar-monitor)
|
||||||
|
- *#workspaces .taskbar-window* (each window in the taskbar)
|
||||||
|
|||||||
@@ -125,3 +125,9 @@ screensaver, also known as "presentation mode".
|
|||||||
"timeout": 30.5
|
"timeout": 30.5
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
# STYLE
|
||||||
|
|
||||||
|
- *#idle_inhibitor*
|
||||||
|
- *#idle_inhibitor.activated*
|
||||||
|
- *#idle_inhibitor.deactivated*
|
||||||
|
|||||||
@@ -120,6 +120,8 @@ Addressed by *memory*
|
|||||||
|
|
||||||
*{swapAvail}*: Amount of available swap in GiB.
|
*{swapAvail}*: Amount of available swap in GiB.
|
||||||
|
|
||||||
|
*{swapState}*: Signals if swap is activated or not
|
||||||
|
|
||||||
# EXAMPLES
|
# EXAMPLES
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ waybar - menu property
|
|||||||
# OVERVIEW
|
# 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.
|
click is done over the module.
|
||||||
|
|
||||||
# PROPERTIES
|
# PROPERTIES
|
||||||
|
|||||||
@@ -204,6 +204,10 @@ Addressed by *mpd*
|
|||||||
|
|
||||||
*{queueLength}*: The length of the current queue.
|
*{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)
|
*{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)
|
*{consumeIcon}*: The icon corresponding the "consume" option (see *consume-icons* option)
|
||||||
|
|||||||
@@ -16,6 +16,11 @@ Addressed by *network*
|
|||||||
typeof: string ++
|
typeof: string ++
|
||||||
Use the defined interface instead of auto-detection. Accepts wildcard.
|
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*: ++
|
*interval*: ++
|
||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
default: 60 ++
|
default: 60 ++
|
||||||
@@ -49,7 +54,7 @@ Addressed by *network*
|
|||||||
|
|
||||||
*format-disabled*: ++
|
*format-disabled*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
This format is used when the displayed interface is disabled.
|
This format is used when rfkill is blocking wlan interfaces.
|
||||||
|
|
||||||
*format-icons*: ++
|
*format-icons*: ++
|
||||||
typeof: array/object ++
|
typeof: array/object ++
|
||||||
@@ -127,7 +132,7 @@ Addressed by *network*
|
|||||||
|
|
||||||
*tooltip-format-disabled*: ++
|
*tooltip-format-disabled*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
This format is used when the displayed interface is disabled.
|
This format is used when rfkill is blocking wlan interfaces.
|
||||||
|
|
||||||
*menu*: ++
|
*menu*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
@@ -171,7 +176,7 @@ Addressed by *network*
|
|||||||
|
|
||||||
*{signaldBm}*: Signal strength of the wireless network in dBm.
|
*{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.
|
*{bandwidthUpBits}*: Instant up speed in bits/seconds.
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
- *default*: Will be shown, when no string matches are found.
|
||||||
- *focused*: Will be shown, when workspace is focused.
|
- *focused*: Will be shown, when workspace is focused.
|
||||||
- *active*: Will be shown, when workspace is active on its output.
|
- *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
|
# EXAMPLES
|
||||||
|
|
||||||
@@ -95,6 +97,7 @@ Additional to workspace name matching, the following *format-icons* can be set.
|
|||||||
- *#workspaces button*
|
- *#workspaces button*
|
||||||
- *#workspaces button.focused*: The single focused workspace.
|
- *#workspaces button.focused*: The single focused workspace.
|
||||||
- *#workspaces button.active*: The workspace is active (visible) on its output.
|
- *#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.empty*: The workspace is empty.
|
||||||
- *#workspaces button.current_output*: The workspace is from the same output as
|
- *#workspaces button.current_output*: The workspace is from the same output as
|
||||||
the bar that it is displayed on.
|
the bar that it is displayed on.
|
||||||
|
|||||||
@@ -37,6 +37,17 @@ the screen or playing audio.
|
|||||||
default: false ++
|
default: false ++
|
||||||
Enables this module to consume all left over space dynamically.
|
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
|
# MODULES CONFIGURATION
|
||||||
|
|
||||||
*type*: ++
|
*type*: ++
|
||||||
@@ -54,6 +65,14 @@ the screen or playing audio.
|
|||||||
default: 24 ++
|
default: 24 ++
|
||||||
The size of each icon in the tooltip.
|
The size of each icon in the tooltip.
|
||||||
|
|
||||||
|
# IGNORE CONFIGURATION
|
||||||
|
|
||||||
|
*type*: ++
|
||||||
|
typeof: string
|
||||||
|
|
||||||
|
*name*: ++
|
||||||
|
typeof: string
|
||||||
|
|
||||||
# EXAMPLES
|
# EXAMPLES
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -77,6 +96,17 @@ the screen or playing audio.
|
|||||||
"tooltip": true,
|
"tooltip": true,
|
||||||
"tooltip-icon-size": 24
|
"tooltip-icon-size": 24
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"ignore-monitor": true,
|
||||||
|
"ignore": [
|
||||||
|
{
|
||||||
|
"type": "audio-in",
|
||||||
|
"name": "cava"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "screenshare",
|
||||||
|
"name": "obs"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -83,6 +83,7 @@ Addressed by *river/layout*
|
|||||||
|
|
||||||
- *#layout*
|
- *#layout*
|
||||||
- *#layout.focused* Applied when the output this module's bar belongs to is focused.
|
- *#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
|
# SEE ALSO
|
||||||
|
|
||||||
|
|||||||
@@ -89,6 +89,11 @@ Addressed by *sway/window*
|
|||||||
default: false ++
|
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.
|
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*: ++
|
*rewrite*: ++
|
||||||
typeof: object ++
|
typeof: object ++
|
||||||
Rules to rewrite the module format output. See *rewrite rules*.
|
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
|
*{shell}*: The shell of the focused window. It's 'xwayland' when the window is
|
||||||
running through xwayland, otherwise, it's 'xdg-shell'.
|
running through xwayland, otherwise, it's 'xdg-shell'.
|
||||||
|
|
||||||
|
*{marks}*: Marks of the window.
|
||||||
|
|
||||||
# REWRITE RULES
|
# REWRITE RULES
|
||||||
|
|
||||||
*rewrite* is an object where keys are regular expressions and values are
|
*rewrite* is an object where keys are regular expressions and values are
|
||||||
|
|||||||
@@ -31,6 +31,11 @@ Addressed by *sway/workspaces*
|
|||||||
default: false ++
|
default: false ++
|
||||||
If set to false, you can scroll to cycle through workspaces. If set to true this behaviour is disabled.
|
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*: ++
|
*disable-click*: ++
|
||||||
typeof: bool ++
|
typeof: bool ++
|
||||||
default: false ++
|
default: false ++
|
||||||
|
|||||||
@@ -62,6 +62,12 @@ Addressed by *systemd-failed-units*
|
|||||||
|
|
||||||
*{nr_failed}*: Number of total 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
|
# EXAMPLES
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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.
|
||||||
+50
-3
@@ -86,7 +86,7 @@ The visual display elements for waybar use a CSS stylesheet, see *waybar-styles(
|
|||||||
*no-center* ++
|
*no-center* ++
|
||||||
typeof: bool ++
|
typeof: bool ++
|
||||||
default: false ++
|
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* ++
|
*spacing* ++
|
||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
@@ -151,6 +151,20 @@ The visual display elements for waybar use a CSS stylesheet, see *waybar-styles(
|
|||||||
default: *false* ++
|
default: *false* ++
|
||||||
Option to enable reloading the css style if a modification is detected on the style sheet file or any imported css files.
|
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
|
# MODULE FORMAT
|
||||||
|
|
||||||
You can use PangoMarkupFormat (See https://developer.gnome.org/pango/stable/PangoMarkupFormat.html#PangoMarkupFormat).
|
You can use PangoMarkupFormat (See https://developer.gnome.org/pango/stable/PangoMarkupFormat.html#PangoMarkupFormat).
|
||||||
@@ -206,14 +220,36 @@ A minimal *config* file could look like this:
|
|||||||
Waybar accepts the following signals:
|
Waybar accepts the following signals:
|
||||||
|
|
||||||
*SIGUSR1*
|
*SIGUSR1*
|
||||||
Toggles the bar visibility (hides if shown, shows if hidden)
|
By default toggles the bar visibility (hides if shown, shows if hidden)
|
||||||
*SIGUSR2*
|
*SIGUSR2*
|
||||||
Reloads (resets) the bar
|
By default reloads (resets) the bar
|
||||||
*SIGINT*
|
*SIGINT*
|
||||||
Quits the bar
|
Quits the bar
|
||||||
|
|
||||||
For example, to toggle the bar programmatically, you can invoke `killall -SIGUSR1 waybar`.
|
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
|
# MULTI OUTPUT CONFIGURATION
|
||||||
|
|
||||||
## Limit a configuration to some outputs
|
## 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.
|
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
|
## 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.
|
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.
|
||||||
|
|||||||
+32
-13
@@ -1,6 +1,6 @@
|
|||||||
project(
|
project(
|
||||||
'waybar', 'cpp', 'c',
|
'waybar', 'cpp', 'c',
|
||||||
version: '0.12.0',
|
version: '0.14.0',
|
||||||
license: 'MIT',
|
license: 'MIT',
|
||||||
meson_version: '>= 0.59.0',
|
meson_version: '>= 0.59.0',
|
||||||
default_options : [
|
default_options : [
|
||||||
@@ -93,6 +93,7 @@ libmpdclient = dependency('libmpdclient', required: get_option('mpd'))
|
|||||||
xkbregistry = dependency('xkbregistry')
|
xkbregistry = dependency('xkbregistry')
|
||||||
libjack = dependency('jack', required: get_option('jack'))
|
libjack = dependency('jack', required: get_option('jack'))
|
||||||
libwireplumber = dependency('wireplumber-0.5', required: get_option('wireplumber'))
|
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'))
|
libsndio = compiler.find_library('sndio', required: get_option('sndio'))
|
||||||
if libsndio.found()
|
if libsndio.found()
|
||||||
@@ -182,6 +183,7 @@ src_files = files(
|
|||||||
'src/util/sanitize_str.cpp',
|
'src/util/sanitize_str.cpp',
|
||||||
'src/util/rewrite_string.cpp',
|
'src/util/rewrite_string.cpp',
|
||||||
'src/util/gtk_icon.cpp',
|
'src/util/gtk_icon.cpp',
|
||||||
|
'src/util/icon_loader.cpp',
|
||||||
'src/util/regex_collection.cpp',
|
'src/util/regex_collection.cpp',
|
||||||
'src/util/css_reload_helper.cpp'
|
'src/util/css_reload_helper.cpp'
|
||||||
)
|
)
|
||||||
@@ -275,6 +277,17 @@ if true
|
|||||||
man_files += files('man/waybar-wlr-taskbar.5.scd')
|
man_files += files('man/waybar-wlr-taskbar.5.scd')
|
||||||
endif
|
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
|
if true
|
||||||
add_project_arguments('-DHAVE_RIVER', language: 'cpp')
|
add_project_arguments('-DHAVE_RIVER', language: 'cpp')
|
||||||
src_files += files(
|
src_files += files(
|
||||||
@@ -306,6 +319,7 @@ if true
|
|||||||
'src/modules/hyprland/language.cpp',
|
'src/modules/hyprland/language.cpp',
|
||||||
'src/modules/hyprland/submap.cpp',
|
'src/modules/hyprland/submap.cpp',
|
||||||
'src/modules/hyprland/window.cpp',
|
'src/modules/hyprland/window.cpp',
|
||||||
|
'src/modules/hyprland/windowcount.cpp',
|
||||||
'src/modules/hyprland/workspace.cpp',
|
'src/modules/hyprland/workspace.cpp',
|
||||||
'src/modules/hyprland/workspaces.cpp',
|
'src/modules/hyprland/workspaces.cpp',
|
||||||
'src/modules/hyprland/windowcreationpayload.cpp',
|
'src/modules/hyprland/windowcreationpayload.cpp',
|
||||||
@@ -333,6 +347,15 @@ if get_option('niri')
|
|||||||
)
|
)
|
||||||
endif
|
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')
|
if get_option('login-proxy')
|
||||||
add_project_arguments('-DHAVE_LOGIN_PROXY', language: 'cpp')
|
add_project_arguments('-DHAVE_LOGIN_PROXY', language: 'cpp')
|
||||||
endif
|
endif
|
||||||
@@ -474,17 +497,6 @@ else
|
|||||||
man_files += files('man/waybar-clock.5.scd')
|
man_files += files('man/waybar-clock.5.scd')
|
||||||
endif
|
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',
|
cava = dependency('cava',
|
||||||
version : '>=0.10.4',
|
version : '>=0.10.4',
|
||||||
required: get_option('cava'),
|
required: get_option('cava'),
|
||||||
@@ -497,6 +509,12 @@ if cava.found()
|
|||||||
man_files += files('man/waybar-cava.5.scd')
|
man_files += files('man/waybar-cava.5.scd')
|
||||||
endif
|
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')
|
subdir('protocol')
|
||||||
|
|
||||||
app_resources = []
|
app_resources = []
|
||||||
@@ -535,7 +553,8 @@ executable(
|
|||||||
libsndio,
|
libsndio,
|
||||||
tz_dep,
|
tz_dep,
|
||||||
xkbregistry,
|
xkbregistry,
|
||||||
cava
|
cava,
|
||||||
|
libgps
|
||||||
],
|
],
|
||||||
include_directories: inc_dirs,
|
include_directories: inc_dirs,
|
||||||
install: true,
|
install: true,
|
||||||
|
|||||||
@@ -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('cava', type: 'feature', value: 'auto', description: 'Enable support for Cava')
|
||||||
option('niri', type: 'boolean', description: 'Enable support for niri')
|
option('niri', type: 'boolean', description: 'Enable support for niri')
|
||||||
option('login-proxy', type: 'boolean', description: 'Enable interfacing with dbus login interface')
|
option('login-proxy', type: 'boolean', description: 'Enable interfacing with dbus login interface')
|
||||||
|
option('gps', type: 'feature', value: 'auto', description: 'Enable support for gps')
|
||||||
|
|||||||
+12
-9
@@ -1,7 +1,8 @@
|
|||||||
{ lib
|
{
|
||||||
, pkgs
|
lib,
|
||||||
, waybar
|
pkgs,
|
||||||
, version
|
waybar,
|
||||||
|
version,
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
libcava = rec {
|
libcava = rec {
|
||||||
@@ -14,8 +15,7 @@ let
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
(waybar.overrideAttrs (
|
(waybar.overrideAttrs (oldAttrs: {
|
||||||
oldAttrs: {
|
|
||||||
inherit version;
|
inherit version;
|
||||||
|
|
||||||
src = lib.cleanSourceWith {
|
src = lib.cleanSourceWith {
|
||||||
@@ -30,8 +30,12 @@ in
|
|||||||
# nixpkgs checks version, no need when building locally
|
# nixpkgs checks version, no need when building locally
|
||||||
nativeInstallCheckInputs = [ ];
|
nativeInstallCheckInputs = [ ];
|
||||||
|
|
||||||
buildInputs = (builtins.filter (p: p.pname != "wireplumber") oldAttrs.buildInputs) ++ [
|
buildInputs = (builtins.filter (p:
|
||||||
|
p.pname != "wireplumber" &&
|
||||||
|
p.pname != "gps"
|
||||||
|
) oldAttrs.buildInputs) ++ [
|
||||||
pkgs.wireplumber
|
pkgs.wireplumber
|
||||||
|
pkgs.gpsd
|
||||||
];
|
];
|
||||||
|
|
||||||
postUnpack = ''
|
postUnpack = ''
|
||||||
@@ -40,5 +44,4 @@ in
|
|||||||
patchShebangs .
|
patchShebangs .
|
||||||
popd
|
popd
|
||||||
'';
|
'';
|
||||||
}
|
}))
|
||||||
))
|
|
||||||
|
|||||||
@@ -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>
|
|
||||||
@@ -26,12 +26,17 @@ client_protocols = [
|
|||||||
[wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'],
|
[wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'],
|
||||||
[wl_protocol_dir, 'unstable/idle-inhibit/idle-inhibit-unstable-v1.xml'],
|
[wl_protocol_dir, 'unstable/idle-inhibit/idle-inhibit-unstable-v1.xml'],
|
||||||
['wlr-foreign-toplevel-management-unstable-v1.xml'],
|
['wlr-foreign-toplevel-management-unstable-v1.xml'],
|
||||||
['ext-workspace-unstable-v1.xml'],
|
|
||||||
['river-status-unstable-v1.xml'],
|
['river-status-unstable-v1.xml'],
|
||||||
['river-control-unstable-v1.xml'],
|
['river-control-unstable-v1.xml'],
|
||||||
['dwl-ipc-unstable-v2.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_src = []
|
||||||
client_protos_headers = []
|
client_protos_headers = []
|
||||||
|
|
||||||
|
|||||||
@@ -112,6 +112,7 @@ class PlayerManager:
|
|||||||
logger.debug(f"Metadata changed for player {player.props.player_name}")
|
logger.debug(f"Metadata changed for player {player.props.player_name}")
|
||||||
player_name = player.props.player_name
|
player_name = player.props.player_name
|
||||||
artist = player.get_artist()
|
artist = player.get_artist()
|
||||||
|
artist = artist.replace("&", "&")
|
||||||
title = player.get_title()
|
title = player.get_title()
|
||||||
title = title.replace("&", "&")
|
title = title.replace("&", "&")
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
[Unit]
|
[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/
|
Documentation=https://github.com/Alexays/Waybar/wiki/
|
||||||
PartOf=graphical-session.target
|
PartOf=graphical-session.target
|
||||||
After=graphical-session.target
|
After=graphical-session.target
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "AIconLabel.hpp"
|
#include "AIconLabel.hpp"
|
||||||
|
|
||||||
#include <gdkmm/pixbuf.h>
|
#include <gdkmm/pixbuf.h>
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
namespace waybar {
|
namespace waybar {
|
||||||
|
|
||||||
@@ -17,14 +18,36 @@ AIconLabel::AIconLabel(const Json::Value &config, const std::string &name, const
|
|||||||
box_.get_style_context()->add_class(id);
|
box_.get_style_context()->add_class(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
box_.set_orientation(Gtk::Orientation::ORIENTATION_HORIZONTAL);
|
||||||
|
else
|
||||||
|
box_.set_orientation(Gtk::Orientation::ORIENTATION_VERTICAL);
|
||||||
box_.set_name(name);
|
box_.set_name(name);
|
||||||
|
|
||||||
int spacing = config_["icon-spacing"].isInt() ? config_["icon-spacing"].asInt() : 8;
|
int spacing = config_["icon-spacing"].isInt() ? config_["icon-spacing"].asInt() : 8;
|
||||||
box_.set_spacing(spacing);
|
box_.set_spacing(spacing);
|
||||||
|
|
||||||
|
bool swap_icon_label = false;
|
||||||
|
if (not config_["swap-icon-label"].isBool())
|
||||||
|
spdlog::warn("'swap-icon-label' must be a bool.");
|
||||||
|
else
|
||||||
|
swap_icon_label = config_["swap-icon-label"].asBool();
|
||||||
|
|
||||||
|
if ((rot == 0 || rot == 3) ^ swap_icon_label) {
|
||||||
box_.add(image_);
|
box_.add(image_);
|
||||||
box_.add(label_);
|
box_.add(label_);
|
||||||
|
} else {
|
||||||
|
box_.add(label_);
|
||||||
|
box_.add(image_);
|
||||||
|
}
|
||||||
|
|
||||||
event_box_.add(box_);
|
event_box_.add(box_);
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -200,7 +200,7 @@ std::string ALabel::getState(uint8_t value, bool lesser) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Sort states
|
// 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;
|
return lesser ? a.second < b.second : a.second > b.second;
|
||||||
});
|
});
|
||||||
std::string valid_state;
|
std::string valid_state;
|
||||||
|
|||||||
+6
-6
@@ -83,7 +83,7 @@ AModule::AModule(const Json::Value& config, const std::string& name, const std::
|
|||||||
}
|
}
|
||||||
|
|
||||||
AModule::~AModule() {
|
AModule::~AModule() {
|
||||||
for (const auto& pid : pid_) {
|
for (const auto& pid : pid_children_) {
|
||||||
if (pid != -1) {
|
if (pid != -1) {
|
||||||
killpg(pid, SIGTERM);
|
killpg(pid, SIGTERM);
|
||||||
}
|
}
|
||||||
@@ -93,15 +93,15 @@ AModule::~AModule() {
|
|||||||
auto AModule::update() -> void {
|
auto AModule::update() -> void {
|
||||||
// Run user-provided update handler if configured
|
// Run user-provided update handler if configured
|
||||||
if (config_["on-update"].isString()) {
|
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
|
// 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 {
|
auto AModule::doAction(const std::string& name) -> void {
|
||||||
if (!name.empty()) {
|
if (!name.empty()) {
|
||||||
const std::map<std::string, std::string>::const_iterator& recA{eventActionMap_.find(name)};
|
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);
|
if (recA != eventActionMap_.cend() && name != recA->second) this->doAction(recA->second);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -182,7 +182,7 @@ bool AModule::handleUserEvent(GdkEventButton* const& e) {
|
|||||||
format.clear();
|
format.clear();
|
||||||
}
|
}
|
||||||
if (!format.empty()) {
|
if (!format.empty()) {
|
||||||
pid_.push_back(util::command::forkExec(format));
|
pid_children_.push_back(util::command::forkExec(format));
|
||||||
}
|
}
|
||||||
dp.emit();
|
dp.emit();
|
||||||
return true;
|
return true;
|
||||||
@@ -267,7 +267,7 @@ bool AModule::handleScroll(GdkEventScroll* e) {
|
|||||||
this->AModule::doAction(eventName);
|
this->AModule::doAction(eventName);
|
||||||
// Second call user scripts
|
// Second call user scripts
|
||||||
if (config_[eventName].isString())
|
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();
|
dp.emit();
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
+40
-4
@@ -8,6 +8,8 @@
|
|||||||
#include "client.hpp"
|
#include "client.hpp"
|
||||||
#include "factory.hpp"
|
#include "factory.hpp"
|
||||||
#include "group.hpp"
|
#include "group.hpp"
|
||||||
|
#include "util/enum.hpp"
|
||||||
|
#include "util/kill_signal.hpp"
|
||||||
|
|
||||||
#ifdef HAVE_SWAY
|
#ifdef HAVE_SWAY
|
||||||
#include "modules/sway/bar.hpp"
|
#include "modules/sway/bar.hpp"
|
||||||
@@ -277,6 +279,32 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config)
|
|||||||
}
|
}
|
||||||
#endif
|
#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();
|
setupWidgets();
|
||||||
window.show_all();
|
window.show_all();
|
||||||
|
|
||||||
@@ -422,6 +450,8 @@ void waybar::Bar::setVisible(bool value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void waybar::Bar::toggle() { setVisible(!visible); }
|
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
|
// Converting string to button code rn as to avoid doing it later
|
||||||
void waybar::Bar::setupAltFormatKeyForModule(const std::string& module_name) {
|
void waybar::Bar::setupAltFormatKeyForModule(const std::string& module_name) {
|
||||||
@@ -479,6 +509,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,
|
void waybar::Bar::getModules(const Factory& factory, const std::string& pos,
|
||||||
waybar::Group* group = nullptr) {
|
waybar::Group* group = nullptr) {
|
||||||
auto module_list = group != nullptr ? config[pos]["modules"] : config[pos];
|
auto module_list = group != nullptr ? config[pos]["modules"] : config[pos];
|
||||||
@@ -496,7 +529,11 @@ void waybar::Bar::getModules(const Factory& factory, const std::string& pos,
|
|||||||
auto vertical = (group != nullptr ? group->getBox().get_orientation()
|
auto vertical = (group != nullptr ? group->getBox().get_orientation()
|
||||||
: box_.get_orientation()) == Gtk::ORIENTATION_VERTICAL;
|
: 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);
|
getModules(factory, ref, group_module);
|
||||||
module = group_module;
|
module = group_module;
|
||||||
} else {
|
} else {
|
||||||
@@ -545,7 +582,6 @@ auto waybar::Bar::setupWidgets() -> void {
|
|||||||
if (config["fixed-center"].isBool() ? config["fixed-center"].asBool() : true) {
|
if (config["fixed-center"].isBool() ? config["fixed-center"].asBool() : true) {
|
||||||
box_.set_center_widget(center_);
|
box_.set_center_widget(center_);
|
||||||
} else {
|
} else {
|
||||||
spdlog::error("No fixed center_");
|
|
||||||
box_.pack_start(center_, true, expand_center);
|
box_.pack_start(center_, true, expand_center);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -569,13 +605,13 @@ auto waybar::Bar::setupWidgets() -> void {
|
|||||||
|
|
||||||
if (!no_center) {
|
if (!no_center) {
|
||||||
for (auto const& module : modules_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());
|
std::reverse(modules_right_.begin(), modules_right_.end());
|
||||||
for (auto const& module : modules_right_) {
|
for (auto const& module : modules_right_) {
|
||||||
right_.pack_end(*module, false, false);
|
right_.pack_end(*module, module->expandEnabled(), module->expandEnabled());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+12
-6
@@ -86,7 +86,7 @@ void waybar::Client::handleOutputDone(void *data, struct zxdg_output_v1 * /*xdg_
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (const std::exception &e) {
|
} 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);
|
auto &output = client->getOutput(data);
|
||||||
output.name = name;
|
output.name = name;
|
||||||
} catch (const std::exception &e) {
|
} 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();
|
auto *client = waybar::Client::inst();
|
||||||
try {
|
try {
|
||||||
auto &output = client->getOutput(data);
|
auto &output = client->getOutput(data);
|
||||||
const char *open_paren = strrchr(description, '(');
|
|
||||||
|
|
||||||
// Description format: "identifier (name)"
|
// Description format: "identifier (name)"
|
||||||
size_t identifier_length = open_paren - description;
|
auto s = std::string(description);
|
||||||
output.identifier = std::string(description, identifier_length - 1);
|
auto pos = s.find(" (");
|
||||||
|
output.identifier = pos != std::string::npos ? s.substr(0, pos) : s;
|
||||||
} catch (const std::exception &e) {
|
} 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,
|
const std::string waybar::Client::getStyle(const std::string &style,
|
||||||
std::optional<Appearance> appearance = std::nullopt) {
|
std::optional<Appearance> appearance = std::nullopt) {
|
||||||
|
auto gtk_settings = Gtk::Settings::get_default();
|
||||||
std::optional<std::string> css_file;
|
std::optional<std::string> css_file;
|
||||||
|
|
||||||
if (style.empty()) {
|
if (style.empty()) {
|
||||||
std::vector<std::string> search_files;
|
std::vector<std::string> search_files;
|
||||||
switch (appearance.value_or(portal->getAppearance())) {
|
switch (appearance.value_or(portal->getAppearance())) {
|
||||||
case waybar::Appearance::LIGHT:
|
case waybar::Appearance::LIGHT:
|
||||||
search_files.emplace_back("style-light.css");
|
search_files.emplace_back("style-light.css");
|
||||||
|
gtk_settings->property_gtk_application_prefer_dark_theme() = false;
|
||||||
break;
|
break;
|
||||||
case waybar::Appearance::DARK:
|
case waybar::Appearance::DARK:
|
||||||
search_files.emplace_back("style-dark.css");
|
search_files.emplace_back("style-dark.css");
|
||||||
|
gtk_settings->property_gtk_application_prefer_dark_theme() = true;
|
||||||
break;
|
break;
|
||||||
case waybar::Appearance::UNKNOWN:
|
case waybar::Appearance::UNKNOWN:
|
||||||
break;
|
break;
|
||||||
@@ -169,9 +173,11 @@ const std::string waybar::Client::getStyle(const std::string &style,
|
|||||||
} else {
|
} else {
|
||||||
css_file = style;
|
css_file = style;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!css_file) {
|
if (!css_file) {
|
||||||
throw std::runtime_error("Missing required resource files");
|
throw std::runtime_error("Missing required resource files");
|
||||||
}
|
}
|
||||||
|
|
||||||
spdlog::info("Using CSS file {}", css_file.value());
|
spdlog::info("Using CSS file {}", css_file.value());
|
||||||
return css_file.value();
|
return css_file.value();
|
||||||
};
|
};
|
||||||
|
|||||||
+35
-4
@@ -2,7 +2,11 @@
|
|||||||
|
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#ifndef __OpenBSD__
|
||||||
#include <wordexp.h>
|
#include <wordexp.h>
|
||||||
|
#else
|
||||||
|
#include <glob.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
@@ -34,6 +38,7 @@ std::vector<std::string> Config::tryExpandPath(const std::string &base,
|
|||||||
spdlog::debug("Try expanding: {}", path.string());
|
spdlog::debug("Try expanding: {}", path.string());
|
||||||
|
|
||||||
std::vector<std::string> results;
|
std::vector<std::string> results;
|
||||||
|
#ifndef __OpenBSD__
|
||||||
wordexp_t p;
|
wordexp_t p;
|
||||||
if (wordexp(path.c_str(), &p, 0) == 0) {
|
if (wordexp(path.c_str(), &p, 0) == 0) {
|
||||||
for (size_t i = 0; i < p.we_wordc; i++) {
|
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);
|
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;
|
return results;
|
||||||
}
|
}
|
||||||
@@ -89,19 +106,33 @@ void Config::setupConfig(Json::Value &dst, const std::string &config_file, int d
|
|||||||
mergeConfig(dst, tmp_config);
|
mergeConfig(dst, tmp_config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> Config::findIncludePath(const std::string &name) {
|
||||||
|
auto match1 = tryExpandPath(name, "");
|
||||||
|
if (!match1.empty()) {
|
||||||
|
return match1.front();
|
||||||
|
}
|
||||||
|
return findConfigPath({name});
|
||||||
|
}
|
||||||
|
|
||||||
void Config::resolveConfigIncludes(Json::Value &config, int depth) {
|
void Config::resolveConfigIncludes(Json::Value &config, int depth) {
|
||||||
Json::Value includes = config["include"];
|
Json::Value includes = config["include"];
|
||||||
if (includes.isArray()) {
|
if (includes.isArray()) {
|
||||||
for (const auto &include : includes) {
|
for (const auto &include : includes) {
|
||||||
spdlog::info("Including resource file: {}", include.asString());
|
spdlog::info("Including resource file: {}", include.asString());
|
||||||
for (const auto &match : tryExpandPath(include.asString(), "")) {
|
auto match = findIncludePath(include.asString());
|
||||||
setupConfig(config, match, depth + 1);
|
if (match.has_value()) {
|
||||||
|
setupConfig(config, match.value(), depth + 1);
|
||||||
|
} else {
|
||||||
|
spdlog::warn("Unable to find resource file: {}", include.asString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (includes.isString()) {
|
} else if (includes.isString()) {
|
||||||
spdlog::info("Including resource file: {}", includes.asString());
|
spdlog::info("Including resource file: {}", includes.asString());
|
||||||
for (const auto &match : tryExpandPath(includes.asString(), "")) {
|
auto match = findIncludePath(includes.asString());
|
||||||
setupConfig(config, match, depth + 1);
|
if (match.has_value()) {
|
||||||
|
setupConfig(config, match.value(), depth + 1);
|
||||||
|
} else {
|
||||||
|
spdlog::warn("Unable to find resource file: {}", includes.asString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+29
-5
@@ -17,8 +17,8 @@
|
|||||||
#ifdef HAVE_WLR_TASKBAR
|
#ifdef HAVE_WLR_TASKBAR
|
||||||
#include "modules/wlr/taskbar.hpp"
|
#include "modules/wlr/taskbar.hpp"
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_WLR_WORKSPACES
|
#ifdef HAVE_EXT_WORKSPACES
|
||||||
#include "modules/wlr/workspace_manager.hpp"
|
#include "modules/ext/workspace_manager.hpp"
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_RIVER
|
#ifdef HAVE_RIVER
|
||||||
#include "modules/river/layout.hpp"
|
#include "modules/river/layout.hpp"
|
||||||
@@ -34,6 +34,7 @@
|
|||||||
#include "modules/hyprland/language.hpp"
|
#include "modules/hyprland/language.hpp"
|
||||||
#include "modules/hyprland/submap.hpp"
|
#include "modules/hyprland/submap.hpp"
|
||||||
#include "modules/hyprland/window.hpp"
|
#include "modules/hyprland/window.hpp"
|
||||||
|
#include "modules/hyprland/windowcount.hpp"
|
||||||
#include "modules/hyprland/workspaces.hpp"
|
#include "modules/hyprland/workspaces.hpp"
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_NIRI
|
#ifdef HAVE_NIRI
|
||||||
@@ -41,6 +42,10 @@
|
|||||||
#include "modules/niri/window.hpp"
|
#include "modules/niri/window.hpp"
|
||||||
#include "modules/niri/workspaces.hpp"
|
#include "modules/niri/workspaces.hpp"
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef HAVE_WAYFIRE
|
||||||
|
#include "modules/wayfire/window.hpp"
|
||||||
|
#include "modules/wayfire/workspaces.hpp"
|
||||||
|
#endif
|
||||||
#if defined(__FreeBSD__) || defined(__linux__)
|
#if defined(__FreeBSD__) || defined(__linux__)
|
||||||
#include "modules/battery.hpp"
|
#include "modules/battery.hpp"
|
||||||
#endif
|
#endif
|
||||||
@@ -109,6 +114,9 @@
|
|||||||
#ifdef HAVE_SYSTEMD_MONITOR
|
#ifdef HAVE_SYSTEMD_MONITOR
|
||||||
#include "modules/systemd_failed_units.hpp"
|
#include "modules/systemd_failed_units.hpp"
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef HAVE_LIBGPS
|
||||||
|
#include "modules/gps.hpp"
|
||||||
|
#endif
|
||||||
#include "modules/cffi.hpp"
|
#include "modules/cffi.hpp"
|
||||||
#include "modules/custom.hpp"
|
#include "modules/custom.hpp"
|
||||||
#include "modules/image.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]);
|
return new waybar::modules::wlr::Taskbar(id, bar_, config_[name]);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_WLR_WORKSPACES
|
#ifdef HAVE_EXT_WORKSPACES
|
||||||
if (ref == "wlr/workspaces") {
|
if (ref == "ext/workspaces") {
|
||||||
return new waybar::modules::wlr::WorkspaceManager(id, bar_, config_[name]);
|
return new waybar::modules::ext::WorkspaceManager(id, bar_, config_[name]);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_RIVER
|
#ifdef HAVE_RIVER
|
||||||
@@ -201,6 +209,9 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name,
|
|||||||
if (ref == "hyprland/window") {
|
if (ref == "hyprland/window") {
|
||||||
return new waybar::modules::hyprland::Window(id, bar_, config_[name]);
|
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") {
|
if (ref == "hyprland/language") {
|
||||||
return new waybar::modules::hyprland::Language(id, bar_, config_[name]);
|
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") {
|
if (ref == "niri/workspaces") {
|
||||||
return new waybar::modules::niri::Workspaces(id, bar_, config_[name]);
|
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
|
#endif
|
||||||
if (ref == "idle_inhibitor") {
|
if (ref == "idle_inhibitor") {
|
||||||
return new waybar::modules::IdleInhibitor(id, bar_, config_[name]);
|
return new waybar::modules::IdleInhibitor(id, bar_, config_[name]);
|
||||||
@@ -331,6 +350,11 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name,
|
|||||||
if (ref == "systemd-failed-units") {
|
if (ref == "systemd-failed-units") {
|
||||||
return new waybar::modules::SystemdFailedUnits(id, config_[name]);
|
return new waybar::modules::SystemdFailedUnits(id, config_[name]);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_LIBGPS
|
||||||
|
if (ref == "gps") {
|
||||||
|
return new waybar::modules::Gps(id, config_[name]);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
if (ref == "temperature") {
|
if (ref == "temperature") {
|
||||||
return new waybar::modules::Temperature(id, config_[name]);
|
return new waybar::modules::Temperature(id, config_[name]);
|
||||||
|
|||||||
+125
-57
@@ -1,3 +1,4 @@
|
|||||||
|
#include <fcntl.h>
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
@@ -6,27 +7,134 @@
|
|||||||
#include <list>
|
#include <list>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
|
#include "bar.hpp"
|
||||||
#include "client.hpp"
|
#include "client.hpp"
|
||||||
|
#include "util/SafeSignal.hpp"
|
||||||
|
|
||||||
std::mutex reap_mtx;
|
std::mutex reap_mtx;
|
||||||
std::list<pid_t> reap;
|
std::list<pid_t> reap;
|
||||||
volatile bool reload;
|
|
||||||
|
|
||||||
void* signalThread(void* args) {
|
static int signal_pipe_write_fd;
|
||||||
int err;
|
|
||||||
int signum;
|
// Write a single signal to `signal_pipe_write_fd`.
|
||||||
sigset_t mask;
|
// This function is set as a signal handler, so it must be async-signal-safe.
|
||||||
sigemptyset(&mask);
|
static void writeSignalToPipe(int signum) {
|
||||||
sigaddset(&mask, SIGCHLD);
|
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) {
|
while (true) {
|
||||||
err = sigwait(&mask, &signum);
|
int signum;
|
||||||
if (err != 0) {
|
ssize_t amt = read(signal_pipe_read_fd, &signum, sizeof(int));
|
||||||
spdlog::error("sigwait failed: {}", strerror(errno));
|
if (amt < 0) {
|
||||||
|
spdlog::error("read from signal pipe failed with error {}, closing thread", strerror(errno));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (amt != sizeof(int)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
signal_handler.emit(signum);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
switch (signum) {
|
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:
|
case SIGCHLD:
|
||||||
spdlog::debug("Received SIGCHLD in signalThread");
|
spdlog::debug("Received SIGCHLD in signalThread");
|
||||||
if (!reap.empty()) {
|
if (!reap.empty()) {
|
||||||
@@ -45,61 +153,21 @@ void* signalThread(void* args) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void startSignalThread() {
|
|
||||||
int err;
|
|
||||||
sigset_t mask;
|
|
||||||
sigemptyset(&mask);
|
|
||||||
sigaddset(&mask, SIGCHLD);
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
try {
|
try {
|
||||||
auto* client = waybar::Client::inst();
|
auto* client = waybar::Client::inst();
|
||||||
|
|
||||||
std::signal(SIGUSR1, [](int /*signal*/) {
|
bool reload;
|
||||||
for (auto& bar : waybar::Client::inst()->bars) {
|
|
||||||
bar->toggle();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
std::signal(SIGUSR2, [](int /*signal*/) {
|
waybar::SafeSignal<int> posix_signal_received;
|
||||||
spdlog::info("Reloading...");
|
posix_signal_received.connect([&](int signum) { handleSignalMainThread(signum, reload); });
|
||||||
reload = true;
|
|
||||||
waybar::Client::inst()->reset();
|
|
||||||
});
|
|
||||||
|
|
||||||
std::signal(SIGINT, [](int /*signal*/) {
|
std::thread signal_thread([&]() { catchSignals(posix_signal_received); });
|
||||||
spdlog::info("Quitting.");
|
|
||||||
reload = false;
|
|
||||||
waybar::Client::inst()->reset();
|
|
||||||
});
|
|
||||||
|
|
||||||
for (int sig = SIGRTMIN + 1; sig <= SIGRTMAX; ++sig) {
|
// Every `std::thread` must be joined or detached.
|
||||||
std::signal(sig, [](int sig) {
|
// This thread should run forever, so detach it.
|
||||||
for (auto& bar : waybar::Client::inst()->bars) {
|
signal_thread.detach();
|
||||||
bar->handleSignal(sig);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
startSignalThread();
|
|
||||||
|
|
||||||
auto ret = 0;
|
auto ret = 0;
|
||||||
do {
|
do {
|
||||||
|
|||||||
@@ -38,10 +38,20 @@ auto waybar::modules::Backlight::update() -> void {
|
|||||||
event_box_.show();
|
event_box_.show();
|
||||||
const uint8_t percent =
|
const uint8_t percent =
|
||||||
best->get_max() == 0 ? 100 : round(best->get_actual() * 100.0f / best->get_max());
|
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)));
|
fmt::arg("icon", getIcon(percent)));
|
||||||
label_.set_markup(desc);
|
label_.set_markup(desc);
|
||||||
getState(percent);
|
|
||||||
if (tooltipEnabled()) {
|
if (tooltipEnabled()) {
|
||||||
std::string tooltip_format;
|
std::string tooltip_format;
|
||||||
if (config_["tooltip-format"].isString()) {
|
if (config_["tooltip-format"].isString()) {
|
||||||
|
|||||||
+35
-6
@@ -1,14 +1,16 @@
|
|||||||
#include "modules/battery.hpp"
|
#include "modules/battery.hpp"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <cctype>
|
||||||
|
|
||||||
|
#include "util/command.hpp"
|
||||||
#if defined(__FreeBSD__)
|
#if defined(__FreeBSD__)
|
||||||
#include <sys/sysctl.h>
|
#include <sys/sysctl.h>
|
||||||
#endif
|
#endif
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
waybar::modules::Battery::Battery(const std::string& id, const Bar& bar, const Json::Value& config)
|
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__)
|
#if defined(__linux__)
|
||||||
battery_watch_fd_ = inotify_init1(IN_CLOEXEC);
|
battery_watch_fd_ = inotify_init1(IN_CLOEXEC);
|
||||||
if (battery_watch_fd_ == -1) {
|
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");
|
throw std::runtime_error("Could not watch for battery plug/unplug");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
spdlog::debug("battery: worker interval is {}", interval_.count());
|
||||||
worker();
|
worker();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -677,18 +680,22 @@ auto waybar::modules::Battery::update() -> void {
|
|||||||
}
|
}
|
||||||
auto status_pretty = status;
|
auto status_pretty = status;
|
||||||
// Transform to lowercase and replace space with dash
|
// Transform to lowercase and replace space with dash
|
||||||
std::transform(status.begin(), status.end(), status.begin(),
|
std::ranges::transform(status.begin(), status.end(), status.begin(),
|
||||||
[](char ch) { return ch == ' ' ? '-' : std::tolower(ch); });
|
[](char ch) { return ch == ' ' ? '-' : std::tolower(ch); });
|
||||||
auto format = format_;
|
auto format = format_;
|
||||||
auto state = getState(capacity, true);
|
auto state = getState(capacity, true);
|
||||||
|
processEvents(state, status, capacity);
|
||||||
setBarClass(state);
|
setBarClass(state);
|
||||||
auto time_remaining_formatted = formatTimeRemaining(time_remaining);
|
auto time_remaining_formatted = formatTimeRemaining(time_remaining);
|
||||||
if (tooltipEnabled()) {
|
if (tooltipEnabled()) {
|
||||||
std::string tooltip_text_default;
|
std::string tooltip_text_default;
|
||||||
std::string tooltip_format = "{timeTo}";
|
std::string tooltip_format = "{timeTo}";
|
||||||
if (time_remaining != 0) {
|
if (time_remaining != 0) {
|
||||||
std::string time_to = std::string("Time to ") + ((time_remaining > 0) ? "empty" : "full");
|
if (time_remaining > 0) {
|
||||||
tooltip_text_default = time_to + ": " + time_remaining_formatted;
|
tooltip_text_default = std::string("Empty in ") + time_remaining_formatted;
|
||||||
|
} else {
|
||||||
|
tooltip_text_default = std::string("Full in ") + time_remaining_formatted;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
tooltip_text_default = status_pretty;
|
tooltip_text_default = status_pretty;
|
||||||
}
|
}
|
||||||
@@ -701,7 +708,7 @@ auto waybar::modules::Battery::update() -> void {
|
|||||||
} else if (config_["tooltip-format"].isString()) {
|
} else if (config_["tooltip-format"].isString()) {
|
||||||
tooltip_format = config_["tooltip-format"].asString();
|
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::format(fmt::runtime(tooltip_format), fmt::arg("timeTo", tooltip_text_default),
|
||||||
fmt::arg("power", power), fmt::arg("capacity", capacity),
|
fmt::arg("power", power), fmt::arg("capacity", capacity),
|
||||||
fmt::arg("time", time_remaining_formatted), fmt::arg("cycles", cycles),
|
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);
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+24
-8
@@ -1,5 +1,6 @@
|
|||||||
#include "modules/clock.hpp"
|
#include "modules/clock.hpp"
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
#include <gtkmm/tooltip.h>
|
#include <gtkmm/tooltip.h>
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
@@ -16,6 +17,7 @@
|
|||||||
#include <clocale>
|
#include <clocale>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
using namespace date;
|
||||||
namespace fmt_lib = waybar::util::date::format;
|
namespace fmt_lib = waybar::util::date::format;
|
||||||
|
|
||||||
waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
|
waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
|
||||||
@@ -25,6 +27,7 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
|
|||||||
m_tooltip_{new Gtk::Label()},
|
m_tooltip_{new Gtk::Label()},
|
||||||
cldInTooltip_{m_tlpFmt_.find("{" + kCldPlaceholder + "}") != std::string::npos},
|
cldInTooltip_{m_tlpFmt_.find("{" + kCldPlaceholder + "}") != std::string::npos},
|
||||||
cldYearShift_{January / 1 / 1900},
|
cldYearShift_{January / 1 / 1900},
|
||||||
|
cldMonShift_{year(1900) / January},
|
||||||
tzInTooltip_{m_tlpFmt_.find("{" + kTZPlaceholder + "}") != std::string::npos},
|
tzInTooltip_{m_tlpFmt_.find("{" + kTZPlaceholder + "}") != std::string::npos},
|
||||||
tzCurrIdx_{0},
|
tzCurrIdx_{0},
|
||||||
ordInTooltip_{m_tlpFmt_.find("{" + kOrdPlaceholder + "}") != std::string::npos} {
|
ordInTooltip_{m_tlpFmt_.find("{" + kOrdPlaceholder + "}") != std::string::npos} {
|
||||||
@@ -348,9 +351,9 @@ auto waybar::modules::Clock::get_calendar(const year_month_day& today, const yea
|
|||||||
m_locale_, fmtMap_[4],
|
m_locale_, fmtMap_[4],
|
||||||
fmt_lib::make_format_args(
|
fmt_lib::make_format_args(
|
||||||
(line == 2)
|
(line == 2)
|
||||||
? static_cast<const date::zoned_seconds&&>(
|
? static_cast<const zoned_seconds&&>(
|
||||||
zoned_seconds{tz, local_days{ymTmp / 1}})
|
zoned_seconds{tz, local_days{ymTmp / 1}})
|
||||||
: static_cast<const date::zoned_seconds&&>(zoned_seconds{
|
: static_cast<const zoned_seconds&&>(zoned_seconds{
|
||||||
tz, local_days{cldGetWeekForLine(ymTmp, firstdow, line)}})))
|
tz, local_days{cldGetWeekForLine(ymTmp, firstdow, line)}})))
|
||||||
<< ' ';
|
<< ' ';
|
||||||
} else
|
} else
|
||||||
@@ -358,10 +361,23 @@ auto waybar::modules::Clock::get_calendar(const year_month_day& today, const yea
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
os << Glib::ustring::format((cldWPos_ != WS::LEFT || line == 0) ? std::left : std::right,
|
// Count wide characters to avoid extra padding
|
||||||
std::setfill(L' '),
|
size_t wideCharCount = 0;
|
||||||
std::setw(cldMonColLen_ + ((line < 2) ? cldWnLen_ : 0)),
|
std::string calendarLine = getCalendarLine(today, ymTmp, line, firstdow, &m_locale_);
|
||||||
getCalendarLine(today, ymTmp, line, firstdow, &m_locale_));
|
if (line < 2) {
|
||||||
|
for (gchar *data = calendarLine.data(), *end = data + calendarLine.size();
|
||||||
|
data != nullptr;) {
|
||||||
|
gunichar c = g_utf8_get_char_validated(data, end - data);
|
||||||
|
if (g_unichar_iswide(c)) {
|
||||||
|
wideCharCount++;
|
||||||
|
}
|
||||||
|
data = g_utf8_find_next_char(data, end);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
os << Glib::ustring::format(
|
||||||
|
(cldWPos_ != WS::LEFT || line == 0) ? std::left : std::right, std::setfill(L' '),
|
||||||
|
std::setw(cldMonColLen_ + ((line < 2) ? cldWnLen_ - wideCharCount : 0)),
|
||||||
|
calendarLine);
|
||||||
|
|
||||||
// Week numbers on the right
|
// Week numbers on the right
|
||||||
if (cldWPos_ == WS::RIGHT && line > 0) {
|
if (cldWPos_ == WS::RIGHT && line > 0) {
|
||||||
@@ -371,9 +387,9 @@ auto waybar::modules::Clock::get_calendar(const year_month_day& today, const yea
|
|||||||
<< fmt_lib::vformat(
|
<< fmt_lib::vformat(
|
||||||
m_locale_, fmtMap_[4],
|
m_locale_, fmtMap_[4],
|
||||||
fmt_lib::make_format_args(
|
fmt_lib::make_format_args(
|
||||||
(line == 2) ? static_cast<const date::zoned_seconds&&>(
|
(line == 2) ? static_cast<const zoned_seconds&&>(
|
||||||
zoned_seconds{tz, local_days{ymTmp / 1}})
|
zoned_seconds{tz, local_days{ymTmp / 1}})
|
||||||
: static_cast<const date::zoned_seconds&&>(
|
: static_cast<const zoned_seconds&&>(
|
||||||
zoned_seconds{tz, local_days{cldGetWeekForLine(
|
zoned_seconds{tz, local_days{cldGetWeekForLine(
|
||||||
ymTmp, firstdow, line)}})));
|
ymTmp, firstdow, line)}})));
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -5,11 +5,12 @@
|
|||||||
|
|
||||||
std::vector<float> waybar::modules::CpuFrequency::parseCpuFrequencies() {
|
std::vector<float> waybar::modules::CpuFrequency::parseCpuFrequencies() {
|
||||||
std::vector<float> frequencies;
|
std::vector<float> frequencies;
|
||||||
char buffer[256];
|
|
||||||
size_t len;
|
size_t len;
|
||||||
int32_t freq;
|
int32_t freq;
|
||||||
uint32_t i = 0;
|
|
||||||
|
|
||||||
|
#ifndef __OpenBSD__
|
||||||
|
char buffer[256];
|
||||||
|
uint32_t i = 0;
|
||||||
while (true) {
|
while (true) {
|
||||||
len = 4;
|
len = 4;
|
||||||
snprintf(buffer, 256, "dev.cpu.%u.freq", i);
|
snprintf(buffer, 256, "dev.cpu.%u.freq", i);
|
||||||
@@ -17,6 +18,12 @@ std::vector<float> waybar::modules::CpuFrequency::parseCpuFrequencies() {
|
|||||||
frequencies.push_back(freq);
|
frequencies.push_back(freq);
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
int getMhz[] = {CTL_HW, HW_CPUSPEED};
|
||||||
|
len = sizeof(freq);
|
||||||
|
sysctl(getMhz, 2, &freq, &len, NULL, 0);
|
||||||
|
frequencies.push_back((float)freq);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (frequencies.empty()) {
|
if (frequencies.empty()) {
|
||||||
spdlog::warn("cpu/bsd: parseCpuFrequencies failed, not found in sysctl");
|
spdlog::warn("cpu/bsd: parseCpuFrequencies failed, not found in sysctl");
|
||||||
|
|||||||
+11
-1
@@ -14,6 +14,9 @@ waybar::modules::Custom::Custom(const std::string& name, const std::string& id,
|
|||||||
percentage_(0),
|
percentage_(0),
|
||||||
fp_(nullptr),
|
fp_(nullptr),
|
||||||
pid_(-1) {
|
pid_(-1) {
|
||||||
|
if (config.isNull()) {
|
||||||
|
spdlog::warn("There is no configuration for 'custom/{}', element will be hidden", name);
|
||||||
|
}
|
||||||
dp.emit();
|
dp.emit();
|
||||||
if (!config_["signal"].empty() && config_["interval"].empty() &&
|
if (!config_["signal"].empty() && config_["interval"].empty() &&
|
||||||
config_["restart-interval"].empty()) {
|
config_["restart-interval"].empty()) {
|
||||||
@@ -35,6 +38,13 @@ waybar::modules::Custom::~Custom() {
|
|||||||
|
|
||||||
void waybar::modules::Custom::delayWorker() {
|
void waybar::modules::Custom::delayWorker() {
|
||||||
thread_ = [this] {
|
thread_ = [this] {
|
||||||
|
for (int i : this->pid_children_) {
|
||||||
|
int status;
|
||||||
|
waitpid(i, &status, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->pid_children_.clear();
|
||||||
|
|
||||||
bool can_update = true;
|
bool can_update = true;
|
||||||
if (config_["exec-if"].isString()) {
|
if (config_["exec-if"].isString()) {
|
||||||
output_ = util::command::execNoRead(config_["exec-if"].asString());
|
output_ = util::command::execNoRead(config_["exec-if"].asString());
|
||||||
@@ -62,7 +72,7 @@ void waybar::modules::Custom::continuousWorker() {
|
|||||||
}
|
}
|
||||||
thread_ = [this, cmd] {
|
thread_ = [this, cmd] {
|
||||||
char* buff = nullptr;
|
char* buff = nullptr;
|
||||||
waybar::util::ScopeGuard buff_deleter([buff]() {
|
waybar::util::ScopeGuard buff_deleter([&buff]() {
|
||||||
if (buff) {
|
if (buff) {
|
||||||
free(buff);
|
free(buff);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,508 @@
|
|||||||
|
#include "modules/ext/workspace_manager.hpp"
|
||||||
|
|
||||||
|
#include <gdk/gdkwayland.h>
|
||||||
|
#include <gtkmm.h>
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "client.hpp"
|
||||||
|
#include "gtkmm/widget.h"
|
||||||
|
#include "modules/ext/workspace_manager_binding.hpp"
|
||||||
|
|
||||||
|
namespace waybar::modules::ext {
|
||||||
|
|
||||||
|
// WorkspaceManager
|
||||||
|
|
||||||
|
uint32_t WorkspaceManager::group_global_id = 0;
|
||||||
|
uint32_t WorkspaceManager::workspace_global_id = 0;
|
||||||
|
std::map<std::string, std::string> Workspace::icon_map_;
|
||||||
|
|
||||||
|
WorkspaceManager::WorkspaceManager(const std::string &id, const waybar::Bar &bar,
|
||||||
|
const Json::Value &config)
|
||||||
|
: waybar::AModule(config, "workspaces", id, false, false), bar_(bar), box_(bar.orientation, 0) {
|
||||||
|
add_registry_listener(this);
|
||||||
|
|
||||||
|
// parse configuration
|
||||||
|
|
||||||
|
const auto config_sort_by_number = config_["sort-by-number"];
|
||||||
|
if (config_sort_by_number.isBool()) {
|
||||||
|
spdlog::warn("[ext/workspaces]: Prefer sort-by-id instead of sort-by-number");
|
||||||
|
sort_by_id_ = config_sort_by_number.asBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto config_sort_by_id = config_["sort-by-id"];
|
||||||
|
if (config_sort_by_id.isBool()) {
|
||||||
|
sort_by_id_ = config_sort_by_id.asBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto config_sort_by_name = config_["sort-by-name"];
|
||||||
|
if (config_sort_by_name.isBool()) {
|
||||||
|
sort_by_name_ = config_sort_by_name.asBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto config_sort_by_coordinates = config_["sort-by-coordinates"];
|
||||||
|
if (config_sort_by_coordinates.isBool()) {
|
||||||
|
sort_by_coordinates_ = config_sort_by_coordinates.asBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto config_all_outputs = config_["all-outputs"];
|
||||||
|
if (config_all_outputs.isBool()) {
|
||||||
|
all_outputs_ = config_all_outputs.asBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
// setup UI
|
||||||
|
|
||||||
|
box_.set_name("workspaces");
|
||||||
|
if (!id.empty()) {
|
||||||
|
box_.get_style_context()->add_class(id);
|
||||||
|
}
|
||||||
|
box_.get_style_context()->add_class(MODULE_CLASS);
|
||||||
|
event_box_.add(box_);
|
||||||
|
|
||||||
|
spdlog::debug("[ext/workspaces]: Workspace manager created");
|
||||||
|
}
|
||||||
|
|
||||||
|
WorkspaceManager::~WorkspaceManager() {
|
||||||
|
workspaces_.clear();
|
||||||
|
groups_.clear();
|
||||||
|
|
||||||
|
if (ext_manager_ != nullptr) {
|
||||||
|
auto *display = Client::inst()->wl_display;
|
||||||
|
// Send `stop` request and wait for one roundtrip.
|
||||||
|
ext_workspace_manager_v1_stop(ext_manager_);
|
||||||
|
wl_display_roundtrip(display);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ext_manager_ != nullptr) {
|
||||||
|
spdlog::warn("[ext/workspaces]: Destroying workspace manager before .finished event");
|
||||||
|
ext_workspace_manager_v1_destroy(ext_manager_);
|
||||||
|
}
|
||||||
|
|
||||||
|
spdlog::debug("[ext/workspaces]: Workspace manager destroyed");
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorkspaceManager::register_manager(wl_registry *registry, uint32_t name, uint32_t version) {
|
||||||
|
if (ext_manager_ != nullptr) {
|
||||||
|
spdlog::warn("[ext/workspaces]: Register workspace manager again although already registered!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (version != 1) {
|
||||||
|
spdlog::warn("[ext/workspaces]: Using different workspace manager protocol version: {}",
|
||||||
|
version);
|
||||||
|
}
|
||||||
|
|
||||||
|
ext_manager_ = workspace_manager_bind(registry, name, version, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorkspaceManager::remove_workspace_group(uint32_t id) {
|
||||||
|
const auto it =
|
||||||
|
std::find_if(groups_.begin(), groups_.end(), [id](const auto &g) { return g->id() == id; });
|
||||||
|
|
||||||
|
if (it == groups_.end()) {
|
||||||
|
spdlog::warn("[ext/workspaces]: Can't find workspace group with id {}", id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
groups_.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorkspaceManager::remove_workspace(uint32_t id) {
|
||||||
|
const auto it = std::find_if(workspaces_.begin(), workspaces_.end(),
|
||||||
|
[id](const auto &w) { return w->id() == id; });
|
||||||
|
|
||||||
|
if (it == workspaces_.end()) {
|
||||||
|
spdlog::warn("[ext/workspaces]: Can't find workspace with id {}", id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
workspaces_.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorkspaceManager::handle_workspace_group(ext_workspace_group_handle_v1 *handle) {
|
||||||
|
const auto new_id = ++group_global_id;
|
||||||
|
groups_.push_back(std::make_unique<WorkspaceGroup>(*this, handle, new_id));
|
||||||
|
spdlog::debug("[ext/workspaces]: Workspace group {} created", new_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorkspaceManager::handle_workspace(ext_workspace_handle_v1 *handle) {
|
||||||
|
const auto new_id = ++workspace_global_id;
|
||||||
|
const auto new_name = std::to_string(++workspace_name);
|
||||||
|
workspaces_.push_back(std::make_unique<Workspace>(config_, *this, handle, new_id, new_name));
|
||||||
|
set_needs_sorting();
|
||||||
|
spdlog::debug("[ext/workspaces]: Workspace {} created", new_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorkspaceManager::handle_done() { dp.emit(); }
|
||||||
|
|
||||||
|
void WorkspaceManager::handle_finished() {
|
||||||
|
spdlog::debug("[ext/workspaces]: Finishing workspace manager");
|
||||||
|
ext_workspace_manager_v1_destroy(ext_manager_);
|
||||||
|
ext_manager_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorkspaceManager::commit() const { ext_workspace_manager_v1_commit(ext_manager_); }
|
||||||
|
|
||||||
|
void WorkspaceManager::update() {
|
||||||
|
spdlog::debug("[ext/workspaces]: Updating state");
|
||||||
|
|
||||||
|
if (needs_sorting_) {
|
||||||
|
clear_buttons();
|
||||||
|
sort_workspaces();
|
||||||
|
needs_sorting_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
update_buttons();
|
||||||
|
AModule::update();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WorkspaceManager::has_button(const Gtk::Button *button) {
|
||||||
|
const auto buttons = box_.get_children();
|
||||||
|
return std::find(buttons.begin(), buttons.end(), button) != buttons.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorkspaceManager::sort_workspaces() {
|
||||||
|
// determine if workspace ID's and names can be sort numerically or literally
|
||||||
|
|
||||||
|
auto is_numeric = [](const std::string &s) {
|
||||||
|
return !s.empty() && std::all_of(s.begin(), s.end(), ::isdigit);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto sort_by_workspace_id_numerically =
|
||||||
|
std::all_of(workspaces_.begin(), workspaces_.end(),
|
||||||
|
[&](const auto &w) { return is_numeric(w->workspace_id()); });
|
||||||
|
|
||||||
|
auto sort_by_name_numerically = std::all_of(workspaces_.begin(), workspaces_.end(),
|
||||||
|
[&](const auto &w) { return is_numeric(w->name()); });
|
||||||
|
|
||||||
|
// sort based on configuration setting with sort-by-id as fallback
|
||||||
|
|
||||||
|
std::sort(workspaces_.begin(), workspaces_.end(), [&](const auto &w1, const auto &w2) {
|
||||||
|
if (sort_by_id_ || (!sort_by_name_ && !sort_by_coordinates_)) {
|
||||||
|
if (w1->workspace_id() == w2->workspace_id()) {
|
||||||
|
return w1->id() < w2->id();
|
||||||
|
}
|
||||||
|
if (sort_by_workspace_id_numerically) {
|
||||||
|
// the idea is that phonetic compare can be applied just to numbers
|
||||||
|
// with same number of digits
|
||||||
|
return w1->workspace_id().size() < w2->workspace_id().size() ||
|
||||||
|
(w1->workspace_id().size() == w2->workspace_id().size() &&
|
||||||
|
w1->workspace_id() < w2->workspace_id());
|
||||||
|
}
|
||||||
|
return w1->workspace_id() < w2->workspace_id();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sort_by_name_) {
|
||||||
|
if (w1->name() == w2->name()) {
|
||||||
|
return w1->id() < w2->id();
|
||||||
|
}
|
||||||
|
if (sort_by_name_numerically) {
|
||||||
|
// see above about numeric sorting
|
||||||
|
return w1->name().size() < w2->name().size() ||
|
||||||
|
(w1->name().size() == w2->name().size() && w1->name() < w2->name());
|
||||||
|
}
|
||||||
|
return w1->name() < w2->name();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sort_by_coordinates_) {
|
||||||
|
if (w1->coordinates() == w2->coordinates()) {
|
||||||
|
return w1->id() < w2->id();
|
||||||
|
}
|
||||||
|
return w1->coordinates() < w2->coordinates();
|
||||||
|
}
|
||||||
|
|
||||||
|
return w1->id() < w2->id();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorkspaceManager::clear_buttons() {
|
||||||
|
for (const auto &workspace : workspaces_) {
|
||||||
|
if (has_button(&workspace->button())) {
|
||||||
|
box_.remove(workspace->button());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorkspaceManager::update_buttons() {
|
||||||
|
const auto *output = gdk_wayland_monitor_get_wl_output(bar_.output->monitor->gobj());
|
||||||
|
|
||||||
|
// go through all workspace
|
||||||
|
|
||||||
|
for (const auto &workspace : workspaces_) {
|
||||||
|
const bool workspace_on_any_group_for_output =
|
||||||
|
std::any_of(groups_.begin(), groups_.end(), [&](const auto &group) {
|
||||||
|
const bool group_on_output = group->has_output(output) || all_outputs_;
|
||||||
|
const bool workspace_on_group = group->has_workspace(workspace->handle());
|
||||||
|
return group_on_output && workspace_on_group;
|
||||||
|
});
|
||||||
|
const bool bar_contains_button = has_button(&workspace->button());
|
||||||
|
|
||||||
|
// add or remove buttons if needed, update button state
|
||||||
|
|
||||||
|
if (workspace_on_any_group_for_output) {
|
||||||
|
if (!bar_contains_button) {
|
||||||
|
// add button to bar
|
||||||
|
box_.pack_start(workspace->button(), false, false);
|
||||||
|
workspace->button().show_all();
|
||||||
|
}
|
||||||
|
workspace->update();
|
||||||
|
} else {
|
||||||
|
if (bar_contains_button) {
|
||||||
|
// remove button from bar
|
||||||
|
box_.remove(workspace->button());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WorkspaceGroup
|
||||||
|
|
||||||
|
WorkspaceGroup::WorkspaceGroup(WorkspaceManager &manager, ext_workspace_group_handle_v1 *handle,
|
||||||
|
uint32_t id)
|
||||||
|
: workspaces_manager_(manager), ext_handle_(handle), id_(id) {
|
||||||
|
add_workspace_group_listener(ext_handle_, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
WorkspaceGroup::~WorkspaceGroup() {
|
||||||
|
if (ext_handle_ != nullptr) {
|
||||||
|
ext_workspace_group_handle_v1_destroy(ext_handle_);
|
||||||
|
}
|
||||||
|
spdlog::debug("[ext/workspaces]: Workspace group {} destroyed", id_);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WorkspaceGroup::has_output(const wl_output *output) {
|
||||||
|
return std::find(outputs_.begin(), outputs_.end(), output) != outputs_.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WorkspaceGroup::has_workspace(const ext_workspace_handle_v1 *workspace) {
|
||||||
|
return std::find(workspaces_.begin(), workspaces_.end(), workspace) != workspaces_.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorkspaceGroup::handle_capabilities(uint32_t capabilities) {
|
||||||
|
spdlog::debug("[ext/workspaces]: Capabilities for workspace group {}:", id_);
|
||||||
|
if ((capabilities & EXT_WORKSPACE_GROUP_HANDLE_V1_GROUP_CAPABILITIES_CREATE_WORKSPACE) ==
|
||||||
|
capabilities) {
|
||||||
|
spdlog::debug("[ext/workspaces]: - create-workspace");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorkspaceGroup::handle_output_enter(wl_output *output) { outputs_.push_back(output); }
|
||||||
|
|
||||||
|
void WorkspaceGroup::handle_output_leave(wl_output *output) {
|
||||||
|
const auto it = std::find(outputs_.begin(), outputs_.end(), output);
|
||||||
|
if (it != outputs_.end()) {
|
||||||
|
outputs_.erase(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorkspaceGroup::handle_workspace_enter(ext_workspace_handle_v1 *handle) {
|
||||||
|
workspaces_.push_back(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorkspaceGroup::handle_workspace_leave(ext_workspace_handle_v1 *handle) {
|
||||||
|
const auto it = std::find(workspaces_.begin(), workspaces_.end(), handle);
|
||||||
|
if (it != workspaces_.end()) {
|
||||||
|
workspaces_.erase(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorkspaceGroup::handle_removed() {
|
||||||
|
spdlog::debug("[ext/workspaces]: Removing workspace group {}", id_);
|
||||||
|
workspaces_manager_.remove_workspace_group(id_);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Workspace
|
||||||
|
|
||||||
|
Workspace::Workspace(const Json::Value &config, WorkspaceManager &manager,
|
||||||
|
ext_workspace_handle_v1 *handle, uint32_t id, const std::string &name)
|
||||||
|
: workspace_manager_(manager), ext_handle_(handle), id_(id), workspace_id_(name), name_(name) {
|
||||||
|
add_workspace_listener(ext_handle_, this);
|
||||||
|
|
||||||
|
// parse configuration
|
||||||
|
|
||||||
|
const auto &config_active_only = config["active-only"];
|
||||||
|
if (config_active_only.isBool()) {
|
||||||
|
active_only_ = config_active_only.asBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto &config_ignore_hidden = config["ignore-hidden"];
|
||||||
|
if (config_ignore_hidden.isBool()) {
|
||||||
|
ignore_hidden_ = config_ignore_hidden.asBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto &config_format = config["format"];
|
||||||
|
format_ = config_format.isString() ? config_format.asString() : "{name}";
|
||||||
|
with_icon_ = format_.find("{icon}") != std::string::npos;
|
||||||
|
|
||||||
|
if (with_icon_ && icon_map_.empty()) {
|
||||||
|
const auto &format_icons = config["format-icons"];
|
||||||
|
for (auto &n : format_icons.getMemberNames()) {
|
||||||
|
icon_map_.emplace(n, format_icons[n].asString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool config_on_click = config["on-click"].isString();
|
||||||
|
if (config_on_click) {
|
||||||
|
on_click_action_ = config["on-click"].asString();
|
||||||
|
}
|
||||||
|
const bool config_on_click_middle = config["on-click-middle"].isString();
|
||||||
|
if (config_on_click_middle) {
|
||||||
|
on_click_middle_action_ = config["on-click"].asString();
|
||||||
|
}
|
||||||
|
const bool config_on_click_right = config["on-click-right"].isString();
|
||||||
|
if (config_on_click_right) {
|
||||||
|
on_click_right_action_ = config["on-click"].asString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// setup UI
|
||||||
|
|
||||||
|
if (config_on_click || config_on_click_middle || config_on_click_right) {
|
||||||
|
button_.add_events(Gdk::BUTTON_PRESS_MASK);
|
||||||
|
button_.signal_button_press_event().connect(sigc::mem_fun(*this, &Workspace::handle_clicked),
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
|
||||||
|
button_.set_relief(Gtk::RELIEF_NONE);
|
||||||
|
content_.set_center_widget(label_);
|
||||||
|
button_.add(content_);
|
||||||
|
}
|
||||||
|
|
||||||
|
Workspace::~Workspace() {
|
||||||
|
if (ext_handle_ != nullptr) {
|
||||||
|
ext_workspace_handle_v1_destroy(ext_handle_);
|
||||||
|
}
|
||||||
|
spdlog::debug("[ext/workspaces]: Workspace {} destroyed", id_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Workspace::update() {
|
||||||
|
if (!needs_updating_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update style and visibility
|
||||||
|
|
||||||
|
const auto style_context = button_.get_style_context();
|
||||||
|
style_context->remove_class("active");
|
||||||
|
style_context->remove_class("urgent");
|
||||||
|
style_context->remove_class("hidden");
|
||||||
|
|
||||||
|
if (has_state(EXT_WORKSPACE_HANDLE_V1_STATE_ACTIVE)) {
|
||||||
|
button_.set_visible(true);
|
||||||
|
style_context->add_class("active");
|
||||||
|
}
|
||||||
|
if (has_state(EXT_WORKSPACE_HANDLE_V1_STATE_URGENT)) {
|
||||||
|
button_.set_visible(true);
|
||||||
|
style_context->add_class("urgent");
|
||||||
|
}
|
||||||
|
if (has_state(EXT_WORKSPACE_HANDLE_V1_STATE_HIDDEN)) {
|
||||||
|
button_.set_visible(!active_only_ && !ignore_hidden_);
|
||||||
|
style_context->add_class("hidden");
|
||||||
|
}
|
||||||
|
if (state_ == 0) {
|
||||||
|
button_.set_visible(!active_only_);
|
||||||
|
}
|
||||||
|
|
||||||
|
// update label
|
||||||
|
label_.set_markup(fmt::format(fmt::runtime(format_), fmt::arg("name", name_),
|
||||||
|
fmt::arg("id", workspace_id_),
|
||||||
|
fmt::arg("icon", with_icon_ ? icon() : "")));
|
||||||
|
|
||||||
|
needs_updating_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Workspace::handle_id(const std::string &id) {
|
||||||
|
spdlog::debug("[ext/workspaces]: ID for workspace {}: {}", id_, id);
|
||||||
|
workspace_id_ = id;
|
||||||
|
needs_updating_ = true;
|
||||||
|
workspace_manager_.set_needs_sorting();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Workspace::handle_name(const std::string &name) {
|
||||||
|
spdlog::debug("[ext/workspaces]: Name for workspace {}: {}", id_, name);
|
||||||
|
name_ = name;
|
||||||
|
needs_updating_ = true;
|
||||||
|
workspace_manager_.set_needs_sorting();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Workspace::handle_coordinates(const std::vector<uint32_t> &coordinates) {
|
||||||
|
coordinates_ = coordinates;
|
||||||
|
needs_updating_ = true;
|
||||||
|
workspace_manager_.set_needs_sorting();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Workspace::handle_state(uint32_t state) {
|
||||||
|
state_ = state;
|
||||||
|
needs_updating_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Workspace::handle_capabilities(uint32_t capabilities) {
|
||||||
|
spdlog::debug("[ext/workspaces]: Capabilities for workspace {}:", id_);
|
||||||
|
if ((capabilities & EXT_WORKSPACE_HANDLE_V1_WORKSPACE_CAPABILITIES_ACTIVATE) == capabilities) {
|
||||||
|
spdlog::debug("[ext/workspaces]: - activate");
|
||||||
|
}
|
||||||
|
if ((capabilities & EXT_WORKSPACE_HANDLE_V1_WORKSPACE_CAPABILITIES_DEACTIVATE) == capabilities) {
|
||||||
|
spdlog::debug("[ext/workspaces]: - deactivate");
|
||||||
|
}
|
||||||
|
if ((capabilities & EXT_WORKSPACE_HANDLE_V1_WORKSPACE_CAPABILITIES_REMOVE) == capabilities) {
|
||||||
|
spdlog::debug("[ext/workspaces]: - remove");
|
||||||
|
}
|
||||||
|
if ((capabilities & EXT_WORKSPACE_HANDLE_V1_WORKSPACE_CAPABILITIES_ASSIGN) == capabilities) {
|
||||||
|
spdlog::debug("[ext/workspaces]: - assign");
|
||||||
|
}
|
||||||
|
needs_updating_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Workspace::handle_removed() {
|
||||||
|
spdlog::debug("[ext/workspaces]: Removing workspace {}", id_);
|
||||||
|
workspace_manager_.remove_workspace(id_);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Workspace::handle_clicked(const GdkEventButton *button) const {
|
||||||
|
std::string action;
|
||||||
|
if (button->button == GDK_BUTTON_PRIMARY) {
|
||||||
|
action = on_click_action_;
|
||||||
|
} else if (button->button == GDK_BUTTON_MIDDLE) {
|
||||||
|
action = on_click_middle_action_;
|
||||||
|
} else if (button->button == GDK_BUTTON_SECONDARY) {
|
||||||
|
action = on_click_right_action_;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action.empty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action == "activate") {
|
||||||
|
ext_workspace_handle_v1_activate(ext_handle_);
|
||||||
|
} else if (action == "close") {
|
||||||
|
ext_workspace_handle_v1_remove(ext_handle_);
|
||||||
|
} else {
|
||||||
|
spdlog::warn("[ext/workspaces]: Unknown action {}", action);
|
||||||
|
}
|
||||||
|
workspace_manager_.commit();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Workspace::icon() {
|
||||||
|
if (has_state(EXT_WORKSPACE_HANDLE_V1_STATE_ACTIVE)) {
|
||||||
|
const auto active_icon_it = icon_map_.find("active");
|
||||||
|
if (active_icon_it != icon_map_.end()) {
|
||||||
|
return active_icon_it->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto named_icon_it = icon_map_.find(name_);
|
||||||
|
if (named_icon_it != icon_map_.end()) {
|
||||||
|
return named_icon_it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto default_icon_it = icon_map_.find("default");
|
||||||
|
if (default_icon_it != icon_map_.end()) {
|
||||||
|
return default_icon_it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
return name_;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace waybar::modules::ext
|
||||||
@@ -0,0 +1,159 @@
|
|||||||
|
#include "modules/ext/workspace_manager_binding.hpp"
|
||||||
|
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include "client.hpp"
|
||||||
|
#include "modules/ext/workspace_manager.hpp"
|
||||||
|
|
||||||
|
namespace waybar::modules::ext {
|
||||||
|
|
||||||
|
static void handle_global(void *data, wl_registry *registry, uint32_t name, const char *interface,
|
||||||
|
uint32_t version) {
|
||||||
|
if (std::strcmp(interface, ext_workspace_manager_v1_interface.name) == 0) {
|
||||||
|
static_cast<WorkspaceManager *>(data)->register_manager(registry, name, version);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_global_remove(void *data, wl_registry *registry, uint32_t name) {
|
||||||
|
/* Nothing to do here */
|
||||||
|
}
|
||||||
|
|
||||||
|
static const wl_registry_listener registry_listener_impl = {.global = handle_global,
|
||||||
|
.global_remove = handle_global_remove};
|
||||||
|
|
||||||
|
void add_registry_listener(void *data) {
|
||||||
|
wl_display *display = Client::inst()->wl_display;
|
||||||
|
wl_registry *registry = wl_display_get_registry(display);
|
||||||
|
|
||||||
|
wl_registry_add_listener(registry, ®istry_listener_impl, data);
|
||||||
|
wl_display_roundtrip(display);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void workspace_manager_handle_workspace_group(
|
||||||
|
void *data, ext_workspace_manager_v1 *_, ext_workspace_group_handle_v1 *workspace_group) {
|
||||||
|
static_cast<WorkspaceManager *>(data)->handle_workspace_group(workspace_group);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void workspace_manager_handle_workspace(void *data, ext_workspace_manager_v1 *_,
|
||||||
|
ext_workspace_handle_v1 *workspace) {
|
||||||
|
static_cast<WorkspaceManager *>(data)->handle_workspace(workspace);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void workspace_manager_handle_done(void *data, ext_workspace_manager_v1 *_) {
|
||||||
|
static_cast<WorkspaceManager *>(data)->handle_done();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void workspace_manager_handle_finished(void *data, ext_workspace_manager_v1 *_) {
|
||||||
|
static_cast<WorkspaceManager *>(data)->handle_finished();
|
||||||
|
}
|
||||||
|
|
||||||
|
static const ext_workspace_manager_v1_listener workspace_manager_impl = {
|
||||||
|
.workspace_group = workspace_manager_handle_workspace_group,
|
||||||
|
.workspace = workspace_manager_handle_workspace,
|
||||||
|
.done = workspace_manager_handle_done,
|
||||||
|
.finished = workspace_manager_handle_finished,
|
||||||
|
};
|
||||||
|
|
||||||
|
ext_workspace_manager_v1 *workspace_manager_bind(wl_registry *registry, uint32_t name,
|
||||||
|
uint32_t version, void *data) {
|
||||||
|
auto *workspace_manager = static_cast<ext_workspace_manager_v1 *>(
|
||||||
|
wl_registry_bind(registry, name, &ext_workspace_manager_v1_interface, version));
|
||||||
|
|
||||||
|
if (workspace_manager)
|
||||||
|
ext_workspace_manager_v1_add_listener(workspace_manager, &workspace_manager_impl, data);
|
||||||
|
else
|
||||||
|
spdlog::error("Failed to register manager");
|
||||||
|
|
||||||
|
return workspace_manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void workspace_group_handle_capabilities(void *data, ext_workspace_group_handle_v1 *_,
|
||||||
|
uint32_t capabilities) {
|
||||||
|
static_cast<WorkspaceGroup *>(data)->handle_capabilities(capabilities);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void workspace_group_handle_output_enter(void *data, ext_workspace_group_handle_v1 *_,
|
||||||
|
wl_output *output) {
|
||||||
|
static_cast<WorkspaceGroup *>(data)->handle_output_enter(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void workspace_group_handle_output_leave(void *data, ext_workspace_group_handle_v1 *_,
|
||||||
|
wl_output *output) {
|
||||||
|
static_cast<WorkspaceGroup *>(data)->handle_output_leave(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void workspace_group_handle_workspace_enter(void *data, ext_workspace_group_handle_v1 *_,
|
||||||
|
ext_workspace_handle_v1 *workspace) {
|
||||||
|
static_cast<WorkspaceGroup *>(data)->handle_workspace_enter(workspace);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void workspace_group_handle_workspace_leave(void *data, ext_workspace_group_handle_v1 *_,
|
||||||
|
ext_workspace_handle_v1 *workspace) {
|
||||||
|
static_cast<WorkspaceGroup *>(data)->handle_workspace_leave(workspace);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void workspace_group_handle_removed(void *data, ext_workspace_group_handle_v1 *_) {
|
||||||
|
static_cast<WorkspaceGroup *>(data)->handle_removed();
|
||||||
|
}
|
||||||
|
|
||||||
|
static const ext_workspace_group_handle_v1_listener workspace_group_impl = {
|
||||||
|
.capabilities = workspace_group_handle_capabilities,
|
||||||
|
.output_enter = workspace_group_handle_output_enter,
|
||||||
|
.output_leave = workspace_group_handle_output_leave,
|
||||||
|
.workspace_enter = workspace_group_handle_workspace_enter,
|
||||||
|
.workspace_leave = workspace_group_handle_workspace_leave,
|
||||||
|
.removed = workspace_group_handle_removed};
|
||||||
|
|
||||||
|
void add_workspace_group_listener(ext_workspace_group_handle_v1 *workspace_group_handle,
|
||||||
|
void *data) {
|
||||||
|
ext_workspace_group_handle_v1_add_listener(workspace_group_handle, &workspace_group_impl, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void workspace_handle_name(void *data, struct ext_workspace_handle_v1 *_, const char *name) {
|
||||||
|
static_cast<Workspace *>(data)->handle_name(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void workspace_handle_id(void *data, struct ext_workspace_handle_v1 *_, const char *id) {
|
||||||
|
static_cast<Workspace *>(data)->handle_id(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void workspace_handle_coordinates(void *data, struct ext_workspace_handle_v1 *_,
|
||||||
|
struct wl_array *coordinates) {
|
||||||
|
std::vector<uint32_t> coords_vec;
|
||||||
|
auto coords = static_cast<uint32_t *>(coordinates->data);
|
||||||
|
for (size_t i = 0; i < coordinates->size / sizeof(uint32_t); ++i) {
|
||||||
|
coords_vec.push_back(coords[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static_cast<Workspace *>(data)->handle_coordinates(coords_vec);
|
||||||
|
}
|
||||||
|
|
||||||
|
void workspace_handle_state(void *data, struct ext_workspace_handle_v1 *workspace_handle,
|
||||||
|
uint32_t state) {
|
||||||
|
static_cast<Workspace *>(data)->handle_state(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void workspace_handle_capabilities(void *data,
|
||||||
|
struct ext_workspace_handle_v1 *workspace_handle,
|
||||||
|
uint32_t capabilities) {
|
||||||
|
static_cast<Workspace *>(data)->handle_capabilities(capabilities);
|
||||||
|
}
|
||||||
|
|
||||||
|
void workspace_handle_removed(void *data, struct ext_workspace_handle_v1 *workspace_handle) {
|
||||||
|
static_cast<Workspace *>(data)->handle_removed();
|
||||||
|
}
|
||||||
|
|
||||||
|
static const ext_workspace_handle_v1_listener workspace_impl = {
|
||||||
|
.id = workspace_handle_id,
|
||||||
|
.name = workspace_handle_name,
|
||||||
|
.coordinates = workspace_handle_coordinates,
|
||||||
|
.state = workspace_handle_state,
|
||||||
|
.capabilities = workspace_handle_capabilities,
|
||||||
|
.removed = workspace_handle_removed};
|
||||||
|
|
||||||
|
void add_workspace_listener(ext_workspace_handle_v1 *workspace_handle, void *data) {
|
||||||
|
ext_workspace_handle_v1_add_listener(workspace_handle, &workspace_impl, data);
|
||||||
|
}
|
||||||
|
} // namespace waybar::modules::ext
|
||||||
@@ -0,0 +1,216 @@
|
|||||||
|
#include "modules/gps.hpp"
|
||||||
|
|
||||||
|
#include <gps.h>
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
// In the 80000 version of fmt library authors decided to optimize imports
|
||||||
|
// and moved declarations required for fmt::dynamic_format_arg_store in new
|
||||||
|
// header fmt/args.h
|
||||||
|
#if (FMT_VERSION >= 80000)
|
||||||
|
#include <fmt/args.h>
|
||||||
|
#else
|
||||||
|
#include <fmt/core.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
using namespace waybar::util;
|
||||||
|
constexpr const char* DEFAULT_FORMAT = "{mode}";
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
waybar::modules::Gps::Gps(const std::string& id, const Json::Value& config)
|
||||||
|
: ALabel(config, "gps", id, "{}", 5)
|
||||||
|
#ifdef WANT_RFKILL
|
||||||
|
,
|
||||||
|
rfkill_{RFKILL_TYPE_GPS}
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
thread_ = [this] {
|
||||||
|
dp.emit();
|
||||||
|
thread_.sleep_for(interval_);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (0 != gps_open("localhost", "2947", &gps_data_)) {
|
||||||
|
throw std::runtime_error("Can't open gpsd socket");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config_["hide-disconnected"].isBool()) {
|
||||||
|
hideDisconnected = config_["hide-disconnected"].asBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config_["hide-no-fix"].isBool()) {
|
||||||
|
hideNoFix = config_["hide-no-fix"].asBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
gps_thread_ = [this] {
|
||||||
|
dp.emit();
|
||||||
|
gps_stream(&gps_data_, WATCH_ENABLE, NULL);
|
||||||
|
int last_gps_mode = 0;
|
||||||
|
|
||||||
|
while (gps_waiting(&gps_data_, 5000000)) {
|
||||||
|
if (gps_read(&gps_data_, NULL, 0) == -1) {
|
||||||
|
throw std::runtime_error("Can't read data from gpsd.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (MODE_SET != (MODE_SET & gps_data_.set)) {
|
||||||
|
// did not even get mode, nothing to see here
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gps_data_.fix.mode != last_gps_mode) {
|
||||||
|
// significant update
|
||||||
|
dp.emit();
|
||||||
|
}
|
||||||
|
last_gps_mode = gps_data_.fix.mode;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef WANT_RFKILL
|
||||||
|
rfkill_.on_update.connect(sigc::hide(sigc::mem_fun(*this, &Gps::update)));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string waybar::modules::Gps::getFixModeName() const {
|
||||||
|
switch (gps_data_.fix.mode) {
|
||||||
|
case MODE_NO_FIX:
|
||||||
|
return "fix-none";
|
||||||
|
case MODE_2D:
|
||||||
|
return "fix-2d";
|
||||||
|
case MODE_3D:
|
||||||
|
return "fix-3d";
|
||||||
|
default:
|
||||||
|
#ifdef WANT_RFKILL
|
||||||
|
if (rfkill_.getState()) return "disabled";
|
||||||
|
#endif
|
||||||
|
return "disconnected";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string waybar::modules::Gps::getFixModeString() const {
|
||||||
|
switch (gps_data_.fix.mode) {
|
||||||
|
case MODE_NO_FIX:
|
||||||
|
return "No fix";
|
||||||
|
case MODE_2D:
|
||||||
|
return "2D Fix";
|
||||||
|
case MODE_3D:
|
||||||
|
return "3D Fix";
|
||||||
|
default:
|
||||||
|
return "Disconnected";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string waybar::modules::Gps::getFixStatusString() const {
|
||||||
|
switch (gps_data_.fix.status) {
|
||||||
|
case STATUS_GPS:
|
||||||
|
return "GPS";
|
||||||
|
case STATUS_DGPS:
|
||||||
|
return "DGPS";
|
||||||
|
case STATUS_RTK_FIX:
|
||||||
|
return "RTK Fixed";
|
||||||
|
case STATUS_RTK_FLT:
|
||||||
|
return "RTK Float";
|
||||||
|
case STATUS_DR:
|
||||||
|
return "Dead Reckoning";
|
||||||
|
case STATUS_GNSSDR:
|
||||||
|
return "GNSS + Dead Reckoning";
|
||||||
|
case STATUS_TIME:
|
||||||
|
return "Time Only";
|
||||||
|
case STATUS_PPS_FIX:
|
||||||
|
return "PPS Fix";
|
||||||
|
default:
|
||||||
|
|
||||||
|
#ifdef WANT_RFKILL
|
||||||
|
if (rfkill_.getState()) return "Disabled";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto waybar::modules::Gps::update() -> void {
|
||||||
|
sleep(0); // Wait for gps status change
|
||||||
|
|
||||||
|
if ((gps_data_.fix.mode == MODE_NOT_SEEN && hideDisconnected) ||
|
||||||
|
(gps_data_.fix.mode == MODE_NO_FIX && hideNoFix)) {
|
||||||
|
event_box_.set_visible(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show the module
|
||||||
|
if (!event_box_.get_visible()) event_box_.set_visible(true);
|
||||||
|
|
||||||
|
std::string tooltip_format;
|
||||||
|
|
||||||
|
if (!alt_) {
|
||||||
|
auto state = getFixModeName();
|
||||||
|
if (!state_.empty() && label_.get_style_context()->has_class(state_)) {
|
||||||
|
label_.get_style_context()->remove_class(state_);
|
||||||
|
}
|
||||||
|
if (config_["format-" + state].isString()) {
|
||||||
|
default_format_ = config_["format-" + state].asString();
|
||||||
|
} else if (config_["format"].isString()) {
|
||||||
|
default_format_ = config_["format"].asString();
|
||||||
|
} else {
|
||||||
|
default_format_ = DEFAULT_FORMAT;
|
||||||
|
}
|
||||||
|
if (config_["tooltip-format-" + state].isString()) {
|
||||||
|
tooltip_format = config_["tooltip-format-" + state].asString();
|
||||||
|
}
|
||||||
|
if (!label_.get_style_context()->has_class(state)) {
|
||||||
|
label_.get_style_context()->add_class(state);
|
||||||
|
}
|
||||||
|
format_ = default_format_;
|
||||||
|
state_ = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto format = format_;
|
||||||
|
|
||||||
|
fmt::dynamic_format_arg_store<fmt::format_context> store;
|
||||||
|
store.push_back(fmt::arg("mode", getFixModeString()));
|
||||||
|
store.push_back(fmt::arg("status", getFixStatusString()));
|
||||||
|
|
||||||
|
store.push_back(fmt::arg("latitude", gps_data_.fix.latitude));
|
||||||
|
store.push_back(fmt::arg("latitude_error", gps_data_.fix.epy));
|
||||||
|
|
||||||
|
store.push_back(fmt::arg("longitude", gps_data_.fix.longitude));
|
||||||
|
store.push_back(fmt::arg("longitude_error", gps_data_.fix.epx));
|
||||||
|
|
||||||
|
store.push_back(fmt::arg("altitude_hae", gps_data_.fix.altHAE));
|
||||||
|
store.push_back(fmt::arg("altitude_msl", gps_data_.fix.altMSL));
|
||||||
|
store.push_back(fmt::arg("altitude_error", gps_data_.fix.epv));
|
||||||
|
|
||||||
|
store.push_back(fmt::arg("speed", gps_data_.fix.speed));
|
||||||
|
store.push_back(fmt::arg("speed_error", gps_data_.fix.eps));
|
||||||
|
|
||||||
|
store.push_back(fmt::arg("climb", gps_data_.fix.climb));
|
||||||
|
store.push_back(fmt::arg("climb_error", gps_data_.fix.epc));
|
||||||
|
|
||||||
|
store.push_back(fmt::arg("satellites_used", gps_data_.satellites_used));
|
||||||
|
store.push_back(fmt::arg("satellites_visible", gps_data_.satellites_visible));
|
||||||
|
|
||||||
|
auto text = fmt::vformat(format, store);
|
||||||
|
|
||||||
|
if (tooltipEnabled()) {
|
||||||
|
if (tooltip_format.empty() && config_["tooltip-format"].isString()) {
|
||||||
|
tooltip_format = config_["tooltip-format"].asString();
|
||||||
|
}
|
||||||
|
if (!tooltip_format.empty()) {
|
||||||
|
auto tooltip_text = fmt::vformat(tooltip_format, store);
|
||||||
|
if (label_.get_tooltip_text() != tooltip_text) {
|
||||||
|
label_.set_tooltip_markup(tooltip_text);
|
||||||
|
}
|
||||||
|
} else if (label_.get_tooltip_text() != text) {
|
||||||
|
label_.set_tooltip_markup(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
label_.set_markup(text);
|
||||||
|
// Call parent update
|
||||||
|
ALabel::update();
|
||||||
|
}
|
||||||
|
|
||||||
|
waybar::modules::Gps::~Gps() {
|
||||||
|
gps_stream(&gps_data_, WATCH_DISABLE, NULL);
|
||||||
|
gps_close(&gps_data_);
|
||||||
|
}
|
||||||
@@ -66,7 +66,18 @@ auto Language::update() -> void {
|
|||||||
void Language::onEvent(const std::string& ev) {
|
void Language::onEvent(const std::string& ev) {
|
||||||
std::lock_guard<std::mutex> lg(mutex_);
|
std::lock_guard<std::mutex> lg(mutex_);
|
||||||
std::string kbName(begin(ev) + ev.find_last_of('>') + 1, begin(ev) + ev.find_first_of(','));
|
std::string kbName(begin(ev) + ev.find_last_of('>') + 1, begin(ev) + ev.find_first_of(','));
|
||||||
auto layoutName = ev.substr(ev.find_last_of(',') + 1);
|
|
||||||
|
// Last comma before variants parenthesis, eg:
|
||||||
|
// activelayout>>micro-star-int'l-co.,-ltd.-msi-gk50-elite-gaming-keyboard,English (US, intl.,
|
||||||
|
// with dead keys)
|
||||||
|
std::string beforeParenthesis;
|
||||||
|
auto parenthesisPos = ev.find_last_of('(');
|
||||||
|
if (parenthesisPos == std::string::npos) {
|
||||||
|
beforeParenthesis = ev;
|
||||||
|
} else {
|
||||||
|
beforeParenthesis = std::string(begin(ev), begin(ev) + parenthesisPos);
|
||||||
|
}
|
||||||
|
auto layoutName = ev.substr(beforeParenthesis.find_last_of(',') + 1);
|
||||||
|
|
||||||
if (config_.isMember("keyboard-name") && kbName != config_["keyboard-name"].asString())
|
if (config_.isMember("keyboard-name") && kbName != config_["keyboard-name"].asString())
|
||||||
return; // ignore
|
return; // ignore
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ Submap::Submap(const std::string& id, const Bar& bar, const Json::Value& config)
|
|||||||
ALabel::update();
|
ALabel::update();
|
||||||
|
|
||||||
// Displays widget immediately if always_on_ assuming default submap
|
// Displays widget immediately if always_on_ assuming default submap
|
||||||
// Needs an actual way to retrive current submap on startup
|
// Needs an actual way to retrieve current submap on startup
|
||||||
if (always_on_) {
|
if (always_on_) {
|
||||||
submap_ = default_submap_;
|
submap_ = default_submap_;
|
||||||
label_.get_style_context()->add_class(submap_);
|
label_.get_style_context()->add_class(submap_);
|
||||||
@@ -68,8 +68,7 @@ void Submap::onEvent(const std::string& ev) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto submapName = ev.substr(ev.find_last_of('>') + 1);
|
auto submapName = ev.substr(ev.find_first_of('>') + 2);
|
||||||
submapName = waybar::util::sanitize_string(submapName);
|
|
||||||
|
|
||||||
if (!submap_.empty()) {
|
if (!submap_.empty()) {
|
||||||
label_.get_style_context()->remove_class(submap_);
|
label_.get_style_context()->remove_class(submap_);
|
||||||
|
|||||||
@@ -0,0 +1,142 @@
|
|||||||
|
#include "modules/hyprland/windowcount.hpp"
|
||||||
|
|
||||||
|
#include <glibmm/fileutils.h>
|
||||||
|
#include <glibmm/keyfile.h>
|
||||||
|
#include <glibmm/miscutils.h>
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "modules/hyprland/backend.hpp"
|
||||||
|
#include "util/sanitize_str.hpp"
|
||||||
|
|
||||||
|
namespace waybar::modules::hyprland {
|
||||||
|
|
||||||
|
WindowCount::WindowCount(const std::string& id, const Bar& bar, const Json::Value& config)
|
||||||
|
: AAppIconLabel(config, "windowcount", id, "{count}", 0, true), bar_(bar) {
|
||||||
|
modulesReady = true;
|
||||||
|
separateOutputs_ =
|
||||||
|
config.isMember("separate-outputs") ? config["separate-outputs"].asBool() : true;
|
||||||
|
|
||||||
|
if (!gIPC) {
|
||||||
|
gIPC = std::make_unique<IPC>();
|
||||||
|
}
|
||||||
|
|
||||||
|
queryActiveWorkspace();
|
||||||
|
update();
|
||||||
|
dp.emit();
|
||||||
|
|
||||||
|
// register for hyprland ipc
|
||||||
|
gIPC->registerForIPC("fullscreen", this);
|
||||||
|
gIPC->registerForIPC("workspace", this);
|
||||||
|
gIPC->registerForIPC("focusedmon", this);
|
||||||
|
gIPC->registerForIPC("openwindow", this);
|
||||||
|
gIPC->registerForIPC("closewindow", this);
|
||||||
|
gIPC->registerForIPC("movewindow", this);
|
||||||
|
}
|
||||||
|
|
||||||
|
WindowCount::~WindowCount() {
|
||||||
|
gIPC->unregisterForIPC(this);
|
||||||
|
// wait for possible event handler to finish
|
||||||
|
std::lock_guard<std::mutex> lg(mutex_);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto WindowCount::update() -> void {
|
||||||
|
std::lock_guard<std::mutex> lg(mutex_);
|
||||||
|
|
||||||
|
std::string format = config_["format"].asString();
|
||||||
|
std::string formatEmpty = config_["format-empty"].asString();
|
||||||
|
std::string formatWindowed = config_["format-windowed"].asString();
|
||||||
|
std::string formatFullscreen = config_["format-fullscreen"].asString();
|
||||||
|
|
||||||
|
setClass("empty", workspace_.windows == 0);
|
||||||
|
setClass("fullscreen", workspace_.hasfullscreen);
|
||||||
|
|
||||||
|
if (workspace_.windows == 0 && !formatEmpty.empty()) {
|
||||||
|
label_.set_markup(fmt::format(fmt::runtime(formatEmpty), workspace_.windows));
|
||||||
|
} else if (!workspace_.hasfullscreen && !formatWindowed.empty()) {
|
||||||
|
label_.set_markup(fmt::format(fmt::runtime(formatWindowed), workspace_.windows));
|
||||||
|
} else if (workspace_.hasfullscreen && !formatFullscreen.empty()) {
|
||||||
|
label_.set_markup(fmt::format(fmt::runtime(formatFullscreen), workspace_.windows));
|
||||||
|
} else if (!format.empty()) {
|
||||||
|
label_.set_markup(fmt::format(fmt::runtime(format), workspace_.windows));
|
||||||
|
} else {
|
||||||
|
label_.set_text(fmt::format("{}", workspace_.windows));
|
||||||
|
}
|
||||||
|
|
||||||
|
label_.show();
|
||||||
|
AAppIconLabel::update();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto WindowCount::getActiveWorkspace() -> Workspace {
|
||||||
|
const auto workspace = gIPC->getSocket1JsonReply("activeworkspace");
|
||||||
|
|
||||||
|
if (workspace.isObject()) {
|
||||||
|
return Workspace::parse(workspace);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto WindowCount::getActiveWorkspace(const std::string& monitorName) -> Workspace {
|
||||||
|
const auto monitors = gIPC->getSocket1JsonReply("monitors");
|
||||||
|
if (monitors.isArray()) {
|
||||||
|
auto monitor = std::find_if(monitors.begin(), monitors.end(), [&](Json::Value monitor) {
|
||||||
|
return monitor["name"] == monitorName;
|
||||||
|
});
|
||||||
|
if (monitor == std::end(monitors)) {
|
||||||
|
spdlog::warn("Monitor not found: {}", monitorName);
|
||||||
|
return Workspace{-1, 0, false};
|
||||||
|
}
|
||||||
|
const int id = (*monitor)["activeWorkspace"]["id"].asInt();
|
||||||
|
|
||||||
|
const auto workspaces = gIPC->getSocket1JsonReply("workspaces");
|
||||||
|
if (workspaces.isArray()) {
|
||||||
|
auto workspace = std::find_if(workspaces.begin(), workspaces.end(),
|
||||||
|
[&](Json::Value workspace) { return workspace["id"] == id; });
|
||||||
|
if (workspace == std::end(workspaces)) {
|
||||||
|
spdlog::warn("No workspace with id {}", id);
|
||||||
|
return Workspace{-1, 0, false};
|
||||||
|
}
|
||||||
|
return Workspace::parse(*workspace);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto WindowCount::Workspace::parse(const Json::Value& value) -> WindowCount::Workspace {
|
||||||
|
return Workspace{
|
||||||
|
value["id"].asInt(),
|
||||||
|
value["windows"].asInt(),
|
||||||
|
value["hasfullscreen"].asBool(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindowCount::queryActiveWorkspace() {
|
||||||
|
std::lock_guard<std::mutex> lg(mutex_);
|
||||||
|
|
||||||
|
if (separateOutputs_) {
|
||||||
|
workspace_ = getActiveWorkspace(this->bar_.output->name);
|
||||||
|
} else {
|
||||||
|
workspace_ = getActiveWorkspace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindowCount::onEvent(const std::string& ev) {
|
||||||
|
queryActiveWorkspace();
|
||||||
|
dp.emit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindowCount::setClass(const std::string& classname, bool enable) {
|
||||||
|
if (enable) {
|
||||||
|
if (!bar_.window.get_style_context()->has_class(classname)) {
|
||||||
|
bar_.window.get_style_context()->add_class(classname);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bar_.window.get_style_context()->remove_class(classname);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace waybar::modules::hyprland
|
||||||
@@ -20,7 +20,7 @@ WindowCreationPayload::WindowCreationPayload(Json::Value const &client_data)
|
|||||||
}
|
}
|
||||||
|
|
||||||
WindowCreationPayload::WindowCreationPayload(std::string workspace_name,
|
WindowCreationPayload::WindowCreationPayload(std::string workspace_name,
|
||||||
WindowAddress window_address, std::string window_repr)
|
WindowAddress window_address, WindowRepr window_repr)
|
||||||
: m_window(std::move(window_repr)),
|
: m_window(std::move(window_repr)),
|
||||||
m_windowAddress(std::move(window_address)),
|
m_windowAddress(std::move(window_address)),
|
||||||
m_workspaceName(std::move(workspace_name)) {
|
m_workspaceName(std::move(workspace_name)) {
|
||||||
@@ -30,10 +30,11 @@ WindowCreationPayload::WindowCreationPayload(std::string workspace_name,
|
|||||||
|
|
||||||
WindowCreationPayload::WindowCreationPayload(std::string workspace_name,
|
WindowCreationPayload::WindowCreationPayload(std::string workspace_name,
|
||||||
WindowAddress window_address, std::string window_class,
|
WindowAddress window_address, std::string window_class,
|
||||||
std::string window_title)
|
std::string window_title, bool is_active)
|
||||||
: m_window(std::make_pair(std::move(window_class), std::move(window_title))),
|
: m_window(std::make_pair(std::move(window_class), std::move(window_title))),
|
||||||
m_windowAddress(std::move(window_address)),
|
m_windowAddress(std::move(window_address)),
|
||||||
m_workspaceName(std::move(workspace_name)) {
|
m_workspaceName(std::move(workspace_name)),
|
||||||
|
m_isActive(is_active) {
|
||||||
clearAddr();
|
clearAddr();
|
||||||
clearWorkspaceName();
|
clearWorkspaceName();
|
||||||
}
|
}
|
||||||
@@ -88,17 +89,18 @@ bool WindowCreationPayload::isEmpty(Workspaces &workspace_manager) {
|
|||||||
|
|
||||||
int WindowCreationPayload::incrementTimeSpentUncreated() { return m_timeSpentUncreated++; }
|
int WindowCreationPayload::incrementTimeSpentUncreated() { return m_timeSpentUncreated++; }
|
||||||
|
|
||||||
void WindowCreationPayload::moveToWorksace(std::string &new_workspace_name) {
|
void WindowCreationPayload::moveToWorkspace(std::string &new_workspace_name) {
|
||||||
m_workspaceName = new_workspace_name;
|
m_workspaceName = new_workspace_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string WindowCreationPayload::repr(Workspaces &workspace_manager) {
|
WindowRepr WindowCreationPayload::repr(Workspaces &workspace_manager) {
|
||||||
if (std::holds_alternative<Repr>(m_window)) {
|
if (std::holds_alternative<Repr>(m_window)) {
|
||||||
return std::get<Repr>(m_window);
|
return std::get<Repr>(m_window);
|
||||||
}
|
}
|
||||||
if (std::holds_alternative<ClassAndTitle>(m_window)) {
|
if (std::holds_alternative<ClassAndTitle>(m_window)) {
|
||||||
auto [window_class, window_title] = std::get<ClassAndTitle>(m_window);
|
auto const &[window_class, window_title] = std::get<ClassAndTitle>(m_window);
|
||||||
return workspace_manager.getRewrite(window_class, window_title);
|
return {m_windowAddress, window_class, window_title,
|
||||||
|
workspace_manager.getRewrite(window_class, window_title), m_isActive};
|
||||||
}
|
}
|
||||||
// Unreachable
|
// Unreachable
|
||||||
spdlog::error("WorkspaceWindow::repr: Unreachable");
|
spdlog::error("WorkspaceWindow::repr: Unreachable");
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "modules/hyprland/workspaces.hpp"
|
#include "modules/hyprland/workspaces.hpp"
|
||||||
|
#include "util/command.hpp"
|
||||||
|
#include "util/icon_loader.hpp"
|
||||||
|
|
||||||
namespace waybar::modules::hyprland {
|
namespace waybar::modules::hyprland {
|
||||||
|
|
||||||
@@ -32,7 +34,12 @@ Workspace::Workspace(const Json::Value &workspace_data, Workspaces &workspace_ma
|
|||||||
false);
|
false);
|
||||||
|
|
||||||
m_button.set_relief(Gtk::RELIEF_NONE);
|
m_button.set_relief(Gtk::RELIEF_NONE);
|
||||||
m_content.set_center_widget(m_label);
|
if (m_workspaceManager.enableTaskbar()) {
|
||||||
|
m_content.set_orientation(m_workspaceManager.taskbarOrientation());
|
||||||
|
m_content.pack_start(m_labelBefore, false, false);
|
||||||
|
} else {
|
||||||
|
m_content.set_center_widget(m_labelBefore);
|
||||||
|
}
|
||||||
m_button.add(m_content);
|
m_button.add(m_content);
|
||||||
|
|
||||||
initializeWindowMap(clients_data);
|
initializeWindowMap(clients_data);
|
||||||
@@ -47,9 +54,14 @@ void addOrRemoveClass(const Glib::RefPtr<Gtk::StyleContext> &context, bool condi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::string> Workspace::closeWindow(WindowAddress const &addr) {
|
std::optional<WindowRepr> Workspace::closeWindow(WindowAddress const &addr) {
|
||||||
if (m_windowMap.contains(addr)) {
|
auto it = std::ranges::find_if(m_windowMap,
|
||||||
return removeWindow(addr);
|
[&addr](const auto &window) { return window.address == addr; });
|
||||||
|
// If the vector contains the address, remove it and return the window representation
|
||||||
|
if (it != m_windowMap.end()) {
|
||||||
|
WindowRepr windowRepr = *it;
|
||||||
|
m_windowMap.erase(it);
|
||||||
|
return windowRepr;
|
||||||
}
|
}
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
@@ -91,30 +103,38 @@ void Workspace::initializeWindowMap(const Json::Value &clients_data) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Workspace::insertWindow(WindowCreationPayload create_window_paylod) {
|
void Workspace::setActiveWindow(WindowAddress const &addr) {
|
||||||
if (!create_window_paylod.isEmpty(m_workspaceManager)) {
|
for (auto &window : m_windowMap) {
|
||||||
auto repr = create_window_paylod.repr(m_workspaceManager);
|
window.setActive(window.address == addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!repr.empty()) {
|
void Workspace::insertWindow(WindowCreationPayload create_window_payload) {
|
||||||
m_windowMap[create_window_paylod.getAddress()] = repr;
|
if (!create_window_payload.isEmpty(m_workspaceManager)) {
|
||||||
|
auto repr = create_window_payload.repr(m_workspaceManager);
|
||||||
|
|
||||||
|
if (!repr.empty() || m_workspaceManager.enableTaskbar()) {
|
||||||
|
auto addr = create_window_payload.getAddress();
|
||||||
|
auto it = std::ranges::find_if(
|
||||||
|
m_windowMap, [&addr](const auto &window) { return window.address == addr; });
|
||||||
|
// If the vector contains the address, update the window representation, otherwise insert it
|
||||||
|
if (it != m_windowMap.end()) {
|
||||||
|
*it = repr;
|
||||||
|
} else {
|
||||||
|
m_windowMap.emplace_back(repr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
bool Workspace::onWindowOpened(WindowCreationPayload const &create_window_paylod) {
|
bool Workspace::onWindowOpened(WindowCreationPayload const &create_window_payload) {
|
||||||
if (create_window_paylod.getWorkspaceName() == name()) {
|
if (create_window_payload.getWorkspaceName() == name()) {
|
||||||
insertWindow(create_window_paylod);
|
insertWindow(create_window_payload);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Workspace::removeWindow(WindowAddress const &addr) {
|
|
||||||
std::string windowRepr = m_windowMap[addr];
|
|
||||||
m_windowMap.erase(addr);
|
|
||||||
return windowRepr;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string &Workspace::selectIcon(std::map<std::string, std::string> &icons_map) {
|
std::string &Workspace::selectIcon(std::map<std::string, std::string> &icons_map) {
|
||||||
spdlog::trace("Selecting icon for workspace {}", name());
|
spdlog::trace("Selecting icon for workspace {}", name());
|
||||||
if (isUrgent()) {
|
if (isUrgent()) {
|
||||||
@@ -172,7 +192,11 @@ std::string &Workspace::selectIcon(std::map<std::string, std::string> &icons_map
|
|||||||
return m_name;
|
return m_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Workspace::update(const std::string &format, const std::string &icon) {
|
void Workspace::update(const std::string &workspace_icon) {
|
||||||
|
if (this->m_workspaceManager.persistentOnly() && !this->isPersistent()) {
|
||||||
|
m_button.hide();
|
||||||
|
return;
|
||||||
|
}
|
||||||
// clang-format off
|
// clang-format off
|
||||||
if (this->m_workspaceManager.activeOnly() && \
|
if (this->m_workspaceManager.activeOnly() && \
|
||||||
!this->isActive() && \
|
!this->isActive() && \
|
||||||
@@ -200,21 +224,122 @@ void Workspace::update(const std::string &format, const std::string &icon) {
|
|||||||
addOrRemoveClass(styleContext, m_workspaceManager.getBarOutput() == output(), "hosting-monitor");
|
addOrRemoveClass(styleContext, m_workspaceManager.getBarOutput() == output(), "hosting-monitor");
|
||||||
|
|
||||||
std::string windows;
|
std::string windows;
|
||||||
|
// Optimization: The {windows} substitution string is only possible if the taskbar is disabled, no
|
||||||
|
// need to compute this if enableTaskbar() is true
|
||||||
|
if (!m_workspaceManager.enableTaskbar()) {
|
||||||
auto windowSeparator = m_workspaceManager.getWindowSeparator();
|
auto windowSeparator = m_workspaceManager.getWindowSeparator();
|
||||||
|
|
||||||
bool isNotFirst = false;
|
bool isNotFirst = false;
|
||||||
|
|
||||||
for (auto &[_pid, window_repr] : m_windowMap) {
|
for (const auto &window_repr : m_windowMap) {
|
||||||
if (isNotFirst) {
|
if (isNotFirst) {
|
||||||
windows.append(windowSeparator);
|
windows.append(windowSeparator);
|
||||||
}
|
}
|
||||||
isNotFirst = true;
|
isNotFirst = true;
|
||||||
windows.append(window_repr);
|
windows.append(window_repr.repr_rewrite);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_label.set_markup(fmt::format(fmt::runtime(format), fmt::arg("id", id()),
|
auto formatBefore = m_workspaceManager.formatBefore();
|
||||||
fmt::arg("name", name()), fmt::arg("icon", icon),
|
m_labelBefore.set_markup(fmt::format(fmt::runtime(formatBefore), fmt::arg("id", id()),
|
||||||
|
fmt::arg("name", name()), fmt::arg("icon", workspace_icon),
|
||||||
fmt::arg("windows", windows)));
|
fmt::arg("windows", windows)));
|
||||||
|
m_labelBefore.get_style_context()->add_class("workspace-label");
|
||||||
|
|
||||||
|
if (m_workspaceManager.enableTaskbar()) {
|
||||||
|
updateTaskbar(workspace_icon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Workspace::updateTaskbar(const std::string &workspace_icon) {
|
||||||
|
for (auto child : m_content.get_children()) {
|
||||||
|
if (child != &m_labelBefore) {
|
||||||
|
m_content.remove(*child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isFirst = true;
|
||||||
|
for (const auto &window_repr : m_windowMap) {
|
||||||
|
if (shouldSkipWindow(window_repr)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (isFirst) {
|
||||||
|
isFirst = false;
|
||||||
|
} else if (m_workspaceManager.getWindowSeparator() != "") {
|
||||||
|
auto windowSeparator = Gtk::make_managed<Gtk::Label>(m_workspaceManager.getWindowSeparator());
|
||||||
|
m_content.pack_start(*windowSeparator, false, false);
|
||||||
|
windowSeparator->show();
|
||||||
|
}
|
||||||
|
auto window_box = Gtk::make_managed<Gtk::Box>(Gtk::ORIENTATION_HORIZONTAL);
|
||||||
|
window_box->set_tooltip_text(window_repr.window_title);
|
||||||
|
window_box->get_style_context()->add_class("taskbar-window");
|
||||||
|
if (window_repr.isActive) {
|
||||||
|
window_box->get_style_context()->add_class("active");
|
||||||
|
}
|
||||||
|
auto event_box = Gtk::manage(new Gtk::EventBox());
|
||||||
|
event_box->add(*window_box);
|
||||||
|
if (m_workspaceManager.onClickWindow() != "") {
|
||||||
|
event_box->signal_button_press_event().connect(
|
||||||
|
sigc::bind(sigc::mem_fun(*this, &Workspace::handleClick), window_repr.address));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto text_before = fmt::format(fmt::runtime(m_workspaceManager.taskbarFormatBefore()),
|
||||||
|
fmt::arg("title", window_repr.window_title));
|
||||||
|
if (!text_before.empty()) {
|
||||||
|
auto window_label_before = Gtk::make_managed<Gtk::Label>(text_before);
|
||||||
|
window_box->pack_start(*window_label_before, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_workspaceManager.taskbarWithIcon()) {
|
||||||
|
auto app_info_ = IconLoader::get_app_info_from_app_id_list(window_repr.window_class);
|
||||||
|
int icon_size = m_workspaceManager.taskbarIconSize();
|
||||||
|
auto window_icon = Gtk::make_managed<Gtk::Image>();
|
||||||
|
m_workspaceManager.iconLoader().image_load_icon(*window_icon, app_info_, icon_size);
|
||||||
|
window_box->pack_start(*window_icon, false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto text_after = fmt::format(fmt::runtime(m_workspaceManager.taskbarFormatAfter()),
|
||||||
|
fmt::arg("title", window_repr.window_title));
|
||||||
|
if (!text_after.empty()) {
|
||||||
|
auto window_label_after = Gtk::make_managed<Gtk::Label>(text_after);
|
||||||
|
window_box->pack_start(*window_label_after, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_content.pack_start(*event_box, true, false);
|
||||||
|
event_box->show_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto formatAfter = m_workspaceManager.formatAfter();
|
||||||
|
if (!formatAfter.empty()) {
|
||||||
|
m_labelAfter.set_markup(fmt::format(fmt::runtime(formatAfter), fmt::arg("id", id()),
|
||||||
|
fmt::arg("name", name()),
|
||||||
|
fmt::arg("icon", workspace_icon)));
|
||||||
|
m_content.pack_end(m_labelAfter, false, false);
|
||||||
|
m_labelAfter.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Workspace::handleClick(const GdkEventButton *event_button, WindowAddress const &addr) const {
|
||||||
|
if (event_button->type == GDK_BUTTON_PRESS) {
|
||||||
|
std::string command = std::regex_replace(m_workspaceManager.onClickWindow(),
|
||||||
|
std::regex("\\{address\\}"), "0x" + addr);
|
||||||
|
command = std::regex_replace(command, std::regex("\\{button\\}"),
|
||||||
|
std::to_string(event_button->button));
|
||||||
|
auto res = util::command::execNoRead(command);
|
||||||
|
if (res.exit_code != 0) {
|
||||||
|
spdlog::error("Failed to execute {}: {}", command, res.out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Workspace::shouldSkipWindow(const WindowRepr &window_repr) const {
|
||||||
|
auto ignore_list = m_workspaceManager.getIgnoredWindows();
|
||||||
|
auto it = std::ranges::find_if(ignore_list, [&window_repr](const auto &ignoreItem) {
|
||||||
|
return std::regex_match(window_repr.window_class, ignoreItem) ||
|
||||||
|
std::regex_match(window_repr.window_title, ignoreItem);
|
||||||
|
});
|
||||||
|
return it != ignore_list.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace waybar::modules::hyprland
|
} // namespace waybar::modules::hyprland
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "util/regex_collection.hpp"
|
#include "util/regex_collection.hpp"
|
||||||
|
#include "util/string.hpp"
|
||||||
|
|
||||||
namespace waybar::modules::hyprland {
|
namespace waybar::modules::hyprland {
|
||||||
|
|
||||||
@@ -65,11 +66,14 @@ Json::Value Workspaces::createMonitorWorkspaceData(std::string const &name,
|
|||||||
void Workspaces::createWorkspace(Json::Value const &workspace_data,
|
void Workspaces::createWorkspace(Json::Value const &workspace_data,
|
||||||
Json::Value const &clients_data) {
|
Json::Value const &clients_data) {
|
||||||
auto workspaceName = workspace_data["name"].asString();
|
auto workspaceName = workspace_data["name"].asString();
|
||||||
|
auto workspaceId = workspace_data["id"].asInt();
|
||||||
spdlog::debug("Creating workspace {}", workspaceName);
|
spdlog::debug("Creating workspace {}", workspaceName);
|
||||||
|
|
||||||
// avoid recreating existing workspaces
|
// avoid recreating existing workspaces
|
||||||
auto workspace =
|
auto workspace = std::ranges::find_if(m_workspaces, [&](std::unique_ptr<Workspace> const &w) {
|
||||||
std::ranges::find_if(m_workspaces, [workspaceName](std::unique_ptr<Workspace> const &w) {
|
if (workspaceId > 0) {
|
||||||
|
return w->id() == workspaceId;
|
||||||
|
}
|
||||||
return (workspaceName.starts_with("special:") && workspaceName.substr(8) == w->name()) ||
|
return (workspaceName.starts_with("special:") && workspaceName.substr(8) == w->name()) ||
|
||||||
workspaceName == w->name();
|
workspaceName == w->name();
|
||||||
});
|
});
|
||||||
@@ -253,10 +257,8 @@ void Workspaces::loadPersistentWorkspacesFromConfig(Json::Value const &clientsJs
|
|||||||
// value is an array => create defined workspaces for this monitor
|
// value is an array => create defined workspaces for this monitor
|
||||||
if (canCreate) {
|
if (canCreate) {
|
||||||
for (const Json::Value &workspace : value) {
|
for (const Json::Value &workspace : value) {
|
||||||
if (workspace.isInt()) {
|
|
||||||
spdlog::debug("Creating workspace {} on monitor {}", workspace, currentMonitor);
|
spdlog::debug("Creating workspace {} on monitor {}", workspace, currentMonitor);
|
||||||
persistentWorkspacesToCreate.emplace_back(std::to_string(workspace.asInt()));
|
persistentWorkspacesToCreate.emplace_back(workspace.asString());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// key is the workspace and value is array of monitors to create on
|
// key is the workspace and value is array of monitors to create on
|
||||||
@@ -292,8 +294,13 @@ void Workspaces::loadPersistentWorkspacesFromWorkspaceRules(const Json::Value &c
|
|||||||
if (!rule["persistent"].asBool()) {
|
if (!rule["persistent"].asBool()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
auto const &workspace = rule.isMember("defaultName") ? rule["defaultName"].asString()
|
auto workspace = rule.isMember("defaultName") ? rule["defaultName"].asString()
|
||||||
: rule["workspaceString"].asString();
|
: rule["workspaceString"].asString();
|
||||||
|
|
||||||
|
// The prefix "name:" cause mismatches with workspace names taken anywhere else.
|
||||||
|
if (workspace.starts_with("name:")) {
|
||||||
|
workspace = workspace.substr(5);
|
||||||
|
}
|
||||||
auto const &monitor = rule["monitor"].asString();
|
auto const &monitor = rule["monitor"].asString();
|
||||||
// create this workspace persistently if:
|
// create this workspace persistently if:
|
||||||
// 1. the allOutputs config option is enabled
|
// 1. the allOutputs config option is enabled
|
||||||
@@ -340,6 +347,8 @@ void Workspaces::onEvent(const std::string &ev) {
|
|||||||
onWorkspaceRenamed(payload);
|
onWorkspaceRenamed(payload);
|
||||||
} else if (eventName == "windowtitlev2") {
|
} else if (eventName == "windowtitlev2") {
|
||||||
onWindowTitleEvent(payload);
|
onWindowTitleEvent(payload);
|
||||||
|
} else if (eventName == "activewindowv2") {
|
||||||
|
onActiveWindowChanged(payload);
|
||||||
} else if (eventName == "configreloaded") {
|
} else if (eventName == "configreloaded") {
|
||||||
onConfigReloaded();
|
onConfigReloaded();
|
||||||
}
|
}
|
||||||
@@ -489,12 +498,14 @@ void Workspaces::onWindowOpened(std::string const &payload) {
|
|||||||
|
|
||||||
std::string windowTitle = payload.substr(nextCommaIdx + 1, payload.length() - nextCommaIdx);
|
std::string windowTitle = payload.substr(nextCommaIdx + 1, payload.length() - nextCommaIdx);
|
||||||
|
|
||||||
m_windowsToCreate.emplace_back(workspaceName, windowAddress, windowClass, windowTitle);
|
bool isActive = m_currentActiveWindowAddress == windowAddress;
|
||||||
|
m_windowsToCreate.emplace_back(workspaceName, windowAddress, windowClass, windowTitle, isActive);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Workspaces::onWindowClosed(std::string const &addr) {
|
void Workspaces::onWindowClosed(std::string const &addr) {
|
||||||
spdlog::trace("Window closed: {}", addr);
|
spdlog::trace("Window closed: {}", addr);
|
||||||
updateWindowCount();
|
updateWindowCount();
|
||||||
|
m_orphanWindowMap.erase(addr);
|
||||||
for (auto &workspace : m_workspaces) {
|
for (auto &workspace : m_workspaces) {
|
||||||
if (workspace->closeWindow(addr)) {
|
if (workspace->closeWindow(addr)) {
|
||||||
break;
|
break;
|
||||||
@@ -507,13 +518,13 @@ void Workspaces::onWindowMoved(std::string const &payload) {
|
|||||||
updateWindowCount();
|
updateWindowCount();
|
||||||
auto [windowAddress, _, workspaceName] = splitTriplePayload(payload);
|
auto [windowAddress, _, workspaceName] = splitTriplePayload(payload);
|
||||||
|
|
||||||
std::string windowRepr;
|
WindowRepr windowRepr;
|
||||||
|
|
||||||
// If the window was still queued to be created, just change its destination
|
// If the window was still queued to be created, just change its destination
|
||||||
// and exit
|
// and exit
|
||||||
for (auto &window : m_windowsToCreate) {
|
for (auto &window : m_windowsToCreate) {
|
||||||
if (window.getAddress() == windowAddress) {
|
if (window.getAddress() == windowAddress) {
|
||||||
window.moveToWorksace(workspaceName);
|
window.moveToWorkspace(workspaceName);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -533,6 +544,7 @@ void Workspaces::onWindowMoved(std::string const &payload) {
|
|||||||
|
|
||||||
// ...and then add it to the new workspace
|
// ...and then add it to the new workspace
|
||||||
if (!windowRepr.empty()) {
|
if (!windowRepr.empty()) {
|
||||||
|
m_orphanWindowMap.erase(windowAddress);
|
||||||
m_windowsToCreate.emplace_back(workspaceName, windowAddress, windowRepr);
|
m_windowsToCreate.emplace_back(workspaceName, windowAddress, windowRepr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -558,8 +570,9 @@ void Workspaces::onWindowTitleEvent(std::string const &payload) {
|
|||||||
(*windowWorkspace)->insertWindow(std::move(wcp));
|
(*windowWorkspace)->insertWindow(std::move(wcp));
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
auto queuedWindow = std::ranges::find_if(m_windowsToCreate, [payload](auto &windowPayload) {
|
auto queuedWindow =
|
||||||
return windowPayload.getAddress() == payload;
|
std::ranges::find_if(m_windowsToCreate, [&windowAddress](auto &windowPayload) {
|
||||||
|
return windowPayload.getAddress() == windowAddress;
|
||||||
});
|
});
|
||||||
|
|
||||||
// If the window was queued, rename it in the queue
|
// If the window was queued, rename it in the queue
|
||||||
@@ -571,7 +584,7 @@ void Workspaces::onWindowTitleEvent(std::string const &payload) {
|
|||||||
|
|
||||||
if (inserter.has_value()) {
|
if (inserter.has_value()) {
|
||||||
Json::Value clientsData = m_ipc.getSocket1JsonReply("clients");
|
Json::Value clientsData = m_ipc.getSocket1JsonReply("clients");
|
||||||
std::string jsonWindowAddress = fmt::format("0x{}", payload);
|
std::string jsonWindowAddress = fmt::format("0x{}", windowAddress);
|
||||||
|
|
||||||
auto client = std::ranges::find_if(clientsData, [jsonWindowAddress](auto &client) {
|
auto client = std::ranges::find_if(clientsData, [jsonWindowAddress](auto &client) {
|
||||||
return client["address"].asString() == jsonWindowAddress;
|
return client["address"].asString() == jsonWindowAddress;
|
||||||
@@ -583,6 +596,21 @@ void Workspaces::onWindowTitleEvent(std::string const &payload) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Workspaces::onActiveWindowChanged(WindowAddress const &activeWindowAddress) {
|
||||||
|
spdlog::trace("Active window changed: {}", activeWindowAddress);
|
||||||
|
m_currentActiveWindowAddress = activeWindowAddress;
|
||||||
|
|
||||||
|
for (auto &[address, window] : m_orphanWindowMap) {
|
||||||
|
window.setActive(address == activeWindowAddress);
|
||||||
|
}
|
||||||
|
for (auto const &workspace : m_workspaces) {
|
||||||
|
workspace->setActiveWindow(activeWindowAddress);
|
||||||
|
}
|
||||||
|
for (auto &window : m_windowsToCreate) {
|
||||||
|
window.setActive(window.getAddress() == activeWindowAddress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Workspaces::onConfigReloaded() {
|
void Workspaces::onConfigReloaded() {
|
||||||
spdlog::info("Hyprland config reloaded, reinitializing hyprland/workspaces module...");
|
spdlog::info("Hyprland config reloaded, reinitializing hyprland/workspaces module...");
|
||||||
init();
|
init();
|
||||||
@@ -590,8 +618,9 @@ void Workspaces::onConfigReloaded() {
|
|||||||
|
|
||||||
auto Workspaces::parseConfig(const Json::Value &config) -> void {
|
auto Workspaces::parseConfig(const Json::Value &config) -> void {
|
||||||
const auto &configFormat = config["format"];
|
const auto &configFormat = config["format"];
|
||||||
m_format = configFormat.isString() ? configFormat.asString() : "{name}";
|
m_formatBefore = configFormat.isString() ? configFormat.asString() : "{name}";
|
||||||
m_withIcon = m_format.find("{icon}") != std::string::npos;
|
m_withIcon = m_formatBefore.find("{icon}") != std::string::npos;
|
||||||
|
auto withWindows = m_formatBefore.find("{windows}") != std::string::npos;
|
||||||
|
|
||||||
if (m_withIcon && m_iconsMap.empty()) {
|
if (m_withIcon && m_iconsMap.empty()) {
|
||||||
populateIconsMap(config["format-icons"]);
|
populateIconsMap(config["format-icons"]);
|
||||||
@@ -600,6 +629,7 @@ auto Workspaces::parseConfig(const Json::Value &config) -> void {
|
|||||||
populateBoolConfig(config, "all-outputs", m_allOutputs);
|
populateBoolConfig(config, "all-outputs", m_allOutputs);
|
||||||
populateBoolConfig(config, "show-special", m_showSpecial);
|
populateBoolConfig(config, "show-special", m_showSpecial);
|
||||||
populateBoolConfig(config, "special-visible-only", m_specialVisibleOnly);
|
populateBoolConfig(config, "special-visible-only", m_specialVisibleOnly);
|
||||||
|
populateBoolConfig(config, "persistent-only", m_persistentOnly);
|
||||||
populateBoolConfig(config, "active-only", m_activeOnly);
|
populateBoolConfig(config, "active-only", m_activeOnly);
|
||||||
populateBoolConfig(config, "move-to-monitor", m_moveToMonitor);
|
populateBoolConfig(config, "move-to-monitor", m_moveToMonitor);
|
||||||
|
|
||||||
@@ -608,6 +638,15 @@ auto Workspaces::parseConfig(const Json::Value &config) -> void {
|
|||||||
populateIgnoreWorkspacesConfig(config);
|
populateIgnoreWorkspacesConfig(config);
|
||||||
populateFormatWindowSeparatorConfig(config);
|
populateFormatWindowSeparatorConfig(config);
|
||||||
populateWindowRewriteConfig(config);
|
populateWindowRewriteConfig(config);
|
||||||
|
|
||||||
|
if (withWindows) {
|
||||||
|
populateWorkspaceTaskbarConfig(config);
|
||||||
|
}
|
||||||
|
if (m_enableTaskbar) {
|
||||||
|
auto parts = split(m_formatBefore, "{windows}", 1);
|
||||||
|
m_formatBefore = parts[0];
|
||||||
|
m_formatAfter = parts.size() > 1 ? parts[1] : "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Workspaces::populateIconsMap(const Json::Value &formatIcons) -> void {
|
auto Workspaces::populateIconsMap(const Json::Value &formatIcons) -> void {
|
||||||
@@ -680,6 +719,64 @@ auto Workspaces::populateWindowRewriteConfig(const Json::Value &config) -> void
|
|||||||
[this](std::string &window_rule) { return windowRewritePriorityFunction(window_rule); });
|
[this](std::string &window_rule) { return windowRewritePriorityFunction(window_rule); });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto Workspaces::populateWorkspaceTaskbarConfig(const Json::Value &config) -> void {
|
||||||
|
const auto &workspaceTaskbar = config["workspace-taskbar"];
|
||||||
|
if (!workspaceTaskbar.isObject()) {
|
||||||
|
spdlog::debug("workspace-taskbar is not defined or is not an object, using default rules.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
populateBoolConfig(workspaceTaskbar, "enable", m_enableTaskbar);
|
||||||
|
populateBoolConfig(workspaceTaskbar, "update-active-window", m_updateActiveWindow);
|
||||||
|
|
||||||
|
if (workspaceTaskbar["format"].isString()) {
|
||||||
|
/* The user defined a format string, use it */
|
||||||
|
std::string format = workspaceTaskbar["format"].asString();
|
||||||
|
m_taskbarWithTitle = format.find("{title") != std::string::npos; /* {title} or {title.length} */
|
||||||
|
auto parts = split(format, "{icon}", 1);
|
||||||
|
m_taskbarFormatBefore = parts[0];
|
||||||
|
if (parts.size() > 1) {
|
||||||
|
m_taskbarWithIcon = true;
|
||||||
|
m_taskbarFormatAfter = parts[1];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* The default is to only show the icon */
|
||||||
|
m_taskbarWithIcon = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto iconTheme = workspaceTaskbar["icon-theme"];
|
||||||
|
if (iconTheme.isArray()) {
|
||||||
|
for (auto &c : iconTheme) {
|
||||||
|
m_iconLoader.add_custom_icon_theme(c.asString());
|
||||||
|
}
|
||||||
|
} else if (iconTheme.isString()) {
|
||||||
|
m_iconLoader.add_custom_icon_theme(iconTheme.asString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (workspaceTaskbar["icon-size"].isInt()) {
|
||||||
|
m_taskbarIconSize = workspaceTaskbar["icon-size"].asInt();
|
||||||
|
}
|
||||||
|
if (workspaceTaskbar["orientation"].isString() &&
|
||||||
|
toLower(workspaceTaskbar["orientation"].asString()) == "vertical") {
|
||||||
|
m_taskbarOrientation = Gtk::ORIENTATION_VERTICAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (workspaceTaskbar["on-click-window"].isString()) {
|
||||||
|
m_onClickWindow = workspaceTaskbar["on-click-window"].asString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (workspaceTaskbar["ignore-list"].isArray()) {
|
||||||
|
for (auto &windowRegex : workspaceTaskbar["ignore-list"]) {
|
||||||
|
std::string ruleString = windowRegex.asString();
|
||||||
|
try {
|
||||||
|
m_ignoreWindows.emplace_back(ruleString, std::regex_constants::icase);
|
||||||
|
} catch (const std::regex_error &e) {
|
||||||
|
spdlog::error("Invalid rule {}: {}", ruleString, e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Workspaces::registerOrphanWindow(WindowCreationPayload create_window_payload) {
|
void Workspaces::registerOrphanWindow(WindowCreationPayload create_window_payload) {
|
||||||
if (!create_window_payload.isEmpty(*this)) {
|
if (!create_window_payload.isEmpty(*this)) {
|
||||||
m_orphanWindowMap[create_window_payload.getAddress()] = create_window_payload.repr(*this);
|
m_orphanWindowMap[create_window_payload.getAddress()] = create_window_payload.repr(*this);
|
||||||
@@ -700,12 +797,18 @@ auto Workspaces::registerIpc() -> void {
|
|||||||
m_ipc.registerForIPC("urgent", this);
|
m_ipc.registerForIPC("urgent", this);
|
||||||
m_ipc.registerForIPC("configreloaded", this);
|
m_ipc.registerForIPC("configreloaded", this);
|
||||||
|
|
||||||
if (windowRewriteConfigUsesTitle()) {
|
if (windowRewriteConfigUsesTitle() || m_taskbarWithTitle) {
|
||||||
spdlog::info(
|
spdlog::info(
|
||||||
"Registering for Hyprland's 'windowtitlev2' events because a user-defined window "
|
"Registering for Hyprland's 'windowtitlev2' events because a user-defined window "
|
||||||
"rewrite rule uses the 'title' field.");
|
"rewrite rule uses the 'title' field.");
|
||||||
m_ipc.registerForIPC("windowtitlev2", this);
|
m_ipc.registerForIPC("windowtitlev2", this);
|
||||||
}
|
}
|
||||||
|
if (m_updateActiveWindow) {
|
||||||
|
spdlog::info(
|
||||||
|
"Registering for Hyprland's 'activewindowv2' events because 'update-active-window' is set "
|
||||||
|
"to true.");
|
||||||
|
m_ipc.registerForIPC("activewindowv2", this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Workspaces::removeWorkspacesToRemove() {
|
void Workspaces::removeWorkspacesToRemove() {
|
||||||
@@ -770,6 +873,40 @@ void Workspaces::setCurrentMonitorId() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Workspaces::sortSpecialCentered() {
|
||||||
|
std::vector<std::unique_ptr<Workspace>> specialWorkspaces;
|
||||||
|
std::vector<std::unique_ptr<Workspace>> hiddenWorkspaces;
|
||||||
|
std::vector<std::unique_ptr<Workspace>> normalWorkspaces;
|
||||||
|
|
||||||
|
for (auto &workspace : m_workspaces) {
|
||||||
|
if (workspace->isSpecial()) {
|
||||||
|
specialWorkspaces.push_back(std::move(workspace));
|
||||||
|
} else {
|
||||||
|
if (workspace->button().is_visible()) {
|
||||||
|
normalWorkspaces.push_back(std::move(workspace));
|
||||||
|
} else {
|
||||||
|
hiddenWorkspaces.push_back(std::move(workspace));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_workspaces.clear();
|
||||||
|
|
||||||
|
size_t center = normalWorkspaces.size() / 2;
|
||||||
|
|
||||||
|
m_workspaces.insert(m_workspaces.end(), std::make_move_iterator(normalWorkspaces.begin()),
|
||||||
|
std::make_move_iterator(normalWorkspaces.begin() + center));
|
||||||
|
|
||||||
|
m_workspaces.insert(m_workspaces.end(), std::make_move_iterator(specialWorkspaces.begin()),
|
||||||
|
std::make_move_iterator(specialWorkspaces.end()));
|
||||||
|
|
||||||
|
m_workspaces.insert(m_workspaces.end(),
|
||||||
|
std::make_move_iterator(normalWorkspaces.begin() + center),
|
||||||
|
std::make_move_iterator(normalWorkspaces.end()));
|
||||||
|
|
||||||
|
m_workspaces.insert(m_workspaces.end(), std::make_move_iterator(hiddenWorkspaces.begin()),
|
||||||
|
std::make_move_iterator(hiddenWorkspaces.end()));
|
||||||
|
}
|
||||||
|
|
||||||
void Workspaces::sortWorkspaces() {
|
void Workspaces::sortWorkspaces() {
|
||||||
std::ranges::sort( //
|
std::ranges::sort( //
|
||||||
m_workspaces, [&](std::unique_ptr<Workspace> &a, std::unique_ptr<Workspace> &b) {
|
m_workspaces, [&](std::unique_ptr<Workspace> &a, std::unique_ptr<Workspace> &b) {
|
||||||
@@ -828,6 +965,9 @@ void Workspaces::sortWorkspaces() {
|
|||||||
// Return a default value if none of the cases match.
|
// Return a default value if none of the cases match.
|
||||||
return isNameLess; // You can adjust this to your specific needs.
|
return isNameLess; // You can adjust this to your specific needs.
|
||||||
});
|
});
|
||||||
|
if (m_sortBy == SortMethod::SPECIAL_CENTERED) {
|
||||||
|
this->sortSpecialCentered();
|
||||||
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < m_workspaces.size(); ++i) {
|
for (size_t i = 0; i < m_workspaces.size(); ++i) {
|
||||||
m_box.reorder_child(m_workspaces[i]->button(), i);
|
m_box.reorder_child(m_workspaces[i]->button(), i);
|
||||||
@@ -860,7 +1000,7 @@ auto Workspaces::update() -> void {
|
|||||||
|
|
||||||
void Workspaces::updateWindowCount() {
|
void Workspaces::updateWindowCount() {
|
||||||
const Json::Value workspacesJson = m_ipc.getSocket1JsonReply("workspaces");
|
const Json::Value workspacesJson = m_ipc.getSocket1JsonReply("workspaces");
|
||||||
for (auto &workspace : m_workspaces) {
|
for (auto const &workspace : m_workspaces) {
|
||||||
auto workspaceJson = std::ranges::find_if(workspacesJson, [&](Json::Value const &x) {
|
auto workspaceJson = std::ranges::find_if(workspacesJson, [&](Json::Value const &x) {
|
||||||
return x["name"].asString() == workspace->name() ||
|
return x["name"].asString() == workspace->name() ||
|
||||||
(workspace->isSpecial() && x["name"].asString() == "special:" + workspace->name());
|
(workspace->isSpecial() && x["name"].asString() == "special:" + workspace->name());
|
||||||
@@ -906,9 +1046,17 @@ bool Workspaces::updateWindowsToCreate() {
|
|||||||
void Workspaces::updateWorkspaceStates() {
|
void Workspaces::updateWorkspaceStates() {
|
||||||
const std::vector<int> visibleWorkspaces = getVisibleWorkspaces();
|
const std::vector<int> visibleWorkspaces = getVisibleWorkspaces();
|
||||||
auto updatedWorkspaces = m_ipc.getSocket1JsonReply("workspaces");
|
auto updatedWorkspaces = m_ipc.getSocket1JsonReply("workspaces");
|
||||||
|
|
||||||
|
auto currentWorkspace = m_ipc.getSocket1JsonReply("activeworkspace");
|
||||||
|
std::string currentWorkspaceName =
|
||||||
|
currentWorkspace.isMember("name") ? currentWorkspace["name"].asString() : "";
|
||||||
|
|
||||||
for (auto &workspace : m_workspaces) {
|
for (auto &workspace : m_workspaces) {
|
||||||
|
bool isActiveByName =
|
||||||
|
!currentWorkspaceName.empty() && workspace->name() == currentWorkspaceName;
|
||||||
|
|
||||||
workspace->setActive(
|
workspace->setActive(
|
||||||
workspace->id() == m_activeWorkspaceId ||
|
workspace->id() == m_activeWorkspaceId || isActiveByName ||
|
||||||
(workspace->isSpecial() && workspace->name() == m_activeSpecialWorkspaceName));
|
(workspace->isSpecial() && workspace->name() == m_activeSpecialWorkspaceName));
|
||||||
if (workspace->isActive() && workspace->isUrgent()) {
|
if (workspace->isActive() && workspace->isUrgent()) {
|
||||||
workspace->setUrgent(false);
|
workspace->setUrgent(false);
|
||||||
@@ -927,7 +1075,7 @@ void Workspaces::updateWorkspaceStates() {
|
|||||||
if (updatedWorkspace != updatedWorkspaces.end()) {
|
if (updatedWorkspace != updatedWorkspaces.end()) {
|
||||||
workspace->setOutput((*updatedWorkspace)["monitor"].asString());
|
workspace->setOutput((*updatedWorkspace)["monitor"].asString());
|
||||||
}
|
}
|
||||||
workspace->update(m_format, workspaceIcon);
|
workspace->update(workspaceIcon);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ auto waybar::modules::Memory::update() -> void {
|
|||||||
fmt::arg("icon", getIcon(used_ram_percentage, icons)),
|
fmt::arg("icon", getIcon(used_ram_percentage, icons)),
|
||||||
fmt::arg("total", total_ram_gigabytes), fmt::arg("swapTotal", total_swap_gigabytes),
|
fmt::arg("total", total_ram_gigabytes), fmt::arg("swapTotal", total_swap_gigabytes),
|
||||||
fmt::arg("percentage", used_ram_percentage),
|
fmt::arg("percentage", used_ram_percentage),
|
||||||
|
fmt::arg("swapState", swaptotal == 0 ? "Off" : "On"),
|
||||||
fmt::arg("swapPercentage", used_swap_percentage), fmt::arg("used", used_ram_gigabytes),
|
fmt::arg("swapPercentage", used_swap_percentage), fmt::arg("used", used_ram_gigabytes),
|
||||||
fmt::arg("swapUsed", used_swap_gigabytes), fmt::arg("avail", available_ram_gigabytes),
|
fmt::arg("swapUsed", used_swap_gigabytes), fmt::arg("avail", available_ram_gigabytes),
|
||||||
fmt::arg("swapAvail", available_swap_gigabytes)));
|
fmt::arg("swapAvail", available_swap_gigabytes)));
|
||||||
@@ -72,6 +73,7 @@ auto waybar::modules::Memory::update() -> void {
|
|||||||
fmt::runtime(tooltip_format), used_ram_percentage,
|
fmt::runtime(tooltip_format), used_ram_percentage,
|
||||||
fmt::arg("total", total_ram_gigabytes), fmt::arg("swapTotal", total_swap_gigabytes),
|
fmt::arg("total", total_ram_gigabytes), fmt::arg("swapTotal", total_swap_gigabytes),
|
||||||
fmt::arg("percentage", used_ram_percentage),
|
fmt::arg("percentage", used_ram_percentage),
|
||||||
|
fmt::arg("swapState", swaptotal == 0 ? "Off" : "On"),
|
||||||
fmt::arg("swapPercentage", used_swap_percentage), fmt::arg("used", used_ram_gigabytes),
|
fmt::arg("swapPercentage", used_swap_percentage), fmt::arg("used", used_ram_gigabytes),
|
||||||
fmt::arg("swapUsed", used_swap_gigabytes), fmt::arg("avail", available_ram_gigabytes),
|
fmt::arg("swapUsed", used_swap_gigabytes), fmt::arg("avail", available_ram_gigabytes),
|
||||||
fmt::arg("swapAvail", available_swap_gigabytes)));
|
fmt::arg("swapAvail", available_swap_gigabytes)));
|
||||||
|
|||||||
+11
-10
@@ -118,7 +118,7 @@ void waybar::modules::MPD::setLabel() {
|
|||||||
|
|
||||||
auto format = format_;
|
auto format = format_;
|
||||||
Glib::ustring artist, album_artist, album, title;
|
Glib::ustring artist, album_artist, album, title;
|
||||||
std::string date, filename;
|
std::string date, filename, uri;
|
||||||
int song_pos = 0, queue_length = 0, volume = 0;
|
int song_pos = 0, queue_length = 0, volume = 0;
|
||||||
std::chrono::seconds elapsedTime, totalTime;
|
std::chrono::seconds elapsedTime, totalTime;
|
||||||
|
|
||||||
@@ -151,6 +151,7 @@ void waybar::modules::MPD::setLabel() {
|
|||||||
title = sanitize_string(getTag(MPD_TAG_TITLE));
|
title = sanitize_string(getTag(MPD_TAG_TITLE));
|
||||||
date = sanitize_string(getTag(MPD_TAG_DATE));
|
date = sanitize_string(getTag(MPD_TAG_DATE));
|
||||||
filename = sanitize_string(getFilename());
|
filename = sanitize_string(getFilename());
|
||||||
|
uri = mpd_song_get_uri(song_.get());
|
||||||
song_pos = mpd_status_get_song_pos(status_.get()) + 1;
|
song_pos = mpd_status_get_song_pos(status_.get()) + 1;
|
||||||
volume = mpd_status_get_volume(status_.get());
|
volume = mpd_status_get_volume(status_.get());
|
||||||
if (volume < 0) {
|
if (volume < 0) {
|
||||||
@@ -184,7 +185,7 @@ void waybar::modules::MPD::setLabel() {
|
|||||||
fmt::arg("songPosition", song_pos), fmt::arg("queueLength", queue_length),
|
fmt::arg("songPosition", song_pos), fmt::arg("queueLength", queue_length),
|
||||||
fmt::arg("stateIcon", stateIcon), fmt::arg("consumeIcon", consumeIcon),
|
fmt::arg("stateIcon", stateIcon), fmt::arg("consumeIcon", consumeIcon),
|
||||||
fmt::arg("randomIcon", randomIcon), fmt::arg("repeatIcon", repeatIcon),
|
fmt::arg("randomIcon", randomIcon), fmt::arg("repeatIcon", repeatIcon),
|
||||||
fmt::arg("singleIcon", singleIcon), fmt::arg("filename", filename));
|
fmt::arg("singleIcon", singleIcon), fmt::arg("filename", filename), fmt::arg("uri", uri));
|
||||||
if (text.empty()) {
|
if (text.empty()) {
|
||||||
label_.hide();
|
label_.hide();
|
||||||
} else {
|
} else {
|
||||||
@@ -200,15 +201,15 @@ void waybar::modules::MPD::setLabel() {
|
|||||||
tooltip_format = config_["tooltip-format"].isString() ? config_["tooltip-format"].asString()
|
tooltip_format = config_["tooltip-format"].isString() ? config_["tooltip-format"].asString()
|
||||||
: "MPD (connected)";
|
: "MPD (connected)";
|
||||||
try {
|
try {
|
||||||
auto tooltip_text =
|
auto tooltip_text = fmt::format(
|
||||||
fmt::format(fmt::runtime(tooltip_format), fmt::arg("artist", artist.raw()),
|
fmt::runtime(tooltip_format), fmt::arg("artist", artist.raw()),
|
||||||
fmt::arg("albumArtist", album_artist.raw()), fmt::arg("album", album.raw()),
|
fmt::arg("albumArtist", album_artist.raw()), fmt::arg("album", album.raw()),
|
||||||
fmt::arg("title", title.raw()), fmt::arg("date", date),
|
fmt::arg("title", title.raw()), fmt::arg("date", date), fmt::arg("volume", volume),
|
||||||
fmt::arg("volume", volume), fmt::arg("elapsedTime", elapsedTime),
|
fmt::arg("elapsedTime", elapsedTime), fmt::arg("totalTime", totalTime),
|
||||||
fmt::arg("totalTime", totalTime), fmt::arg("songPosition", song_pos),
|
fmt::arg("songPosition", song_pos), fmt::arg("queueLength", queue_length),
|
||||||
fmt::arg("queueLength", queue_length), fmt::arg("stateIcon", stateIcon),
|
fmt::arg("stateIcon", stateIcon), fmt::arg("consumeIcon", consumeIcon),
|
||||||
fmt::arg("consumeIcon", consumeIcon), fmt::arg("randomIcon", randomIcon),
|
fmt::arg("randomIcon", randomIcon), fmt::arg("repeatIcon", repeatIcon),
|
||||||
fmt::arg("repeatIcon", repeatIcon), fmt::arg("singleIcon", singleIcon));
|
fmt::arg("singleIcon", singleIcon), fmt::arg("filename", filename), fmt::arg("uri", uri));
|
||||||
label_.set_tooltip_text(tooltip_text);
|
label_.set_tooltip_text(tooltip_text);
|
||||||
} catch (fmt::format_error const& e) {
|
} catch (fmt::format_error const& e) {
|
||||||
spdlog::warn("mpd: format error (tooltip): {}", e.what());
|
spdlog::warn("mpd: format error (tooltip): {}", e.what());
|
||||||
|
|||||||
+35
-24
@@ -14,7 +14,6 @@ extern "C" {
|
|||||||
|
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
namespace waybar::modules::mpris {
|
namespace waybar::modules::mpris {
|
||||||
|
|
||||||
const std::string DEFAULT_FORMAT = "{player} ({status}): {dynamic}";
|
const std::string DEFAULT_FORMAT = "{player} ({status}): {dynamic}";
|
||||||
@@ -425,9 +424,11 @@ auto Mpris::onPlayerNameVanished(PlayerctlPlayerManager* manager, PlayerctlPlaye
|
|||||||
auto* mpris = static_cast<Mpris*>(data);
|
auto* mpris = static_cast<Mpris*>(data);
|
||||||
if (!mpris) return;
|
if (!mpris) return;
|
||||||
|
|
||||||
spdlog::debug("mpris: player-vanished callback: {}", player_name->name);
|
spdlog::debug("mpris: name-vanished callback: {}", player_name->name);
|
||||||
|
|
||||||
if (std::string(player_name->name) == mpris->player_) {
|
if (mpris->player_ == "playerctld") {
|
||||||
|
mpris->dp.emit();
|
||||||
|
} else if (mpris->player_ == player_name->name) {
|
||||||
mpris->player = nullptr;
|
mpris->player = nullptr;
|
||||||
mpris->event_box_.set_visible(false);
|
mpris->event_box_.set_visible(false);
|
||||||
mpris->dp.emit();
|
mpris->dp.emit();
|
||||||
@@ -498,7 +499,10 @@ auto Mpris::getPlayerInfo() -> std::optional<PlayerInfo> {
|
|||||||
// > get the list of players [..] in order of activity
|
// > get the list of players [..] in order of activity
|
||||||
// https://github.com/altdesktop/playerctl/blob/b19a71cb9dba635df68d271bd2b3f6a99336a223/playerctl/playerctl-common.c#L248-L249
|
// https://github.com/altdesktop/playerctl/blob/b19a71cb9dba635df68d271bd2b3f6a99336a223/playerctl/playerctl-common.c#L248-L249
|
||||||
players = g_list_first(players);
|
players = g_list_first(players);
|
||||||
if (players) player_name = static_cast<PlayerctlPlayerName*>(players->data)->name;
|
if (players)
|
||||||
|
player_name = static_cast<PlayerctlPlayerName*>(players->data)->name;
|
||||||
|
else
|
||||||
|
return std::nullopt; // no players found, hide the widget
|
||||||
}
|
}
|
||||||
|
|
||||||
if (std::any_of(ignored_players_.begin(), ignored_players_.end(),
|
if (std::any_of(ignored_players_.begin(), ignored_players_.end(),
|
||||||
@@ -584,38 +588,45 @@ errorexit:
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Mpris::handleToggle(GdkEventButton* const& e) {
|
bool Mpris::handleToggle(GdkEventButton* const& e) {
|
||||||
|
if (!e || e->type != GdkEventType::GDK_BUTTON_PRESS) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto info = getPlayerInfo();
|
||||||
|
if (!info) return false;
|
||||||
|
|
||||||
|
struct ButtonAction {
|
||||||
|
guint button;
|
||||||
|
const char* config_key;
|
||||||
|
std::function<void()> builtin_action;
|
||||||
|
};
|
||||||
|
|
||||||
GError* error = nullptr;
|
GError* error = nullptr;
|
||||||
waybar::util::ScopeGuard error_deleter([error]() {
|
waybar::util::ScopeGuard error_deleter([&error]() {
|
||||||
if (error) {
|
if (error) {
|
||||||
g_error_free(error);
|
g_error_free(error);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
auto info = getPlayerInfo();
|
// Command pattern: encapsulate each button's action
|
||||||
if (!info) return false;
|
const ButtonAction actions[] = {
|
||||||
|
{1, "on-click", [&]() { playerctl_player_play_pause(player, &error); }},
|
||||||
|
{2, "on-click-middle", [&]() { playerctl_player_previous(player, &error); }},
|
||||||
|
{3, "on-click-right", [&]() { playerctl_player_next(player, &error); }},
|
||||||
|
{8, "on-click-backward", [&]() { playerctl_player_previous(player, &error); }},
|
||||||
|
{9, "on-click-forward", [&]() { playerctl_player_next(player, &error); }},
|
||||||
|
};
|
||||||
|
|
||||||
if (e->type == GdkEventType::GDK_BUTTON_PRESS) {
|
for (const auto& action : actions) {
|
||||||
switch (e->button) {
|
if (e->button == action.button) {
|
||||||
case 1: // left-click
|
if (config_[action.config_key].isString()) {
|
||||||
if (config_["on-click"].isString()) {
|
|
||||||
return ALabel::handleToggle(e);
|
return ALabel::handleToggle(e);
|
||||||
}
|
}
|
||||||
playerctl_player_play_pause(player, &error);
|
action.builtin_action();
|
||||||
break;
|
|
||||||
case 2: // middle-click
|
|
||||||
if (config_["on-click-middle"].isString()) {
|
|
||||||
return ALabel::handleToggle(e);
|
|
||||||
}
|
|
||||||
playerctl_player_previous(player, &error);
|
|
||||||
break;
|
|
||||||
case 3: // right-click
|
|
||||||
if (config_["on-click-right"].isString()) {
|
|
||||||
return ALabel::handleToggle(e);
|
|
||||||
}
|
|
||||||
playerctl_player_next(player, &error);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
spdlog::error("mpris[{}]: error running builtin on-click action: {}", (*info).name,
|
spdlog::error("mpris[{}]: error running builtin on-click action: {}", (*info).name,
|
||||||
error->message);
|
error->message);
|
||||||
|
|||||||
+122
-94
@@ -1,13 +1,18 @@
|
|||||||
#include "modules/network.hpp"
|
#include "modules/network.hpp"
|
||||||
|
|
||||||
#include <linux/if.h>
|
#include <linux/if.h>
|
||||||
|
#include <linux/if_link.h>
|
||||||
|
#include <netlink/netlink.h>
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
#include <sys/eventfd.h>
|
#include <sys/eventfd.h>
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#include <cstring>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "util/format.hpp"
|
#include "util/format.hpp"
|
||||||
#ifdef WANT_RFKILL
|
#ifdef WANT_RFKILL
|
||||||
@@ -78,25 +83,7 @@ waybar::modules::Network::readBandwidthUsage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
waybar::modules::Network::Network(const std::string &id, const Json::Value &config)
|
waybar::modules::Network::Network(const std::string &id, const Json::Value &config)
|
||||||
: ALabel(config, "network", id, DEFAULT_FORMAT, 60),
|
: ALabel(config, "network", id, DEFAULT_FORMAT, 60) {
|
||||||
ifid_(-1),
|
|
||||||
addr_pref_(IPV4),
|
|
||||||
efd_(-1),
|
|
||||||
ev_fd_(-1),
|
|
||||||
want_route_dump_(false),
|
|
||||||
want_link_dump_(false),
|
|
||||||
want_addr_dump_(false),
|
|
||||||
dump_in_progress_(false),
|
|
||||||
is_p2p_(false),
|
|
||||||
cidr_(0),
|
|
||||||
cidr6_(0),
|
|
||||||
signal_strength_dbm_(0),
|
|
||||||
signal_strength_(0),
|
|
||||||
#ifdef WANT_RFKILL
|
|
||||||
rfkill_{RFKILL_TYPE_WLAN},
|
|
||||||
#endif
|
|
||||||
frequency_(0.0) {
|
|
||||||
|
|
||||||
// Start with some "text" in the module's label_. update() will then
|
// Start with some "text" in the module's label_. update() will then
|
||||||
// update it. Since the text should be different, update() will be able
|
// update it. Since the text should be different, update() will be able
|
||||||
// to show or hide the event_box_. This is to work around the case where
|
// to show or hide the event_box_. This is to work around the case where
|
||||||
@@ -223,8 +210,8 @@ void waybar::modules::Network::worker() {
|
|||||||
std::lock_guard<std::mutex> lock(mutex_);
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
if (ifid_ > 0) {
|
if (ifid_ > 0) {
|
||||||
getInfo();
|
getInfo();
|
||||||
dp.emit();
|
|
||||||
}
|
}
|
||||||
|
dp.emit();
|
||||||
}
|
}
|
||||||
thread_timer_.sleep_for(interval_);
|
thread_timer_.sleep_for(interval_);
|
||||||
};
|
};
|
||||||
@@ -271,13 +258,16 @@ void waybar::modules::Network::worker() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const std::string waybar::modules::Network::getNetworkState() const {
|
const std::string waybar::modules::Network::getNetworkState() const {
|
||||||
if (ifid_ == -1) {
|
if (ifid_ == -1 || !carrier_) {
|
||||||
#ifdef WANT_RFKILL
|
#ifdef WANT_RFKILL
|
||||||
if (rfkill_.getState()) return "disabled";
|
bool display_rfkill = true;
|
||||||
|
if (config_["rfkill"].isBool()) {
|
||||||
|
display_rfkill = config_["rfkill"].asBool();
|
||||||
|
}
|
||||||
|
if (rfkill_.getState() && display_rfkill) return "disabled";
|
||||||
#endif
|
#endif
|
||||||
return "disconnected";
|
return "disconnected";
|
||||||
}
|
}
|
||||||
if (!carrier_) return "disconnected";
|
|
||||||
if (ipaddr_.empty() && ipaddr6_.empty()) return "linked";
|
if (ipaddr_.empty() && ipaddr6_.empty()) return "linked";
|
||||||
if (essid_.empty()) return "ethernet";
|
if (essid_.empty()) return "ethernet";
|
||||||
return "wifi";
|
return "wifi";
|
||||||
@@ -401,11 +391,65 @@ auto waybar::modules::Network::update() -> void {
|
|||||||
ALabel::update();
|
ALabel::update();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool waybar::modules::Network::checkInterface(std::string name) {
|
// https://gist.github.com/rressi/92af77630faf055934c723ce93ae2495
|
||||||
if (config_["interface"].isString()) {
|
static bool wildcardMatch(const std::string &pattern, const std::string &text) {
|
||||||
return config_["interface"].asString() == name ||
|
auto P = int(pattern.size());
|
||||||
wildcardMatch(config_["interface"].asString(), name);
|
auto T = int(text.size());
|
||||||
|
|
||||||
|
auto p = 0, fallback_p = -1;
|
||||||
|
auto t = 0, fallback_t = -1;
|
||||||
|
|
||||||
|
while (t < T) {
|
||||||
|
// Wildcard match:
|
||||||
|
if (p < P && pattern[p] == '*') {
|
||||||
|
fallback_p = p++; // starting point after failures
|
||||||
|
fallback_t = t; // starting point after failures
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Simple match:
|
||||||
|
else if (p < P && (pattern[p] == '?' || pattern[p] == text[t])) {
|
||||||
|
p++;
|
||||||
|
t++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Failure, fall back just after last matched '*':
|
||||||
|
else if (fallback_p >= 0) {
|
||||||
|
p = fallback_p + 1; // position just after last matched '*"
|
||||||
|
t = ++fallback_t; // re-try to match text from here
|
||||||
|
}
|
||||||
|
|
||||||
|
// There were no '*' before, so we fail here:
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Consume all '*' at the end of pattern:
|
||||||
|
while (p < P && pattern[p] == '*') p++;
|
||||||
|
|
||||||
|
return p == P;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool waybar::modules::Network::matchInterface(const std::string &ifname,
|
||||||
|
const std::vector<std::string> &altnames,
|
||||||
|
std::string &matched) const {
|
||||||
|
if (!config_["interface"].isString()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto config_ifname = config_["interface"].asString();
|
||||||
|
if (config_ifname == ifname || wildcardMatch(config_ifname, ifname)) {
|
||||||
|
matched = ifname;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto &altname : altnames) {
|
||||||
|
if (config_ifname == altname || wildcardMatch(config_ifname, altname)) {
|
||||||
|
matched = altname;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -420,6 +464,7 @@ void waybar::modules::Network::clearIface() {
|
|||||||
netmask_.clear();
|
netmask_.clear();
|
||||||
netmask6_.clear();
|
netmask6_.clear();
|
||||||
carrier_ = false;
|
carrier_ = false;
|
||||||
|
is_p2p_ = false;
|
||||||
cidr_ = 0;
|
cidr_ = 0;
|
||||||
cidr6_ = 0;
|
cidr6_ = 0;
|
||||||
signal_strength_dbm_ = 0;
|
signal_strength_dbm_ = 0;
|
||||||
@@ -439,12 +484,16 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) {
|
|||||||
is_del_event = true;
|
is_del_event = true;
|
||||||
case RTM_NEWLINK: {
|
case RTM_NEWLINK: {
|
||||||
struct ifinfomsg *ifi = static_cast<struct ifinfomsg *>(NLMSG_DATA(nh));
|
struct ifinfomsg *ifi = static_cast<struct ifinfomsg *>(NLMSG_DATA(nh));
|
||||||
ssize_t attrlen = IFLA_PAYLOAD(nh);
|
struct nlattr *attrs[IFLA_MAX + 1];
|
||||||
struct rtattr *ifla = IFLA_RTA(ifi);
|
std::string ifname;
|
||||||
const char *ifname = NULL;
|
std::vector<std::string> altnames;
|
||||||
size_t ifname_len = 0;
|
|
||||||
std::optional<bool> carrier;
|
std::optional<bool> carrier;
|
||||||
|
|
||||||
|
if (nlmsg_parse(nh, sizeof(*ifi), attrs, IFLA_MAX, nullptr) < 0) {
|
||||||
|
spdlog::error("network: failed to parse netlink attributes");
|
||||||
|
return NL_SKIP;
|
||||||
|
}
|
||||||
|
|
||||||
if (net->ifid_ != -1 && ifi->ifi_index != net->ifid_) {
|
if (net->ifid_ != -1 && ifi->ifi_index != net->ifid_) {
|
||||||
return NL_OK;
|
return NL_OK;
|
||||||
}
|
}
|
||||||
@@ -462,26 +511,33 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) {
|
|||||||
return NL_OK;
|
return NL_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (; RTA_OK(ifla, attrlen); ifla = RTA_NEXT(ifla, attrlen)) {
|
if (attrs[IFLA_IFNAME] != nullptr) {
|
||||||
switch (ifla->rta_type) {
|
const char *ifname_ptr = nla_get_string(attrs[IFLA_IFNAME]);
|
||||||
case IFLA_IFNAME:
|
size_t ifname_len = nla_len(attrs[IFLA_IFNAME]) - 1; // minus \0
|
||||||
ifname = static_cast<const char *>(RTA_DATA(ifla));
|
ifname = std::string(ifname_ptr, ifname_len);
|
||||||
ifname_len = RTA_PAYLOAD(ifla) - 1; // minus \0
|
}
|
||||||
if (ifi->ifi_flags & IFF_POINTOPOINT && net->checkInterface(ifname))
|
|
||||||
net->is_p2p_ = true;
|
if (attrs[IFLA_CARRIER] != nullptr) {
|
||||||
break;
|
carrier = nla_get_u8(attrs[IFLA_CARRIER]) == 1;
|
||||||
case IFLA_CARRIER: {
|
}
|
||||||
carrier = *(char *)RTA_DATA(ifla) == 1;
|
|
||||||
break;
|
if (attrs[IFLA_PROP_LIST] != nullptr) {
|
||||||
|
struct nlattr *prop;
|
||||||
|
int rem;
|
||||||
|
|
||||||
|
nla_for_each_nested(prop, attrs[IFLA_PROP_LIST], rem) {
|
||||||
|
if (nla_type(prop) == IFLA_ALT_IFNAME) {
|
||||||
|
const char *altname_ptr = nla_get_string(prop);
|
||||||
|
size_t altname_len = nla_len(prop) - 1; // minus \0
|
||||||
|
altnames.emplace_back(altname_ptr, altname_len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!is_del_event && ifi->ifi_index == net->ifid_) {
|
if (!is_del_event && ifi->ifi_index == net->ifid_) {
|
||||||
// Update interface information
|
// Update interface information
|
||||||
if (net->ifname_.empty() && ifname != NULL) {
|
if (net->ifname_.empty() && !ifname.empty()) {
|
||||||
std::string new_ifname(ifname, ifname_len);
|
net->ifname_ = ifname;
|
||||||
net->ifname_ = new_ifname;
|
|
||||||
}
|
}
|
||||||
if (carrier.has_value()) {
|
if (carrier.has_value()) {
|
||||||
if (net->carrier_ != *carrier) {
|
if (net->carrier_ != *carrier) {
|
||||||
@@ -502,13 +558,25 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) {
|
|||||||
}
|
}
|
||||||
} else if (!is_del_event && net->ifid_ == -1) {
|
} else if (!is_del_event && net->ifid_ == -1) {
|
||||||
// Checking if it's an interface we care about.
|
// Checking if it's an interface we care about.
|
||||||
std::string new_ifname(ifname, ifname_len);
|
std::string matched;
|
||||||
if (net->checkInterface(new_ifname)) {
|
if (net->matchInterface(ifname, altnames, matched)) {
|
||||||
spdlog::debug("network: selecting new interface {}/{}", new_ifname, ifi->ifi_index);
|
if (ifname == matched) {
|
||||||
|
spdlog::debug("network: selecting new interface {}/{}", ifname, ifi->ifi_index);
|
||||||
|
} else {
|
||||||
|
spdlog::debug("network: selecting new interface {}/{} (matched altname {})", ifname,
|
||||||
|
ifi->ifi_index, matched);
|
||||||
|
}
|
||||||
|
|
||||||
net->ifname_ = new_ifname;
|
net->ifname_ = ifname;
|
||||||
net->ifid_ = ifi->ifi_index;
|
net->ifid_ = ifi->ifi_index;
|
||||||
if (ifi->ifi_flags & IFF_POINTOPOINT) net->is_p2p_ = true;
|
if ((ifi->ifi_flags & IFF_POINTOPOINT) != 0) {
|
||||||
|
net->is_p2p_ = true;
|
||||||
|
}
|
||||||
|
if ((ifi->ifi_flags & IFF_UP) == 0) {
|
||||||
|
// With some network drivers (e.g. mt7921e), the interface may
|
||||||
|
// report having a carrier even though interface is down.
|
||||||
|
carrier = false;
|
||||||
|
}
|
||||||
if (carrier.has_value()) {
|
if (carrier.has_value()) {
|
||||||
net->carrier_ = carrier.value();
|
net->carrier_ = carrier.value();
|
||||||
}
|
}
|
||||||
@@ -602,7 +670,6 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
char temp_gw_addr[INET6_ADDRSTRLEN];
|
|
||||||
case RTM_DELROUTE:
|
case RTM_DELROUTE:
|
||||||
is_del_event = true;
|
is_del_event = true;
|
||||||
case RTM_NEWROUTE: {
|
case RTM_NEWROUTE: {
|
||||||
@@ -613,6 +680,7 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) {
|
|||||||
int family = rtm->rtm_family;
|
int family = rtm->rtm_family;
|
||||||
ssize_t attrlen = RTM_PAYLOAD(nh);
|
ssize_t attrlen = RTM_PAYLOAD(nh);
|
||||||
struct rtattr *attr = RTM_RTA(rtm);
|
struct rtattr *attr = RTM_RTA(rtm);
|
||||||
|
char gateway_addr[INET6_ADDRSTRLEN];
|
||||||
bool has_gateway = false;
|
bool has_gateway = false;
|
||||||
bool has_destination = false;
|
bool has_destination = false;
|
||||||
int temp_idx = -1;
|
int temp_idx = -1;
|
||||||
@@ -638,7 +706,7 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) {
|
|||||||
* If someone ever needs to figure out the gateway address as well,
|
* If someone ever needs to figure out the gateway address as well,
|
||||||
* it's here as the attribute payload.
|
* it's here as the attribute payload.
|
||||||
*/
|
*/
|
||||||
inet_ntop(family, RTA_DATA(attr), temp_gw_addr, sizeof(temp_gw_addr));
|
inet_ntop(family, RTA_DATA(attr), gateway_addr, sizeof(gateway_addr));
|
||||||
has_gateway = true;
|
has_gateway = true;
|
||||||
break;
|
break;
|
||||||
case RTA_DST: {
|
case RTA_DST: {
|
||||||
@@ -683,8 +751,8 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) {
|
|||||||
net->clearIface();
|
net->clearIface();
|
||||||
net->ifid_ = temp_idx;
|
net->ifid_ = temp_idx;
|
||||||
net->route_priority = priority;
|
net->route_priority = priority;
|
||||||
net->gwaddr_ = temp_gw_addr;
|
net->gwaddr_ = gateway_addr;
|
||||||
spdlog::debug("network: new default route via {} on if{} metric {}", temp_gw_addr,
|
spdlog::debug("network: new default route via {} on if{} metric {}", gateway_addr,
|
||||||
temp_idx, priority);
|
temp_idx, priority);
|
||||||
|
|
||||||
/* Ask ifname associated with temp_idx as well as carrier status */
|
/* Ask ifname associated with temp_idx as well as carrier status */
|
||||||
@@ -897,43 +965,3 @@ auto waybar::modules::Network::getInfo() -> void {
|
|||||||
}
|
}
|
||||||
nl_send_sync(sock_, nl_msg);
|
nl_send_sync(sock_, nl_msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://gist.github.com/rressi/92af77630faf055934c723ce93ae2495
|
|
||||||
bool waybar::modules::Network::wildcardMatch(const std::string &pattern,
|
|
||||||
const std::string &text) const {
|
|
||||||
auto P = int(pattern.size());
|
|
||||||
auto T = int(text.size());
|
|
||||||
|
|
||||||
auto p = 0, fallback_p = -1;
|
|
||||||
auto t = 0, fallback_t = -1;
|
|
||||||
|
|
||||||
while (t < T) {
|
|
||||||
// Wildcard match:
|
|
||||||
if (p < P && pattern[p] == '*') {
|
|
||||||
fallback_p = p++; // starting point after failures
|
|
||||||
fallback_t = t; // starting point after failures
|
|
||||||
}
|
|
||||||
|
|
||||||
// Simple match:
|
|
||||||
else if (p < P && (pattern[p] == '?' || pattern[p] == text[t])) {
|
|
||||||
p++;
|
|
||||||
t++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Failure, fall back just after last matched '*':
|
|
||||||
else if (fallback_p >= 0) {
|
|
||||||
p = fallback_p + 1; // position just after last matched '*"
|
|
||||||
t = ++fallback_t; // re-try to match text from here
|
|
||||||
}
|
|
||||||
|
|
||||||
// There were no '*' before, so we fail here:
|
|
||||||
else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Consume all '*' at the end of pattern:
|
|
||||||
while (p < P && pattern[p] == '*') p++;
|
|
||||||
|
|
||||||
return p == P;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -147,6 +147,17 @@ void IPC::parseIPC(const std::string &line) {
|
|||||||
} else {
|
} else {
|
||||||
spdlog::error("Active window changed on unknown workspace");
|
spdlog::error("Active window changed on unknown workspace");
|
||||||
}
|
}
|
||||||
|
} else if (const auto &payload = ev["WorkspaceUrgencyChanged"]) {
|
||||||
|
const auto id = payload["id"].asUInt64();
|
||||||
|
const auto urgent = payload["urgent"].asBool();
|
||||||
|
auto it = std::find_if(workspaces_.begin(), workspaces_.end(),
|
||||||
|
[id](const auto &ws) { return ws["id"].asUInt64() == id; });
|
||||||
|
if (it != workspaces_.end()) {
|
||||||
|
auto &ws = *it;
|
||||||
|
ws["is_urgent"] = urgent;
|
||||||
|
} else {
|
||||||
|
spdlog::error("Urgency changed for unknown workspace");
|
||||||
|
}
|
||||||
} else if (const auto &payload = ev["KeyboardLayoutsChanged"]) {
|
} else if (const auto &payload = ev["KeyboardLayoutsChanged"]) {
|
||||||
const auto &layouts = payload["keyboard_layouts"];
|
const auto &layouts = payload["keyboard_layouts"];
|
||||||
const auto &names = layouts["names"];
|
const auto &names = layouts["names"];
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value
|
|||||||
gIPC->registerForIPC("WorkspacesChanged", this);
|
gIPC->registerForIPC("WorkspacesChanged", this);
|
||||||
gIPC->registerForIPC("WorkspaceActivated", this);
|
gIPC->registerForIPC("WorkspaceActivated", this);
|
||||||
gIPC->registerForIPC("WorkspaceActiveWindowChanged", this);
|
gIPC->registerForIPC("WorkspaceActiveWindowChanged", this);
|
||||||
|
gIPC->registerForIPC("WorkspaceUrgencyChanged", this);
|
||||||
|
|
||||||
dp.emit();
|
dp.emit();
|
||||||
}
|
}
|
||||||
@@ -67,6 +68,11 @@ void Workspaces::doUpdate() {
|
|||||||
else
|
else
|
||||||
style_context->remove_class("active");
|
style_context->remove_class("active");
|
||||||
|
|
||||||
|
if (ws["is_urgent"].asBool())
|
||||||
|
style_context->add_class("urgent");
|
||||||
|
else
|
||||||
|
style_context->remove_class("urgent");
|
||||||
|
|
||||||
if (ws["output"]) {
|
if (ws["output"]) {
|
||||||
if (ws["output"].asString() == bar_.output->name)
|
if (ws["output"].asString() == bar_.output->name)
|
||||||
style_context->add_class("current_output");
|
style_context->add_class("current_output");
|
||||||
@@ -166,6 +172,10 @@ std::string Workspaces::getIcon(const std::string &value, const Json::Value &ws)
|
|||||||
const auto &icons = config_["format-icons"];
|
const auto &icons = config_["format-icons"];
|
||||||
if (!icons) return value;
|
if (!icons) return value;
|
||||||
|
|
||||||
|
if (ws["is_urgent"].asBool() && icons["urgent"]) return icons["urgent"].asString();
|
||||||
|
|
||||||
|
if (ws["active_window_id"].isNull() && icons["empty"]) return icons["empty"].asString();
|
||||||
|
|
||||||
if (ws["is_focused"].asBool() && icons["focused"]) return icons["focused"].asString();
|
if (ws["is_focused"].asBool() && icons["focused"]) return icons["focused"].asString();
|
||||||
|
|
||||||
if (ws["is_active"].asBool() && icons["active"]) return icons["active"].asString();
|
if (ws["is_active"].asBool() && icons["active"]) return icons["active"].asString();
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user