Merge upstream/master
Some checks failed
clang-format / lint (push) Has been cancelled
freebsd / build (push) Has been cancelled
linux / build (c++20, alpine) (push) Has been cancelled
linux / build (c++20, archlinux) (push) Has been cancelled
linux / build (c++20, debian) (push) Has been cancelled
linux / build (c++20, fedora) (push) Has been cancelled
linux / build (c++20, gentoo) (push) Has been cancelled
linux / build (c++20, opensuse) (push) Has been cancelled
Nix-Tests / nix-flake-check (push) Has been cancelled
Some checks failed
clang-format / lint (push) Has been cancelled
freebsd / build (push) Has been cancelled
linux / build (c++20, alpine) (push) Has been cancelled
linux / build (c++20, archlinux) (push) Has been cancelled
linux / build (c++20, debian) (push) Has been cancelled
linux / build (c++20, fedora) (push) Has been cancelled
linux / build (c++20, gentoo) (push) Has been cancelled
linux / build (c++20, opensuse) (push) Has been cancelled
Nix-Tests / nix-flake-check (push) Has been cancelled
This commit is contained in:
2
.github/workflows/clang-format.yml
vendored
2
.github/workflows/clang-format.yml
vendored
@ -10,7 +10,7 @@ jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v6
|
||||
- uses: RafikFarhad/clang-format-github-action@v6
|
||||
name: clang-format
|
||||
with:
|
||||
|
||||
2
.github/workflows/clang-tidy.yml.bak
vendored
2
.github/workflows/clang-tidy.yml.bak
vendored
@ -12,7 +12,7 @@ jobs:
|
||||
container:
|
||||
image: alexays/waybar:debian
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v6
|
||||
- name: configure
|
||||
run: |
|
||||
meson -Dcpp_std=c++20 build # necessary to generate compile_commands.json
|
||||
|
||||
2
.github/workflows/docker.yml
vendored
2
.github/workflows/docker.yml
vendored
@ -17,7 +17,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
|
||||
4
.github/workflows/freebsd.yml
vendored
4
.github/workflows/freebsd.yml
vendored
@ -12,13 +12,13 @@ jobs:
|
||||
# https://github.com/actions/runner/issues/385 - for FreeBSD runner support
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v6
|
||||
- name: Test in FreeBSD VM
|
||||
uses: cross-platform-actions/action@v0.28.0
|
||||
timeout-minutes: 180
|
||||
env:
|
||||
CPPFLAGS: '-isystem/usr/local/include'
|
||||
LDFLAGS: '-L/usr/local/lib'
|
||||
LDFLAGS: '-L/usr/local/lib'
|
||||
with:
|
||||
operating_system: freebsd
|
||||
version: "14.2"
|
||||
|
||||
2
.github/workflows/linux.yml
vendored
2
.github/workflows/linux.yml
vendored
@ -25,7 +25,7 @@ jobs:
|
||||
image: alexays/waybar:${{ matrix.distro }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v6
|
||||
- name: configure
|
||||
run: meson setup -Dman-pages=enabled -Dcpp_std=${{matrix.cpp_std}} build
|
||||
- name: build
|
||||
|
||||
21
.github/workflows/nix-tests.yml
vendored
21
.github/workflows/nix-tests.yml
vendored
@ -2,16 +2,19 @@ name: "Nix-Tests"
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-nix-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
jobs:
|
||||
nix-flake-check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: cachix/install-nix-action@v27
|
||||
with:
|
||||
extra_nix_config: |
|
||||
experimental-features = nix-command flakes
|
||||
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
||||
- run: nix flake show
|
||||
- run: nix flake check --print-build-logs
|
||||
- run: nix build --print-build-logs
|
||||
- uses: actions/checkout@v6
|
||||
- uses: cachix/install-nix-action@v31
|
||||
with:
|
||||
extra_nix_config: |
|
||||
experimental-features = nix-command flakes
|
||||
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
||||
- run: nix flake show
|
||||
- run: nix flake check --print-build-logs
|
||||
- run: nix build --print-build-logs
|
||||
|
||||
10
.github/workflows/nix-update-flake-lock.yml
vendored
10
.github/workflows/nix-update-flake-lock.yml
vendored
@ -4,19 +4,19 @@ on:
|
||||
schedule:
|
||||
- cron: '0 0 1 * *' # Run monthly
|
||||
push:
|
||||
paths:
|
||||
- 'flake.nix'
|
||||
paths:
|
||||
- 'flake.nix'
|
||||
jobs:
|
||||
lockfile:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name != 'schedule' || github.repository == 'Alexays/Waybar'
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- name: Install Nix
|
||||
uses: cachix/install-nix-action@v27
|
||||
uses: cachix/install-nix-action@v31
|
||||
with:
|
||||
extra_nix_config: |
|
||||
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Update flake.lock
|
||||
uses: DeterminateSystems/update-flake-lock@v21
|
||||
uses: DeterminateSystems/update-flake-lock@v28
|
||||
|
||||
@ -48,7 +48,7 @@ class AModule : public IModule {
|
||||
virtual bool handleMouseLeave(GdkEventCrossing* const& ev);
|
||||
virtual bool handleScroll(GdkEventScroll*);
|
||||
virtual bool handleRelease(GdkEventButton* const& ev);
|
||||
GObject* menu_;
|
||||
GObject* menu_ = nullptr;
|
||||
|
||||
private:
|
||||
bool handleUserEvent(GdkEventButton* const& ev);
|
||||
|
||||
@ -35,6 +35,7 @@ class Group : public AModule {
|
||||
bool handleMouseLeave(GdkEventCrossing* const& ev) override;
|
||||
bool handleToggle(GdkEventButton* const& ev) override;
|
||||
void toggle();
|
||||
bool handleScroll(GdkEventScroll* e) override;
|
||||
void show_group();
|
||||
void hide_group();
|
||||
void set_visible(bool v) {
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
#if defined(__linux__)
|
||||
#include <sys/inotify.h>
|
||||
#endif
|
||||
#include <sys/poll.h>
|
||||
#include <poll.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
|
||||
@ -43,6 +43,7 @@ class Workspaces : public AModule, public EventHandler {
|
||||
auto moveToMonitor() const -> bool { return m_moveToMonitor; }
|
||||
auto enableTaskbar() const -> bool { return m_enableTaskbar; }
|
||||
auto taskbarWithIcon() const -> bool { return m_taskbarWithIcon; }
|
||||
auto barScroll() const -> bool { return m_barScroll; }
|
||||
|
||||
auto getBarOutput() const -> std::string { return m_bar.output->name; }
|
||||
auto formatBefore() const -> std::string { return m_formatBefore; }
|
||||
@ -122,6 +123,8 @@ class Workspaces : public AModule, public EventHandler {
|
||||
static std::pair<std::string, std::string> splitDoublePayload(std::string const& payload);
|
||||
static std::tuple<std::string, std::string, std::string> splitTriplePayload(
|
||||
std::string const& payload);
|
||||
// scroll events
|
||||
bool handleScroll(GdkEventScroll* e) override;
|
||||
|
||||
// Update methods
|
||||
void doUpdate();
|
||||
@ -145,6 +148,7 @@ class Workspaces : public AModule, public EventHandler {
|
||||
bool m_specialVisibleOnly = false;
|
||||
bool m_persistentOnly = false;
|
||||
bool m_moveToMonitor = false;
|
||||
bool m_barScroll = false;
|
||||
Json::Value m_persistentWorkspaceConfig;
|
||||
|
||||
// Map for windows stored in workspaces not present in the current bar.
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
#include <fmt/chrono.h>
|
||||
#include <gtkmm/label.h>
|
||||
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
|
||||
@ -41,6 +42,7 @@ class KeyboardState : public AModule {
|
||||
|
||||
struct libinput* libinput_;
|
||||
std::unordered_map<std::string, struct libinput_device*> libinput_devices_;
|
||||
std::mutex devices_mutex_; // protects libinput_devices_
|
||||
std::set<int> binding_keys;
|
||||
|
||||
util::SleeperThread libinput_thread_, hotplug_thread_;
|
||||
|
||||
@ -78,6 +78,7 @@ class Mpris : public ALabel {
|
||||
|
||||
PlayerctlPlayerManager* manager;
|
||||
PlayerctlPlayer* player;
|
||||
PlayerctlPlayer* last_active_player_ = nullptr;
|
||||
std::string lastStatus;
|
||||
std::string lastPlayer;
|
||||
|
||||
|
||||
@ -17,7 +17,7 @@ class EventHandler {
|
||||
|
||||
class IPC {
|
||||
public:
|
||||
IPC() { startIPC(); }
|
||||
IPC();
|
||||
|
||||
void registerForIPC(const std::string& ev, EventHandler* ev_handler);
|
||||
void unregisterForIPC(EventHandler* handler);
|
||||
|
||||
@ -12,6 +12,7 @@
|
||||
#include <string>
|
||||
|
||||
#include "ipc.hpp"
|
||||
#include "util/SafeSignal.hpp"
|
||||
#include "util/sleeper_thread.hpp"
|
||||
|
||||
namespace waybar::modules::sway {
|
||||
@ -27,8 +28,8 @@ class Ipc {
|
||||
std::string payload;
|
||||
};
|
||||
|
||||
sigc::signal<void, const struct ipc_response&> signal_event;
|
||||
sigc::signal<void, const struct ipc_response&> signal_cmd;
|
||||
::waybar::SafeSignal<const struct ipc_response&> signal_event;
|
||||
::waybar::SafeSignal<const struct ipc_response&> signal_cmd;
|
||||
|
||||
void sendCmd(uint32_t type, const std::string& payload = "");
|
||||
void subscribe(const std::string& payload);
|
||||
|
||||
@ -89,7 +89,7 @@ struct Sock {
|
||||
}
|
||||
};
|
||||
|
||||
class IPC {
|
||||
class IPC : public std::enable_shared_from_this<IPC> {
|
||||
static std::weak_ptr<IPC> instance;
|
||||
Json::CharReaderBuilder reader_builder;
|
||||
Json::StreamWriterBuilder writer_builder;
|
||||
@ -98,7 +98,7 @@ class IPC {
|
||||
State state;
|
||||
std::mutex state_mutex;
|
||||
|
||||
IPC() { start(); }
|
||||
IPC() = default;
|
||||
|
||||
static auto connect() -> Sock;
|
||||
auto receive(Sock& sock) -> Json::Value;
|
||||
|
||||
@ -59,6 +59,7 @@ inline int close(FILE* fp, pid_t pid) {
|
||||
spdlog::debug("Cmd continued");
|
||||
} else if (ret == -1) {
|
||||
spdlog::debug("waitpid failed: {}", strerror(errno));
|
||||
break;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
@ -172,8 +173,6 @@ inline int32_t forkExec(const std::string& cmd, const std::string& output_name)
|
||||
return pid;
|
||||
}
|
||||
|
||||
inline int32_t forkExec(const std::string& cmd) {
|
||||
return forkExec(cmd, "");
|
||||
}
|
||||
inline int32_t forkExec(const std::string& cmd) { return forkExec(cmd, ""); }
|
||||
|
||||
} // namespace waybar::util::command
|
||||
|
||||
@ -182,6 +182,7 @@ Every entry in the *events* object consists of a *<event-name>* (typeof: *string
|
||||
|
||||
- *on-<status>-<state>*
|
||||
- *on-<status>-<capacity>*
|
||||
- *on-<status>*
|
||||
|
||||
Where:
|
||||
|
||||
@ -203,7 +204,9 @@ Where:
|
||||
"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!'"
|
||||
"on-charging-100": "notify-send -u normal 'Battery Full!'",
|
||||
"on-discharging": "notify-send -u normal 'Power Switch' Discharging",
|
||||
"on-charging": "notify-send -u normal 'Power Switch' Charging'"
|
||||
},
|
||||
"format": "{capacity}% {icon}",
|
||||
"format-icons": ["", "", "", "", ""],
|
||||
|
||||
@ -130,6 +130,11 @@ This setting is ignored if *workspace-taskbar.enable* is set to true.
|
||||
Otherwise, the workspace will open on the monitor where it was previously assigned.
|
||||
Analog to using `focusworkspaceoncurrentmonitor` dispatcher instead of `workspace` in Hyprland.
|
||||
|
||||
*enable-bar-scroll*: ++
|
||||
typeof: bool ++
|
||||
default: false ++
|
||||
If set to false, you can't scroll to cycle throughout workspaces from the entire bar. If set to true this behaviour is enabled.
|
||||
|
||||
*ignore-workspaces*: ++
|
||||
typeof: array ++
|
||||
default: [] ++
|
||||
|
||||
@ -26,7 +26,8 @@ The *image* module displays an image from a path.
|
||||
*interval*: ++
|
||||
typeof: integer or float ++
|
||||
The interval (in seconds) to re-render the image. ++
|
||||
Minimum value is 0.001 (1ms). Values smaller than 1ms will be set to 1ms. ++
|
||||
If set to a positive value, the minimum is 0.001 (1ms). Values smaller than 1ms will be set to 1ms. ++
|
||||
Zero or negative values are treated as "once". ++
|
||||
This is useful if the contents of *path* changes. ++
|
||||
If no *interval* is defined, the image will only be rendered once.
|
||||
|
||||
|
||||
@ -36,7 +36,7 @@ The visual display elements for waybar use a CSS stylesheet, see *waybar-styles(
|
||||
*expand-right* ++
|
||||
typeof: bool ++
|
||||
default: false ++
|
||||
Enables the modules-left to consume all left over space dynamically.
|
||||
Enables the modules-right to consume all left over space dynamically.
|
||||
|
||||
*layer* ++
|
||||
typeof: string ++
|
||||
|
||||
@ -151,19 +151,24 @@ void AAppIconLabel::updateAppIconName(const std::string& app_identifier,
|
||||
}
|
||||
|
||||
void AAppIconLabel::updateAppIcon() {
|
||||
if (update_app_icon_) {
|
||||
if (update_app_icon_ || (!iconEnabled() && image_.get_visible())) {
|
||||
update_app_icon_ = false;
|
||||
if (app_icon_name_.empty()) {
|
||||
image_.set_visible(false);
|
||||
} else if (app_icon_name_.front() == '/') {
|
||||
auto pixbuf = Gdk::Pixbuf::create_from_file(app_icon_name_);
|
||||
int scaled_icon_size = app_icon_size_ * image_.get_scale_factor();
|
||||
pixbuf = Gdk::Pixbuf::create_from_file(app_icon_name_, scaled_icon_size, scaled_icon_size);
|
||||
try {
|
||||
int scaled_icon_size = app_icon_size_ * image_.get_scale_factor();
|
||||
auto pixbuf =
|
||||
Gdk::Pixbuf::create_from_file(app_icon_name_, scaled_icon_size, scaled_icon_size);
|
||||
|
||||
auto surface = Gdk::Cairo::create_surface_from_pixbuf(pixbuf, image_.get_scale_factor(),
|
||||
image_.get_window());
|
||||
image_.set(surface);
|
||||
image_.set_visible(true);
|
||||
auto surface = Gdk::Cairo::create_surface_from_pixbuf(pixbuf, image_.get_scale_factor(),
|
||||
image_.get_window());
|
||||
image_.set(surface);
|
||||
image_.set_visible(true);
|
||||
} catch (const Glib::Exception& e) {
|
||||
spdlog::warn("Failed to load app icon {}: {}", app_icon_name_, std::string(e.what()));
|
||||
image_.set_visible(false);
|
||||
}
|
||||
} else {
|
||||
image_.set_from_icon_name(app_icon_name_, Gtk::ICON_SIZE_INVALID);
|
||||
image_.set_visible(true);
|
||||
|
||||
@ -91,11 +91,13 @@ ALabel::ALabel(const Json::Value& config, const std::string& name, const std::st
|
||||
|
||||
// Make the GtkBuilder and check for errors in his parsing
|
||||
if (gtk_builder_add_from_string(builder, fileContent.str().c_str(), -1, nullptr) == 0U) {
|
||||
g_object_unref(builder);
|
||||
throw std::runtime_error("Error found in the file " + menuFile);
|
||||
}
|
||||
|
||||
menu_ = gtk_builder_get_object(builder, "menu");
|
||||
if (menu_ == nullptr) {
|
||||
g_object_unref(builder);
|
||||
throw std::runtime_error("Failed to get 'menu' object from GtkBuilder");
|
||||
}
|
||||
submenus_ = std::map<std::string, GtkMenuItem*>();
|
||||
@ -105,11 +107,17 @@ ALabel::ALabel(const Json::Value& config, const std::string& name, const std::st
|
||||
for (Json::Value::const_iterator it = config_["menu-actions"].begin();
|
||||
it != config_["menu-actions"].end(); ++it) {
|
||||
std::string key = it.key().asString();
|
||||
submenus_[key] = GTK_MENU_ITEM(gtk_builder_get_object(builder, key.c_str()));
|
||||
auto* item = gtk_builder_get_object(builder, key.c_str());
|
||||
if (item == nullptr) {
|
||||
spdlog::warn("Menu item '{}' not found in builder file", key);
|
||||
continue;
|
||||
}
|
||||
submenus_[key] = GTK_MENU_ITEM(item);
|
||||
menuActionsMap_[key] = it->asString();
|
||||
g_signal_connect(submenus_[key], "activate", G_CALLBACK(handleGtkMenuEvent),
|
||||
(gpointer)menuActionsMap_[key].c_str());
|
||||
}
|
||||
g_object_unref(builder);
|
||||
} catch (std::runtime_error& e) {
|
||||
spdlog::warn("Error while creating the menu : {}. Menu popup not activated.", e.what());
|
||||
}
|
||||
@ -141,7 +149,8 @@ std::string ALabel::getIcon(uint16_t percentage, const std::string& alt, uint16_
|
||||
if (format_icons.isArray()) {
|
||||
auto size = format_icons.size();
|
||||
if (size != 0U) {
|
||||
auto idx = std::clamp(percentage / ((max == 0 ? 100 : max) / size), 0U, size - 1);
|
||||
auto divisor = std::max(1U, (max == 0 ? 100U : static_cast<unsigned>(max)) / size);
|
||||
auto idx = std::clamp(percentage / divisor, 0U, size - 1);
|
||||
format_icons = format_icons[idx];
|
||||
}
|
||||
}
|
||||
@ -167,7 +176,8 @@ std::string ALabel::getIcon(uint16_t percentage, const std::vector<std::string>&
|
||||
if (format_icons.isArray()) {
|
||||
auto size = format_icons.size();
|
||||
if (size != 0U) {
|
||||
auto idx = std::clamp(percentage / ((max == 0 ? 100 : max) / size), 0U, size - 1);
|
||||
auto divisor = std::max(1U, (max == 0 ? 100U : static_cast<unsigned>(max)) / size);
|
||||
auto idx = std::clamp(percentage / divisor, 0U, size - 1);
|
||||
format_icons = format_icons[idx];
|
||||
}
|
||||
}
|
||||
|
||||
@ -166,9 +166,9 @@ bool AModule::handleUserEvent(GdkEventButton* const& e) {
|
||||
}
|
||||
|
||||
// Check that a menu has been configured
|
||||
if (config_["menu"].isString()) {
|
||||
if (rec != eventMap_.cend() && config_["menu"].isString()) {
|
||||
// Check if the event is the one specified for the "menu" option
|
||||
if (rec->second == config_["menu"].asString()) {
|
||||
if (rec->second == config_["menu"].asString() && menu_ != nullptr) {
|
||||
// Popup the menu
|
||||
gtk_widget_show_all(GTK_WIDGET(menu_));
|
||||
gtk_menu_popup_at_pointer(GTK_MENU(menu_), reinterpret_cast<GdkEvent*>(e));
|
||||
|
||||
@ -143,6 +143,11 @@ void Group::refresh(int sig) {
|
||||
}
|
||||
}
|
||||
|
||||
bool Group::handleScroll(GdkEventScroll* e) {
|
||||
// no scroll.
|
||||
return true;
|
||||
}
|
||||
|
||||
Gtk::Box& Group::getBox() { return is_drawer ? (is_first_widget ? box : revealer_box) : box; }
|
||||
|
||||
void Group::addWidget(Gtk::Widget& widget) {
|
||||
|
||||
14
src/main.cpp
14
src/main.cpp
@ -33,7 +33,10 @@ static void writeSignalToPipe(int signum) {
|
||||
// to `signal_handler`.
|
||||
static void catchSignals(waybar::SafeSignal<int>& signal_handler) {
|
||||
int fd[2];
|
||||
pipe(fd);
|
||||
if (pipe(fd) != 0) {
|
||||
spdlog::error("Failed to create signal pipe: {}", strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
int signal_pipe_read_fd = fd[0];
|
||||
signal_pipe_write_fd = fd[1];
|
||||
@ -138,15 +141,16 @@ static void handleSignalMainThread(int signum, bool& reload) {
|
||||
break;
|
||||
case SIGCHLD:
|
||||
spdlog::debug("Received SIGCHLD in signalThread");
|
||||
if (!reap.empty()) {
|
||||
reap_mtx.lock();
|
||||
for (auto it = reap.begin(); it != reap.end(); ++it) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(reap_mtx);
|
||||
for (auto it = reap.begin(); it != reap.end();) {
|
||||
if (waitpid(*it, nullptr, WNOHANG) == *it) {
|
||||
spdlog::debug("Reaped child with PID: {}", *it);
|
||||
it = reap.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
reap_mtx.unlock();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
||||
@ -58,11 +58,11 @@ auto waybar::modules::Backlight::update() -> void {
|
||||
tooltip_format = config_["tooltip-format"].asString();
|
||||
}
|
||||
if (!tooltip_format.empty()) {
|
||||
label_.set_tooltip_text(fmt::format(fmt::runtime(tooltip_format),
|
||||
fmt::arg("percent", percent),
|
||||
fmt::arg("icon", getIcon(percent))));
|
||||
label_.set_tooltip_markup(fmt::format(fmt::runtime(tooltip_format),
|
||||
fmt::arg("percent", percent),
|
||||
fmt::arg("icon", getIcon(percent))));
|
||||
} else {
|
||||
label_.set_tooltip_text(desc);
|
||||
label_.set_tooltip_markup(desc);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@ -139,7 +139,9 @@ void waybar::modules::Battery::refreshBatteries() {
|
||||
auto event_path = (node.path() / "uevent");
|
||||
auto wd = inotify_add_watch(battery_watch_fd_, event_path.c_str(), IN_ACCESS);
|
||||
if (wd < 0) {
|
||||
throw std::runtime_error("Could not watch events for " + node.path().string());
|
||||
spdlog::warn("Could not watch events for {} (device may have been removed)",
|
||||
node.path().string());
|
||||
continue;
|
||||
}
|
||||
batteries_[node.path()] = wd;
|
||||
}
|
||||
@ -686,7 +688,7 @@ auto waybar::modules::Battery::update() -> void {
|
||||
status = getAdapterStatus(capacity);
|
||||
}
|
||||
auto status_pretty = status;
|
||||
puts(status.c_str());
|
||||
|
||||
// Transform to lowercase and replace space with dash
|
||||
std::ranges::transform(status.begin(), status.end(), status.begin(),
|
||||
[](char ch) { return ch == ' ' ? '-' : std::tolower(ch); });
|
||||
@ -790,16 +792,19 @@ void waybar::modules::Battery::processEvents(std::string& state, std::string& st
|
||||
if (!events.isObject() || events.empty()) {
|
||||
return;
|
||||
}
|
||||
std::string event_name = fmt::format("on-{}-{}", status == "discharging" ? status : "charging",
|
||||
state.empty() ? std::to_string(capacity) : state);
|
||||
auto exec = [](Json::Value const& event) {
|
||||
if (!event.isString()) return;
|
||||
if (auto command = event.asString(); !command.empty()) {
|
||||
util::command::exec(command, "");
|
||||
}
|
||||
};
|
||||
std::string status_name = status == "discharging" ? "on-discharging" : "on-charging";
|
||||
std::string event_name = status_name + '-' + (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, "");
|
||||
}
|
||||
exec(events[event_name]);
|
||||
if (!last_event_.empty() && last_event_[3] != event_name[3]) {
|
||||
exec(events[status_name]);
|
||||
}
|
||||
last_event_ = event_name;
|
||||
}
|
||||
|
||||
@ -111,13 +111,16 @@ waybar::modules::Bluetooth::Bluetooth(const std::string& id, const Json::Value&
|
||||
findConnectedDevices(cur_controller_->path, connected_devices_);
|
||||
}
|
||||
|
||||
g_signal_connect(manager_.get(), "object-added", G_CALLBACK(onObjectAdded), this);
|
||||
g_signal_connect(manager_.get(), "object-removed", G_CALLBACK(onObjectRemoved), this);
|
||||
g_signal_connect(manager_.get(), "interface-proxy-properties-changed",
|
||||
G_CALLBACK(onInterfaceProxyPropertiesChanged), this);
|
||||
g_signal_connect(manager_.get(), "interface-added", G_CALLBACK(onInterfaceAddedOrRemoved), this);
|
||||
g_signal_connect(manager_.get(), "interface-removed", G_CALLBACK(onInterfaceAddedOrRemoved),
|
||||
this);
|
||||
if (manager_) {
|
||||
g_signal_connect(manager_.get(), "object-added", G_CALLBACK(onObjectAdded), this);
|
||||
g_signal_connect(manager_.get(), "object-removed", G_CALLBACK(onObjectRemoved), this);
|
||||
g_signal_connect(manager_.get(), "interface-proxy-properties-changed",
|
||||
G_CALLBACK(onInterfaceProxyPropertiesChanged), this);
|
||||
g_signal_connect(manager_.get(), "interface-added", G_CALLBACK(onInterfaceAddedOrRemoved),
|
||||
this);
|
||||
g_signal_connect(manager_.get(), "interface-removed", G_CALLBACK(onInterfaceAddedOrRemoved),
|
||||
this);
|
||||
}
|
||||
|
||||
#ifdef WANT_RFKILL
|
||||
rfkill_.on_update.connect(sigc::hide(sigc::mem_fun(*this, &Bluetooth::update)));
|
||||
@ -446,6 +449,10 @@ auto waybar::modules::Bluetooth::getControllerProperties(GDBusObject* object,
|
||||
auto waybar::modules::Bluetooth::findCurController() -> std::optional<ControllerInfo> {
|
||||
std::optional<ControllerInfo> controller_info;
|
||||
|
||||
if (!manager_) {
|
||||
return controller_info;
|
||||
}
|
||||
|
||||
GList* objects = g_dbus_object_manager_get_objects(manager_.get());
|
||||
for (GList* l = objects; l != NULL; l = l->next) {
|
||||
GDBusObject* object = G_DBUS_OBJECT(l->data);
|
||||
@ -465,6 +472,9 @@ auto waybar::modules::Bluetooth::findCurController() -> std::optional<Controller
|
||||
auto waybar::modules::Bluetooth::findConnectedDevices(const std::string& cur_controller_path,
|
||||
std::vector<DeviceInfo>& connected_devices)
|
||||
-> void {
|
||||
if (!manager_) {
|
||||
return;
|
||||
}
|
||||
GList* objects = g_dbus_object_manager_get_objects(manager_.get());
|
||||
for (GList* l = objects; l != NULL; l = l->next) {
|
||||
GDBusObject* object = G_DBUS_OBJECT(l->data);
|
||||
|
||||
@ -26,6 +26,7 @@ void waybar::modules::cava::Cava::pause_resume() { backend_->doPauseResume(); }
|
||||
auto waybar::modules::cava::Cava::onUpdate(const std::string& input) -> void {
|
||||
Glib::signal_idle().connect_once([this, input]() {
|
||||
if (silence_) {
|
||||
silence_ = false;
|
||||
label_.get_style_context()->remove_class("silent");
|
||||
if (!label_.get_style_context()->has_class("updated"))
|
||||
label_.get_style_context()->add_class("updated");
|
||||
@ -38,7 +39,6 @@ auto waybar::modules::cava::Cava::onUpdate(const std::string& input) -> void {
|
||||
label_.show();
|
||||
ALabel::update();
|
||||
});
|
||||
silence_ = false;
|
||||
}
|
||||
|
||||
auto waybar::modules::cava::Cava::onSilence() -> void {
|
||||
|
||||
@ -218,7 +218,7 @@ void waybar::modules::cava::CavaBackend::loadConfig() {
|
||||
prm_.input = ::cava::input_method_by_name(config_["method"].asString().c_str());
|
||||
if (config_["source"].isString()) {
|
||||
if (prm_.audio_source) free(prm_.audio_source);
|
||||
prm_.audio_source = config_["source"].asString().data();
|
||||
prm_.audio_source = strdup(config_["source"].asString().c_str());
|
||||
}
|
||||
if (config_["sample_rate"].isNumeric()) prm_.samplerate = config_["sample_rate"].asLargestInt();
|
||||
if (config_["sample_bits"].isInt()) prm_.samplebits = config_["sample_bits"].asInt();
|
||||
|
||||
@ -35,7 +35,7 @@ CFFI::CFFI(const std::string& name, const std::string& id, const Json::Value& co
|
||||
throw std::runtime_error{std::string{"Missing wbcffi_init function: "} + dlerror()};
|
||||
}
|
||||
hooks_.deinit = reinterpret_cast<DenitFn*>(dlsym(handle, "wbcffi_deinit"));
|
||||
if (!hooks_.init) {
|
||||
if (!hooks_.deinit) {
|
||||
throw std::runtime_error{std::string{"Missing wbcffi_deinit function: "} + dlerror()};
|
||||
}
|
||||
// Optional functions
|
||||
|
||||
@ -26,9 +26,7 @@ auto waybar::modules::Cpu::update() -> void {
|
||||
auto [load1, load5, load15] = Load::getLoad();
|
||||
auto [cpu_usage, tooltip] = CpuUsage::getCpuUsage(prev_times_);
|
||||
auto [max_frequency, min_frequency, avg_frequency] = CpuFrequency::getCpuFrequency();
|
||||
if (tooltipEnabled()) {
|
||||
label_.set_tooltip_text(tooltip);
|
||||
}
|
||||
|
||||
auto format = format_;
|
||||
auto total_usage = cpu_usage.empty() ? 0 : cpu_usage[0];
|
||||
auto state = getState(total_usage);
|
||||
@ -48,14 +46,25 @@ auto waybar::modules::Cpu::update() -> void {
|
||||
store.push_back(fmt::arg("max_frequency", max_frequency));
|
||||
store.push_back(fmt::arg("min_frequency", min_frequency));
|
||||
store.push_back(fmt::arg("avg_frequency", avg_frequency));
|
||||
std::vector<std::string> arg_names;
|
||||
arg_names.reserve(cpu_usage.size() * 2);
|
||||
for (size_t i = 1; i < cpu_usage.size(); ++i) {
|
||||
auto core_i = i - 1;
|
||||
auto core_format = fmt::format("usage{}", core_i);
|
||||
store.push_back(fmt::arg(core_format.c_str(), cpu_usage[i]));
|
||||
auto icon_format = fmt::format("icon{}", core_i);
|
||||
store.push_back(fmt::arg(icon_format.c_str(), getIcon(cpu_usage[i], icons)));
|
||||
arg_names.push_back(fmt::format("usage{}", core_i));
|
||||
store.push_back(fmt::arg(arg_names.back().c_str(), cpu_usage[i]));
|
||||
arg_names.push_back(fmt::format("icon{}", core_i));
|
||||
store.push_back(fmt::arg(arg_names.back().c_str(), getIcon(cpu_usage[i], icons)));
|
||||
}
|
||||
label_.set_markup(fmt::vformat(format, store));
|
||||
|
||||
if (tooltipEnabled()) {
|
||||
if (config_["tooltip-format"].isString()) {
|
||||
tooltip = config_["tooltip-format"].asString();
|
||||
label_.set_tooltip_markup(fmt::vformat(tooltip, store));
|
||||
} else {
|
||||
label_.set_tooltip_markup(tooltip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Call parent update
|
||||
|
||||
@ -20,12 +20,7 @@ waybar::modules::CpuFrequency::CpuFrequency(const std::string& id, const Json::V
|
||||
auto waybar::modules::CpuFrequency::update() -> void {
|
||||
// TODO: as creating dynamic fmt::arg arrays is buggy we have to calc both
|
||||
auto [max_frequency, min_frequency, avg_frequency] = CpuFrequency::getCpuFrequency();
|
||||
if (tooltipEnabled()) {
|
||||
auto tooltip =
|
||||
fmt::format("Minimum frequency: {}\nAverage frequency: {}\nMaximum frequency: {}\n",
|
||||
min_frequency, avg_frequency, max_frequency);
|
||||
label_.set_tooltip_text(tooltip);
|
||||
}
|
||||
|
||||
auto format = format_;
|
||||
auto state = getState(avg_frequency);
|
||||
if (!state.empty() && config_["format-" + state].isString()) {
|
||||
@ -43,6 +38,18 @@ auto waybar::modules::CpuFrequency::update() -> void {
|
||||
store.push_back(fmt::arg("min_frequency", min_frequency));
|
||||
store.push_back(fmt::arg("avg_frequency", avg_frequency));
|
||||
label_.set_markup(fmt::vformat(format, store));
|
||||
|
||||
if (tooltipEnabled()) {
|
||||
std::string tooltip;
|
||||
if (config_["tooltip-format"].isString()) {
|
||||
tooltip = config_["tooltip-format"].asString();
|
||||
label_.set_tooltip_markup(fmt::vformat(tooltip, store));
|
||||
} else {
|
||||
tooltip = "Minimum frequency: {}\nAverage frequency: {}\nMaximum frequency: {}\n";
|
||||
label_.set_tooltip_markup(
|
||||
fmt::format(fmt::runtime(tooltip), min_frequency, avg_frequency, max_frequency));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Call parent update
|
||||
|
||||
@ -20,9 +20,7 @@ waybar::modules::CpuUsage::CpuUsage(const std::string& id, const Json::Value& co
|
||||
auto waybar::modules::CpuUsage::update() -> void {
|
||||
// TODO: as creating dynamic fmt::arg arrays is buggy we have to calc both
|
||||
auto [cpu_usage, tooltip] = CpuUsage::getCpuUsage(prev_times_);
|
||||
if (tooltipEnabled()) {
|
||||
label_.set_tooltip_text(tooltip);
|
||||
}
|
||||
|
||||
auto format = format_;
|
||||
auto total_usage = cpu_usage.empty() ? 0 : cpu_usage[0];
|
||||
auto state = getState(total_usage);
|
||||
@ -38,14 +36,25 @@ auto waybar::modules::CpuUsage::update() -> void {
|
||||
fmt::dynamic_format_arg_store<fmt::format_context> store;
|
||||
store.push_back(fmt::arg("usage", total_usage));
|
||||
store.push_back(fmt::arg("icon", getIcon(total_usage, icons)));
|
||||
std::vector<std::string> arg_names;
|
||||
arg_names.reserve(cpu_usage.size() * 2);
|
||||
for (size_t i = 1; i < cpu_usage.size(); ++i) {
|
||||
auto core_i = i - 1;
|
||||
auto core_format = fmt::format("usage{}", core_i);
|
||||
store.push_back(fmt::arg(core_format.c_str(), cpu_usage[i]));
|
||||
auto icon_format = fmt::format("icon{}", core_i);
|
||||
store.push_back(fmt::arg(icon_format.c_str(), getIcon(cpu_usage[i], icons)));
|
||||
arg_names.push_back(fmt::format("usage{}", core_i));
|
||||
store.push_back(fmt::arg(arg_names.back().c_str(), cpu_usage[i]));
|
||||
arg_names.push_back(fmt::format("icon{}", core_i));
|
||||
store.push_back(fmt::arg(arg_names.back().c_str(), getIcon(cpu_usage[i], icons)));
|
||||
}
|
||||
label_.set_markup(fmt::vformat(format, store));
|
||||
|
||||
if (tooltipEnabled()) {
|
||||
if (config_["tooltip-format"].isString()) {
|
||||
tooltip = config_["tooltip-format"].asString();
|
||||
label_.set_tooltip_markup(fmt::vformat(tooltip, store));
|
||||
} else {
|
||||
label_.set_tooltip_markup(tooltip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Call parent update
|
||||
@ -71,7 +80,8 @@ std::tuple<std::vector<uint16_t>, std::string> waybar::modules::CpuUsage::getCpu
|
||||
auto [prev_idle, prev_total] = prev_times[0];
|
||||
const float delta_idle = curr_idle - prev_idle;
|
||||
const float delta_total = curr_total - prev_total;
|
||||
uint16_t tmp = 100 * (1 - delta_idle / delta_total);
|
||||
uint16_t tmp =
|
||||
(delta_total > 0) ? static_cast<uint16_t>(100 * (1 - delta_idle / delta_total)) : 0;
|
||||
tooltip = fmt::format("Total: {}%\nCores: (pending)", tmp);
|
||||
usage.push_back(tmp);
|
||||
} else {
|
||||
@ -93,7 +103,8 @@ std::tuple<std::vector<uint16_t>, std::string> waybar::modules::CpuUsage::getCpu
|
||||
}
|
||||
const float delta_idle = curr_idle - prev_idle;
|
||||
const float delta_total = curr_total - prev_total;
|
||||
uint16_t tmp = 100 * (1 - delta_idle / delta_total);
|
||||
uint16_t tmp =
|
||||
(delta_total > 0) ? static_cast<uint16_t>(100 * (1 - delta_idle / delta_total)) : 0;
|
||||
if (i == 0) {
|
||||
tooltip = fmt::format("Total: {}%", tmp);
|
||||
} else {
|
||||
|
||||
@ -182,9 +182,10 @@ auto waybar::modules::Custom::update() -> void {
|
||||
if (tooltipEnabled()) {
|
||||
if (tooltip_format_enabled_) {
|
||||
auto tooltip = config_["tooltip-format"].asString();
|
||||
tooltip = fmt::format(
|
||||
fmt::runtime(tooltip), fmt::arg("text", text_), fmt::arg("alt", alt_),
|
||||
fmt::arg("icon", getIcon(percentage_, alt_)), fmt::arg("percentage", percentage_));
|
||||
tooltip = fmt::format(fmt::runtime(tooltip), fmt::arg("text", text_),
|
||||
fmt::arg("tooltip", tooltip_), fmt::arg("alt", alt_),
|
||||
fmt::arg("icon", getIcon(percentage_, alt_)),
|
||||
fmt::arg("percentage", percentage_));
|
||||
label_.set_tooltip_markup(tooltip);
|
||||
} else if (text_ == tooltip_) {
|
||||
label_.set_tooltip_markup(str);
|
||||
|
||||
@ -41,7 +41,7 @@ auto waybar::modules::Disk::update() -> void {
|
||||
fs_used - File system used space
|
||||
*/
|
||||
|
||||
if (err != 0) {
|
||||
if (err != 0 || stats.f_blocks == 0) {
|
||||
event_box_.hide();
|
||||
return;
|
||||
}
|
||||
@ -81,7 +81,7 @@ auto waybar::modules::Disk::update() -> void {
|
||||
if (config_["tooltip-format"].isString()) {
|
||||
tooltip_format = config_["tooltip-format"].asString();
|
||||
}
|
||||
label_.set_tooltip_text(fmt::format(
|
||||
label_.set_tooltip_markup(fmt::format(
|
||||
fmt::runtime(tooltip_format), stats.f_bavail * 100 / stats.f_blocks, fmt::arg("free", free),
|
||||
fmt::arg("percentage_free", stats.f_bavail * 100 / stats.f_blocks), fmt::arg("used", used),
|
||||
fmt::arg("percentage_used", percentage_used), fmt::arg("total", total),
|
||||
@ -112,4 +112,4 @@ float waybar::modules::Disk::calc_specific_divisor(std::string divisor) {
|
||||
} else { // default to Bytes if it is anything that we don't recongnise
|
||||
return 1.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -179,6 +179,7 @@ bool Tags::handle_button_press(GdkEventButton* event_button, uint32_t tag) {
|
||||
}
|
||||
|
||||
void Tags::handle_view_tags(uint32_t tag, uint32_t state, uint32_t clients, uint32_t focused) {
|
||||
if (tag >= buttons_.size()) return;
|
||||
// First clear all occupied state
|
||||
auto& button = buttons_[tag];
|
||||
if (clients) {
|
||||
|
||||
@ -116,7 +116,7 @@ void Window::handle_frame() {
|
||||
updateAppIconName(appid_, "");
|
||||
updateAppIcon();
|
||||
if (tooltipEnabled()) {
|
||||
label_.set_tooltip_text(title_);
|
||||
label_.set_tooltip_markup(title_);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -143,7 +143,9 @@ void WorkspaceManager::handle_finished() {
|
||||
ext_manager_ = nullptr;
|
||||
}
|
||||
|
||||
void WorkspaceManager::commit() const { ext_workspace_manager_v1_commit(ext_manager_); }
|
||||
void WorkspaceManager::commit() const {
|
||||
if (ext_manager_) ext_workspace_manager_v1_commit(ext_manager_);
|
||||
}
|
||||
|
||||
void WorkspaceManager::update() {
|
||||
spdlog::debug("[ext/workspaces]: Updating state");
|
||||
|
||||
@ -128,9 +128,9 @@ void Gamemode::getData() {
|
||||
Glib::VariantContainerBase data = gamemode_proxy->call_sync("Get", parameters);
|
||||
if (data && data.is_of_type(Glib::VariantType("(v)"))) {
|
||||
Glib::VariantBase variant;
|
||||
g_variant_get(data.gobj_copy(), "(v)", &variant);
|
||||
g_variant_get(const_cast<GVariant*>(data.gobj()), "(v)", &variant);
|
||||
if (variant && variant.is_of_type(Glib::VARIANT_TYPE_INT32)) {
|
||||
g_variant_get(variant.gobj_copy(), "i", &gameCount);
|
||||
g_variant_get(const_cast<GVariant*>(variant.gobj()), "i", &gameCount);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -158,7 +158,7 @@ void Gamemode::prepareForSleep_cb(const Glib::RefPtr<Gio::DBus::Connection>& con
|
||||
const Glib::VariantContainerBase& parameters) {
|
||||
if (parameters.is_of_type(Glib::VariantType("(b)"))) {
|
||||
gboolean sleeping;
|
||||
g_variant_get(parameters.gobj_copy(), "(b)", &sleeping);
|
||||
g_variant_get(const_cast<GVariant*>(parameters.gobj()), "(b)", &sleeping);
|
||||
if (!sleeping) {
|
||||
getData();
|
||||
dp.emit();
|
||||
@ -212,7 +212,7 @@ auto Gamemode::update() -> void {
|
||||
// Tooltip
|
||||
if (tooltip) {
|
||||
std::string text = fmt::format(fmt::runtime(tooltip_format), fmt::arg("count", gameCount));
|
||||
box_.set_tooltip_text(text);
|
||||
box_.set_tooltip_markup(text);
|
||||
}
|
||||
|
||||
// Label format
|
||||
|
||||
@ -60,7 +60,7 @@ auto Submap::update() -> void {
|
||||
} else {
|
||||
label_.set_markup(fmt::format(fmt::runtime(format_), submap_));
|
||||
if (tooltipEnabled()) {
|
||||
label_.set_tooltip_text(submap_);
|
||||
label_.set_tooltip_markup(submap_);
|
||||
}
|
||||
event_box_.show();
|
||||
}
|
||||
|
||||
@ -72,13 +72,13 @@ auto Window::update() -> void {
|
||||
tooltip_format = config_["tooltip-format"].asString();
|
||||
}
|
||||
if (!tooltip_format.empty()) {
|
||||
label_.set_tooltip_text(
|
||||
label_.set_tooltip_markup(
|
||||
fmt::format(fmt::runtime(tooltip_format), fmt::arg("title", windowName),
|
||||
fmt::arg("initialTitle", windowData_.initial_title),
|
||||
fmt::arg("class", windowData_.class_name),
|
||||
fmt::arg("initialClass", windowData_.initial_class_name)));
|
||||
} else if (!label_text.empty()) {
|
||||
label_.set_tooltip_text(label_text);
|
||||
label_.set_tooltip_markup(label_text);
|
||||
}
|
||||
}
|
||||
|
||||
@ -237,9 +237,7 @@ void Window::queryActiveWorkspace() {
|
||||
}
|
||||
}
|
||||
|
||||
void Window::onEvent(const std::string& ev) {
|
||||
dp.emit();
|
||||
}
|
||||
void Window::onEvent(const std::string& ev) { dp.emit(); }
|
||||
|
||||
void Window::setClass(const std::string& classname, bool enable) {
|
||||
if (enable) {
|
||||
|
||||
@ -38,7 +38,7 @@ WindowCount::~WindowCount() {
|
||||
|
||||
auto WindowCount::update() -> void {
|
||||
std::lock_guard<std::mutex> lg(mutex_);
|
||||
|
||||
|
||||
queryActiveWorkspace();
|
||||
|
||||
std::string format = config_["format"].asString();
|
||||
@ -58,7 +58,7 @@ auto WindowCount::update() -> void {
|
||||
} else if (!format.empty()) {
|
||||
label_.set_markup(fmt::format(fmt::runtime(format), workspace_.windows));
|
||||
} else {
|
||||
label_.set_text(fmt::format("{}", workspace_.windows));
|
||||
label_.set_markup(fmt::format("{}", workspace_.windows));
|
||||
}
|
||||
|
||||
label_.show();
|
||||
@ -125,9 +125,7 @@ void WindowCount::queryActiveWorkspace() {
|
||||
}
|
||||
}
|
||||
|
||||
void WindowCount::onEvent(const std::string& ev) {
|
||||
dp.emit();
|
||||
}
|
||||
void WindowCount::onEvent(const std::string& ev) { dp.emit(); }
|
||||
|
||||
void WindowCount::setClass(const std::string& classname, bool enable) {
|
||||
if (enable) {
|
||||
|
||||
@ -300,7 +300,7 @@ void Workspace::updateTaskbar(const std::string& workspace_icon) {
|
||||
}
|
||||
|
||||
auto window_box = Gtk::make_managed<Gtk::Box>(Gtk::ORIENTATION_HORIZONTAL);
|
||||
window_box->set_tooltip_text(window_repr.window_title);
|
||||
window_box->set_tooltip_markup(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");
|
||||
|
||||
@ -43,6 +43,13 @@ void Workspaces::init() {
|
||||
m_activeWorkspaceId = m_ipc.getSocket1JsonReply("activeworkspace")["id"].asInt();
|
||||
|
||||
initializeWorkspaces();
|
||||
|
||||
if (barScroll()) {
|
||||
auto& window = const_cast<Bar&>(m_bar).window;
|
||||
window.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK);
|
||||
window.signal_scroll_event().connect(sigc::mem_fun(*this, &Workspaces::handleScroll));
|
||||
}
|
||||
|
||||
dp.emit();
|
||||
}
|
||||
|
||||
@ -636,6 +643,7 @@ auto Workspaces::parseConfig(const Json::Value& config) -> void {
|
||||
populateBoolConfig(config, "persistent-only", m_persistentOnly);
|
||||
populateBoolConfig(config, "active-only", m_activeOnly);
|
||||
populateBoolConfig(config, "move-to-monitor", m_moveToMonitor);
|
||||
populateBoolConfig(config, "enable-bar-scroll", m_barScroll);
|
||||
|
||||
m_persistentWorkspaceConfig = config.get("persistent-workspaces", Json::Value());
|
||||
populateSortByConfig(config);
|
||||
@ -1151,4 +1159,31 @@ std::optional<int> Workspaces::parseWorkspaceId(std::string const& workspaceIdSt
|
||||
}
|
||||
}
|
||||
|
||||
bool Workspaces::handleScroll(GdkEventScroll* e) {
|
||||
// Ignore emulated scroll events on window
|
||||
if (gdk_event_get_pointer_emulated((GdkEvent*)e)) {
|
||||
return false;
|
||||
}
|
||||
auto dir = AModule::getScrollDir(e);
|
||||
if (dir == SCROLL_DIR::NONE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (dir == SCROLL_DIR::DOWN || dir == SCROLL_DIR::RIGHT) {
|
||||
if (allOutputs()) {
|
||||
m_ipc.getSocket1Reply("dispatch workspace e+1");
|
||||
} else {
|
||||
m_ipc.getSocket1Reply("dispatch workspace m+1");
|
||||
}
|
||||
} else if (dir == SCROLL_DIR::UP || dir == SCROLL_DIR::LEFT) {
|
||||
if (allOutputs()) {
|
||||
m_ipc.getSocket1Reply("dispatch workspace e-1");
|
||||
} else {
|
||||
m_ipc.getSocket1Reply("dispatch workspace m-1");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace waybar::modules::hyprland
|
||||
|
||||
@ -14,22 +14,27 @@ waybar::modules::Image::Image(const std::string& id, const Json::Value& config)
|
||||
|
||||
size_ = config["size"].asInt();
|
||||
|
||||
interval_ = config_["interval"] == "once"
|
||||
? std::chrono::milliseconds::max()
|
||||
: std::chrono::milliseconds(std::max(
|
||||
1L, // Minimum 1ms due to millisecond precision
|
||||
static_cast<long>(
|
||||
(config_["interval"].isNumeric() ? config_["interval"].asDouble() : 0) *
|
||||
1000)));
|
||||
const auto once = std::chrono::milliseconds::max();
|
||||
if (!config_.isMember("interval") || config_["interval"].isNull() ||
|
||||
config_["interval"] == "once") {
|
||||
interval_ = once;
|
||||
} else if (config_["interval"].isNumeric()) {
|
||||
const auto interval_seconds = config_["interval"].asDouble();
|
||||
if (interval_seconds <= 0) {
|
||||
interval_ = once;
|
||||
} else {
|
||||
interval_ =
|
||||
std::chrono::milliseconds(std::max(1L, // Minimum 1ms due to millisecond precision
|
||||
static_cast<long>(interval_seconds * 1000)));
|
||||
}
|
||||
} else {
|
||||
interval_ = once;
|
||||
}
|
||||
|
||||
if (size_ == 0) {
|
||||
size_ = 16;
|
||||
}
|
||||
|
||||
if (interval_.count() == 0) {
|
||||
interval_ = std::chrono::milliseconds::max();
|
||||
}
|
||||
|
||||
delayWorker();
|
||||
}
|
||||
|
||||
|
||||
@ -123,7 +123,7 @@ auto Inhibitor::update() -> void {
|
||||
label_.get_style_context()->add_class(status_text);
|
||||
|
||||
if (tooltipEnabled()) {
|
||||
label_.set_tooltip_text(status_text);
|
||||
label_.set_tooltip_markup(status_text);
|
||||
}
|
||||
|
||||
return ALabel::update();
|
||||
|
||||
@ -53,7 +53,7 @@ std::string JACK::JACKState() {
|
||||
auto JACK::update() -> void {
|
||||
std::string format;
|
||||
std::string state = JACKState();
|
||||
float latency = 1000 * (float)bufsize_ / (float)samplerate_;
|
||||
float latency = samplerate_ > 0 ? 1000.0f * (float)bufsize_ / (float)samplerate_ : 0.0f;
|
||||
|
||||
if (label_.get_style_context()->has_class("xrun")) {
|
||||
label_.get_style_context()->remove_class("xrun");
|
||||
@ -80,7 +80,7 @@ auto JACK::update() -> void {
|
||||
if (tooltipEnabled()) {
|
||||
std::string tooltip_format = "{bufsize}/{samplerate} {latency}ms";
|
||||
if (config_["tooltip-format"].isString()) tooltip_format = config_["tooltip-format"].asString();
|
||||
label_.set_tooltip_text(fmt::format(
|
||||
label_.set_tooltip_markup(fmt::format(
|
||||
fmt::runtime(tooltip_format), fmt::arg("load", std::round(load_)),
|
||||
fmt::arg("bufsize", bufsize_), fmt::arg("samplerate", samplerate_),
|
||||
fmt::arg("latency", fmt::format("{:.2f}", latency)), fmt::arg("xruns", xruns_)));
|
||||
@ -91,16 +91,19 @@ auto JACK::update() -> void {
|
||||
}
|
||||
|
||||
int JACK::bufSize(jack_nframes_t size) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
bufsize_ = size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int JACK::sampleRate(jack_nframes_t rate) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
samplerate_ = rate;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int JACK::xrun() {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
xruns_ += 1;
|
||||
state_ = "xrun";
|
||||
return 0;
|
||||
|
||||
@ -232,9 +232,12 @@ waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Bar&
|
||||
}
|
||||
tryAddDevice(dev_path);
|
||||
} else if (event->mask & IN_DELETE) {
|
||||
std::lock_guard<std::mutex> lock(devices_mutex_);
|
||||
auto it = libinput_devices_.find(dev_path);
|
||||
if (it != libinput_devices_.end()) {
|
||||
spdlog::info("Keyboard {} has been removed.", dev_path);
|
||||
libinput_path_remove_device(it->second);
|
||||
libinput_device_unref(it->second);
|
||||
libinput_devices_.erase(it);
|
||||
}
|
||||
}
|
||||
@ -245,6 +248,7 @@ waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Bar&
|
||||
}
|
||||
|
||||
waybar::modules::KeyboardState::~KeyboardState() {
|
||||
std::lock_guard<std::mutex> lock(devices_mutex_);
|
||||
for (const auto& [_, dev_ptr] : libinput_devices_) {
|
||||
libinput_path_remove_device(dev_ptr);
|
||||
}
|
||||
@ -256,11 +260,17 @@ auto waybar::modules::KeyboardState::update() -> void {
|
||||
|
||||
try {
|
||||
std::string dev_path;
|
||||
if (config_["device-path"].isString() &&
|
||||
libinput_devices_.find(config_["device-path"].asString()) != libinput_devices_.end()) {
|
||||
dev_path = config_["device-path"].asString();
|
||||
} else {
|
||||
dev_path = libinput_devices_.begin()->first;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(devices_mutex_);
|
||||
if (libinput_devices_.empty()) {
|
||||
return;
|
||||
}
|
||||
if (config_["device-path"].isString() &&
|
||||
libinput_devices_.find(config_["device-path"].asString()) != libinput_devices_.end()) {
|
||||
dev_path = config_["device-path"].asString();
|
||||
} else {
|
||||
dev_path = libinput_devices_.begin()->first;
|
||||
}
|
||||
}
|
||||
int fd = openFile(dev_path, O_NONBLOCK | O_CLOEXEC | O_RDONLY);
|
||||
auto dev = openDevice(fd);
|
||||
@ -308,10 +318,15 @@ auto waybar::modules ::KeyboardState::tryAddDevice(const std::string& dev_path)
|
||||
auto dev = openDevice(fd);
|
||||
if (supportsLockStates(dev)) {
|
||||
spdlog::info("Found device {} at '{}'", libevdev_get_name(dev), dev_path);
|
||||
std::lock_guard<std::mutex> lock(devices_mutex_);
|
||||
if (libinput_devices_.find(dev_path) == libinput_devices_.end()) {
|
||||
auto device = libinput_path_add_device(libinput_, dev_path.c_str());
|
||||
libinput_device_ref(device);
|
||||
libinput_devices_[dev_path] = device;
|
||||
if (device) {
|
||||
libinput_device_ref(device);
|
||||
libinput_devices_[dev_path] = device;
|
||||
} else {
|
||||
spdlog::warn("keyboard-state: Failed to add device to libinput: {}", dev_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
libevdev_free(dev);
|
||||
|
||||
@ -22,7 +22,7 @@ auto waybar::modules::Load::update() -> void {
|
||||
auto [load1, load5, load15] = Load::getLoad();
|
||||
if (tooltipEnabled()) {
|
||||
auto tooltip = fmt::format("Load 1: {}\nLoad 5: {}\nLoad 15: {}", load1, load5, load15);
|
||||
label_.set_tooltip_text(tooltip);
|
||||
label_.set_tooltip_markup(tooltip);
|
||||
}
|
||||
auto format = format_;
|
||||
auto state = getState(load1);
|
||||
|
||||
@ -69,7 +69,7 @@ auto waybar::modules::Memory::update() -> void {
|
||||
if (tooltipEnabled()) {
|
||||
if (config_["tooltip-format"].isString()) {
|
||||
auto tooltip_format = config_["tooltip-format"].asString();
|
||||
label_.set_tooltip_text(fmt::format(
|
||||
label_.set_tooltip_markup(fmt::format(
|
||||
fmt::runtime(tooltip_format), used_ram_percentage,
|
||||
fmt::arg("total", total_ram_gigabytes), fmt::arg("swapTotal", total_swap_gigabytes),
|
||||
fmt::arg("percentage", used_ram_percentage),
|
||||
@ -78,7 +78,7 @@ auto waybar::modules::Memory::update() -> void {
|
||||
fmt::arg("swapUsed", used_swap_gigabytes), fmt::arg("avail", available_ram_gigabytes),
|
||||
fmt::arg("swapAvail", available_swap_gigabytes)));
|
||||
} else {
|
||||
label_.set_tooltip_text(fmt::format("{:.{}f}GiB used", used_ram_gigabytes, 1));
|
||||
label_.set_tooltip_markup(fmt::format("{:.{}f}GiB used", used_ram_gigabytes, 1));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@ -110,7 +110,7 @@ void waybar::modules::MPD::setLabel() {
|
||||
? config_["tooltip-format-disconnected"].asString()
|
||||
: "MPD (disconnected)";
|
||||
// Nothing to format
|
||||
label_.set_tooltip_text(tooltip_format);
|
||||
label_.set_tooltip_markup(tooltip_format);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -210,7 +210,7 @@ void waybar::modules::MPD::setLabel() {
|
||||
fmt::arg("stateIcon", stateIcon), fmt::arg("consumeIcon", consumeIcon),
|
||||
fmt::arg("randomIcon", randomIcon), fmt::arg("repeatIcon", repeatIcon),
|
||||
fmt::arg("singleIcon", singleIcon), fmt::arg("filename", filename), fmt::arg("uri", uri));
|
||||
label_.set_tooltip_text(tooltip_text);
|
||||
label_.set_tooltip_markup(tooltip_text);
|
||||
} catch (fmt::format_error const& e) {
|
||||
spdlog::warn("mpd: format error (tooltip): {}", e.what());
|
||||
}
|
||||
@ -323,6 +323,7 @@ void waybar::modules::MPD::checkErrors(mpd_connection* conn) {
|
||||
case MPD_ERROR_SYSTEM:
|
||||
if (auto ec = mpd_connection_get_system_error(conn); ec != 0) {
|
||||
mpd_connection_clear_error(conn);
|
||||
connection_.reset();
|
||||
throw std::system_error(ec, std::system_category());
|
||||
}
|
||||
G_GNUC_FALLTHROUGH;
|
||||
|
||||
@ -117,7 +117,7 @@ Mpris::Mpris(const std::string& id, const Json::Value& config)
|
||||
}
|
||||
|
||||
GError* error = nullptr;
|
||||
waybar::util::ScopeGuard error_deleter([error]() {
|
||||
waybar::util::ScopeGuard error_deleter([&error]() {
|
||||
if (error) {
|
||||
g_error_free(error);
|
||||
}
|
||||
@ -178,6 +178,7 @@ Mpris::Mpris(const std::string& id, const Json::Value& config)
|
||||
}
|
||||
|
||||
Mpris::~Mpris() {
|
||||
if (last_active_player_ && last_active_player_ != player) g_object_unref(last_active_player_);
|
||||
if (manager != nullptr) g_object_unref(manager);
|
||||
if (player != nullptr) g_object_unref(player);
|
||||
}
|
||||
@ -458,10 +459,7 @@ auto Mpris::onPlayerStop(PlayerctlPlayer* player, gpointer data) -> void {
|
||||
if (!mpris) return;
|
||||
|
||||
spdlog::debug("mpris: player-stop callback");
|
||||
|
||||
// hide widget
|
||||
mpris->event_box_.set_visible(false);
|
||||
// update widget
|
||||
// update widget (update() handles visibility)
|
||||
mpris->dp.emit();
|
||||
}
|
||||
|
||||
@ -480,7 +478,7 @@ auto Mpris::getPlayerInfo() -> std::optional<PlayerInfo> {
|
||||
}
|
||||
|
||||
GError* error = nullptr;
|
||||
waybar::util::ScopeGuard error_deleter([error]() {
|
||||
waybar::util::ScopeGuard error_deleter([&error]() {
|
||||
if (error) {
|
||||
g_error_free(error);
|
||||
}
|
||||
@ -488,7 +486,12 @@ auto Mpris::getPlayerInfo() -> std::optional<PlayerInfo> {
|
||||
|
||||
char* player_status = nullptr;
|
||||
auto player_playback_status = PLAYERCTL_PLAYBACK_STATUS_STOPPED;
|
||||
g_object_get(player, "status", &player_status, "playback-status", &player_playback_status, NULL);
|
||||
|
||||
// Clean up previous fallback player
|
||||
if (last_active_player_ && last_active_player_ != player) {
|
||||
g_object_unref(last_active_player_);
|
||||
last_active_player_ = nullptr;
|
||||
}
|
||||
|
||||
std::string player_name = player_;
|
||||
if (player_name == "playerctld") {
|
||||
@ -498,19 +501,52 @@ auto Mpris::getPlayerInfo() -> std::optional<PlayerInfo> {
|
||||
}
|
||||
// > get the list of players [..] in order of activity
|
||||
// https://github.com/altdesktop/playerctl/blob/b19a71cb9dba635df68d271bd2b3f6a99336a223/playerctl/playerctl-common.c#L248-L249
|
||||
players = g_list_first(players);
|
||||
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(),
|
||||
[&](const std::string& pn) { return player_name == pn; })) {
|
||||
PlayerctlPlayer* first_valid_player = nullptr;
|
||||
std::string first_valid_name;
|
||||
for (auto* p = g_list_first(players); p != nullptr; p = p->next) {
|
||||
auto* pn = static_cast<PlayerctlPlayerName*>(p->data);
|
||||
std::string name = pn->name;
|
||||
if (std::any_of(ignored_players_.begin(), ignored_players_.end(),
|
||||
[&](const std::string& ignored) { return name == ignored; })) {
|
||||
spdlog::warn("mpris[{}]: ignoring player update", name);
|
||||
continue;
|
||||
}
|
||||
auto* tmp = playerctl_player_new_from_name(pn, &error);
|
||||
if (error || !tmp) continue;
|
||||
if (!first_valid_player) {
|
||||
first_valid_player = tmp;
|
||||
first_valid_name = name;
|
||||
}
|
||||
PlayerctlPlaybackStatus status;
|
||||
g_object_get(tmp, "playback-status", &status, NULL);
|
||||
if (status == PLAYERCTL_PLAYBACK_STATUS_PLAYING) {
|
||||
if (tmp != first_valid_player) g_object_unref(first_valid_player);
|
||||
last_active_player_ = tmp;
|
||||
player_name = name;
|
||||
break;
|
||||
}
|
||||
if (tmp != first_valid_player) g_object_unref(tmp);
|
||||
}
|
||||
if (!last_active_player_) {
|
||||
if (!first_valid_player) return std::nullopt;
|
||||
last_active_player_ = first_valid_player;
|
||||
player_name = first_valid_name;
|
||||
}
|
||||
} else if (std::any_of(ignored_players_.begin(), ignored_players_.end(),
|
||||
[&](const std::string& pn) { return player_name == pn; })) {
|
||||
spdlog::warn("mpris[{}]: ignoring player update", player_name);
|
||||
return std::nullopt;
|
||||
} else {
|
||||
last_active_player_ = player;
|
||||
}
|
||||
|
||||
g_object_get(last_active_player_, "status", &player_status, "playback-status",
|
||||
&player_playback_status, NULL);
|
||||
|
||||
if (!player_status) {
|
||||
spdlog::error("mpris: failed to get player status");
|
||||
return std::nullopt;
|
||||
}
|
||||
// make status lowercase
|
||||
player_status[0] = std::tolower(player_status[0]);
|
||||
|
||||
@ -524,28 +560,29 @@ auto Mpris::getPlayerInfo() -> std::optional<PlayerInfo> {
|
||||
.length = std::nullopt,
|
||||
};
|
||||
|
||||
if (auto* artist_ = playerctl_player_get_artist(player, &error)) {
|
||||
if (auto* artist_ = playerctl_player_get_artist(last_active_player_, &error)) {
|
||||
spdlog::debug("mpris[{}]: artist = {}", info.name, artist_);
|
||||
info.artist = artist_;
|
||||
g_free(artist_);
|
||||
}
|
||||
if (error) goto errorexit;
|
||||
|
||||
if (auto* album_ = playerctl_player_get_album(player, &error)) {
|
||||
if (auto* album_ = playerctl_player_get_album(last_active_player_, &error)) {
|
||||
spdlog::debug("mpris[{}]: album = {}", info.name, album_);
|
||||
info.album = album_;
|
||||
g_free(album_);
|
||||
}
|
||||
if (error) goto errorexit;
|
||||
|
||||
if (auto* title_ = playerctl_player_get_title(player, &error)) {
|
||||
if (auto* title_ = playerctl_player_get_title(last_active_player_, &error)) {
|
||||
spdlog::debug("mpris[{}]: title = {}", info.name, title_);
|
||||
info.title = title_;
|
||||
g_free(title_);
|
||||
}
|
||||
if (error) goto errorexit;
|
||||
|
||||
if (auto* length_ = playerctl_player_print_metadata_prop(player, "mpris:length", &error)) {
|
||||
if (auto* length_ =
|
||||
playerctl_player_print_metadata_prop(last_active_player_, "mpris:length", &error)) {
|
||||
spdlog::debug("mpris[{}]: mpris:length = {}", info.name, length_);
|
||||
auto len = std::chrono::microseconds(std::strtol(length_, nullptr, 10));
|
||||
auto len_h = std::chrono::duration_cast<std::chrono::hours>(len);
|
||||
@ -557,7 +594,7 @@ auto Mpris::getPlayerInfo() -> std::optional<PlayerInfo> {
|
||||
if (error) goto errorexit;
|
||||
|
||||
{
|
||||
auto position_ = playerctl_player_get_position(player, &error);
|
||||
auto position_ = playerctl_player_get_position(last_active_player_, &error);
|
||||
if (error) {
|
||||
// it's fine to have an error here because not all players report a position
|
||||
g_error_free(error);
|
||||
@ -609,12 +646,13 @@ bool Mpris::handleToggle(GdkEventButton* const& e) {
|
||||
});
|
||||
|
||||
// Command pattern: encapsulate each button's action
|
||||
auto* target = last_active_player_ ? last_active_player_ : player;
|
||||
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); }},
|
||||
{1, "on-click", [&]() { playerctl_player_play_pause(target, &error); }},
|
||||
{2, "on-click-middle", [&]() { playerctl_player_previous(target, &error); }},
|
||||
{3, "on-click-right", [&]() { playerctl_player_next(target, &error); }},
|
||||
{8, "on-click-backward", [&]() { playerctl_player_previous(target, &error); }},
|
||||
{9, "on-click-forward", [&]() { playerctl_player_next(target, &error); }},
|
||||
};
|
||||
|
||||
for (const auto& action : actions) {
|
||||
@ -734,7 +772,7 @@ auto Mpris::update() -> void {
|
||||
fmt::arg("player_icon", getIconFromJson(config_["player-icons"], info.name)),
|
||||
fmt::arg("status_icon", getIconFromJson(config_["status-icons"], info.status_string)));
|
||||
|
||||
label_.set_tooltip_text(tooltip_text);
|
||||
label_.set_tooltip_markup(tooltip_text);
|
||||
} catch (fmt::format_error const& e) {
|
||||
spdlog::warn("mpris: format error (tooltip): {}", e.what());
|
||||
}
|
||||
|
||||
@ -45,7 +45,8 @@ waybar::modules::Network::readBandwidthUsage() {
|
||||
std::istringstream iss(line);
|
||||
|
||||
std::string ifacename;
|
||||
iss >> ifacename; // ifacename contains "eth0:"
|
||||
iss >> ifacename; // ifacename contains "eth0:"
|
||||
if (ifacename.empty()) continue;
|
||||
ifacename.pop_back(); // remove trailing ':'
|
||||
if (ifacename != ifname_) {
|
||||
continue;
|
||||
|
||||
@ -20,12 +20,13 @@
|
||||
|
||||
namespace waybar::modules::niri {
|
||||
|
||||
IPC::IPC() { startIPC(); }
|
||||
|
||||
int IPC::connectToSocket() {
|
||||
const char* socket_path = getenv("NIRI_SOCKET");
|
||||
|
||||
if (socket_path == nullptr) {
|
||||
spdlog::warn("Niri is not running, niri IPC will not be available.");
|
||||
return -1;
|
||||
throw std::runtime_error("Niri IPC: NIRI_SOCKET was not set! (Is Niri running?)");
|
||||
}
|
||||
|
||||
struct sockaddr_un addr;
|
||||
@ -54,16 +55,9 @@ int IPC::connectToSocket() {
|
||||
void IPC::startIPC() {
|
||||
// will start IPC and relay events to parseIPC
|
||||
|
||||
std::thread([&]() {
|
||||
int socketfd;
|
||||
try {
|
||||
socketfd = connectToSocket();
|
||||
} catch (std::exception& e) {
|
||||
spdlog::error("Niri IPC: failed to start, reason: {}", e.what());
|
||||
return;
|
||||
}
|
||||
if (socketfd == -1) return;
|
||||
int socketfd = connectToSocket();
|
||||
|
||||
std::thread([this, socketfd]() {
|
||||
spdlog::info("Niri IPC starting");
|
||||
|
||||
auto unix_istream = Gio::UnixInputStream::create(socketfd, true);
|
||||
@ -242,7 +236,6 @@ void IPC::unregisterForIPC(EventHandler* ev_handler) {
|
||||
|
||||
Json::Value IPC::send(const Json::Value& request) {
|
||||
int socketfd = connectToSocket();
|
||||
if (socketfd == -1) throw std::runtime_error("Niri is not running");
|
||||
|
||||
auto unix_istream = Gio::UnixInputStream::create(socketfd, true);
|
||||
auto unix_ostream = Gio::UnixOutputStream::create(socketfd, false);
|
||||
|
||||
@ -67,7 +67,7 @@ void Window::doUpdate() {
|
||||
|
||||
updateAppIconName(appId, "");
|
||||
|
||||
if (tooltipEnabled()) label_.set_tooltip_text(title);
|
||||
if (tooltipEnabled()) label_.set_tooltip_markup(title);
|
||||
|
||||
const auto id = window["id"].asUInt64();
|
||||
const auto workspaceId = window["workspace_id"].asUInt64();
|
||||
|
||||
@ -174,11 +174,11 @@ std::string Workspaces::getIcon(const std::string& value, const Json::Value& ws)
|
||||
|
||||
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_active"].asBool() && icons["active"]) return icons["active"].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["active_window_id"].isNull() && icons["empty"]) return icons["empty"].asString();
|
||||
|
||||
if (ws["name"]) {
|
||||
const auto& name = ws["name"].asString();
|
||||
|
||||
@ -157,7 +157,7 @@ auto PowerProfilesDaemon::update() -> void {
|
||||
store.push_back(fmt::arg("icon", getIcon(0, profile.name)));
|
||||
label_.set_markup(fmt::vformat(format_, store));
|
||||
if (tooltipEnabled()) {
|
||||
label_.set_tooltip_text(fmt::vformat(tooltipFormat_, store));
|
||||
label_.set_tooltip_markup(fmt::vformat(tooltipFormat_, store));
|
||||
}
|
||||
|
||||
// Set CSS class
|
||||
@ -176,6 +176,10 @@ auto PowerProfilesDaemon::update() -> void {
|
||||
|
||||
bool PowerProfilesDaemon::handleToggle(GdkEventButton* const& e) {
|
||||
if (e->type == GdkEventType::GDK_BUTTON_PRESS && connected_) {
|
||||
if (availableProfiles_.empty()) return true;
|
||||
if (activeProfile_ == availableProfiles_.end()) {
|
||||
activeProfile_ = availableProfiles_.begin();
|
||||
}
|
||||
if (e->button == 1) /* left click */ {
|
||||
activeProfile_++;
|
||||
if (activeProfile_ == availableProfiles_.end()) {
|
||||
|
||||
@ -138,7 +138,7 @@ auto waybar::modules::Pulseaudio::update() -> void {
|
||||
fmt::arg("source_volume", source_volume), fmt::arg("source_desc", source_desc),
|
||||
fmt::arg("icon", getIcon(sink_volume, getPulseIcon()))));
|
||||
} else {
|
||||
label_.set_tooltip_text(sink_desc);
|
||||
label_.set_tooltip_markup(sink_desc);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -118,6 +118,7 @@ Layout::Layout(const std::string& id, const waybar::Bar& bar, const Json::Value&
|
||||
|
||||
if (!seat_) {
|
||||
spdlog::error("wl_seat not advertised");
|
||||
return;
|
||||
}
|
||||
|
||||
label_.hide();
|
||||
|
||||
@ -77,6 +77,7 @@ Mode::Mode(const std::string& id, const waybar::Bar& bar, const Json::Value& con
|
||||
|
||||
if (!seat_) {
|
||||
spdlog::error("wl_seat not advertised");
|
||||
return;
|
||||
}
|
||||
|
||||
if (config_["hidden-modes"].isArray()) {
|
||||
|
||||
@ -141,10 +141,12 @@ Tags::Tags(const std::string& id, const waybar::Bar& bar, const Json::Value& con
|
||||
|
||||
if (!control_) {
|
||||
spdlog::error("river_control_v1 not advertised");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!seat_) {
|
||||
spdlog::error("wl_seat not advertised");
|
||||
return;
|
||||
}
|
||||
|
||||
seat_status_ = zriver_status_manager_v1_get_river_seat_status(status_manager_, seat_);
|
||||
@ -217,6 +219,7 @@ Tags::~Tags() {
|
||||
}
|
||||
|
||||
void Tags::handle_show() {
|
||||
if (!status_manager_) return;
|
||||
struct wl_output* output = gdk_wayland_monitor_get_wl_output(bar_.output->monitor->gobj());
|
||||
output_status_ = zriver_status_manager_v1_get_river_output_status(status_manager_, output);
|
||||
zriver_output_status_v1_add_listener(output_status_, &output_status_listener_impl, this);
|
||||
|
||||
@ -82,6 +82,7 @@ Window::Window(const std::string& id, const waybar::Bar& bar, const Json::Value&
|
||||
|
||||
if (!seat_) {
|
||||
spdlog::error("wl_seat not advertised");
|
||||
return;
|
||||
}
|
||||
|
||||
label_.hide(); // hide the label until populated
|
||||
|
||||
@ -25,9 +25,9 @@ auto waybar::modules::Clock::update() -> void {
|
||||
if (config_["tooltip-format"].isString()) {
|
||||
auto tooltip_format = config_["tooltip-format"].asString();
|
||||
auto tooltip_text = fmt::format(fmt::runtime(tooltip_format), localtime);
|
||||
label_.set_tooltip_text(tooltip_text);
|
||||
label_.set_tooltip_markup(tooltip_text);
|
||||
} else {
|
||||
label_.set_tooltip_text(text);
|
||||
label_.set_tooltip_markup(text);
|
||||
}
|
||||
}
|
||||
// Call parent update
|
||||
|
||||
@ -102,7 +102,9 @@ Sndio::~Sndio() { sioctl_close(hdl_); }
|
||||
|
||||
auto Sndio::update() -> void {
|
||||
auto format = format_;
|
||||
unsigned int vol = 100. * static_cast<double>(volume_) / static_cast<double>(maxval_);
|
||||
unsigned int vol = (maxval_ > 0) ? static_cast<unsigned int>(100. * static_cast<double>(volume_) /
|
||||
static_cast<double>(maxval_))
|
||||
: 0;
|
||||
|
||||
if (volume_ == 0) {
|
||||
label_.get_style_context()->add_class("muted");
|
||||
|
||||
@ -59,7 +59,7 @@ void Host::nameVanished(const Glib::RefPtr<Gio::DBus::Connection>& conn, const G
|
||||
|
||||
void Host::proxyReady(GObject* src, GAsyncResult* res, gpointer data) {
|
||||
GError* error = nullptr;
|
||||
waybar::util::ScopeGuard error_deleter([error]() {
|
||||
waybar::util::ScopeGuard error_deleter([&error]() {
|
||||
if (error != nullptr) {
|
||||
g_error_free(error);
|
||||
}
|
||||
@ -81,7 +81,7 @@ void Host::proxyReady(GObject* src, GAsyncResult* res, gpointer data) {
|
||||
|
||||
void Host::registerHost(GObject* src, GAsyncResult* res, gpointer data) {
|
||||
GError* error = nullptr;
|
||||
waybar::util::ScopeGuard error_deleter([error]() {
|
||||
waybar::util::ScopeGuard error_deleter([&error]() {
|
||||
if (error != nullptr) {
|
||||
g_error_free(error);
|
||||
}
|
||||
|
||||
@ -365,13 +365,14 @@ Glib::RefPtr<Gdk::Pixbuf> Item::extractPixBuf(GVariant* variant) {
|
||||
|
||||
void Item::updateImage() {
|
||||
auto pixbuf = getIconPixbuf();
|
||||
if (!pixbuf) return;
|
||||
auto scaled_icon_size = getScaledIconSize();
|
||||
|
||||
// If the loaded icon is not square, assume that the icon height should match the
|
||||
// requested icon size, but the width is allowed to be different. As such, if the
|
||||
// height of the image does not match the requested icon size, resize the icon such that
|
||||
// the aspect ratio is maintained, but the height matches the requested icon size.
|
||||
if (pixbuf->get_height() != scaled_icon_size) {
|
||||
if (pixbuf->get_height() > 0 && pixbuf->get_height() != scaled_icon_size) {
|
||||
int width = scaled_icon_size * pixbuf->get_width() / pixbuf->get_height();
|
||||
pixbuf = pixbuf->scale_simple(width, scaled_icon_size, Gdk::InterpType::INTERP_BILINEAR);
|
||||
}
|
||||
|
||||
@ -31,7 +31,7 @@ Watcher::~Watcher() {
|
||||
|
||||
void Watcher::busAcquired(const Glib::RefPtr<Gio::DBus::Connection>& conn, Glib::ustring name) {
|
||||
GError* error = nullptr;
|
||||
waybar::util::ScopeGuard error_deleter([error]() {
|
||||
waybar::util::ScopeGuard error_deleter([&error]() {
|
||||
if (error) {
|
||||
g_error_free(error);
|
||||
}
|
||||
|
||||
@ -44,7 +44,7 @@ auto Mode::update() -> void {
|
||||
} else {
|
||||
label_.set_markup(fmt::format(fmt::runtime(format_), mode_));
|
||||
if (tooltipEnabled()) {
|
||||
label_.set_tooltip_text(mode_);
|
||||
label_.set_tooltip_markup(mode_);
|
||||
}
|
||||
event_box_.show();
|
||||
}
|
||||
|
||||
@ -99,7 +99,7 @@ auto Window::update() -> void {
|
||||
fmt::arg("shell", shell_), fmt::arg("marks", marks_)),
|
||||
config_["rewrite"]));
|
||||
if (tooltipEnabled()) {
|
||||
label_.set_tooltip_text(window_);
|
||||
label_.set_tooltip_markup(window_);
|
||||
}
|
||||
|
||||
updateAppIcon();
|
||||
|
||||
@ -77,9 +77,9 @@ void SystemdFailedUnits::RequestSystemState() {
|
||||
Glib::VariantContainerBase data = proxy->call_sync("Get", parameters);
|
||||
if (data && data.is_of_type(Glib::VariantType("(v)"))) {
|
||||
Glib::VariantBase variant;
|
||||
g_variant_get(data.gobj_copy(), "(v)", &variant);
|
||||
g_variant_get(const_cast<GVariant*>(data.gobj()), "(v)", &variant);
|
||||
if (variant && variant.is_of_type(Glib::VARIANT_TYPE_STRING)) {
|
||||
return g_variant_get_string(variant.gobj_copy(), NULL);
|
||||
return g_variant_get_string(const_cast<GVariant*>(variant.gobj()), NULL);
|
||||
}
|
||||
}
|
||||
} catch (Glib::Error& e) {
|
||||
@ -105,9 +105,9 @@ void SystemdFailedUnits::RequestFailedUnits() {
|
||||
Glib::VariantContainerBase data = proxy->call_sync("Get", parameters);
|
||||
if (data && data.is_of_type(Glib::VariantType("(v)"))) {
|
||||
Glib::VariantBase variant;
|
||||
g_variant_get(data.gobj_copy(), "(v)", &variant);
|
||||
g_variant_get(const_cast<GVariant*>(data.gobj()), "(v)", &variant);
|
||||
if (variant && variant.is_of_type(Glib::VARIANT_TYPE_UINT32)) {
|
||||
return g_variant_get_uint32(variant.gobj_copy());
|
||||
return g_variant_get_uint32(const_cast<GVariant*>(variant.gobj()));
|
||||
}
|
||||
}
|
||||
} catch (Glib::Error& e) {
|
||||
|
||||
@ -101,7 +101,7 @@ auto waybar::modules::Temperature::update() -> void {
|
||||
if (config_["tooltip-format"].isString()) {
|
||||
tooltip_format = config_["tooltip-format"].asString();
|
||||
}
|
||||
label_.set_tooltip_text(fmt::format(
|
||||
label_.set_tooltip_markup(fmt::format(
|
||||
fmt::runtime(tooltip_format), fmt::arg("temperatureC", temperature_c),
|
||||
fmt::arg("temperatureF", temperature_f), fmt::arg("temperatureK", temperature_k)));
|
||||
}
|
||||
|
||||
@ -59,10 +59,13 @@ UPower::UPower(const std::string& id, const Json::Value& config)
|
||||
sigc::mem_fun(*this, &UPower::getConn_cb));
|
||||
|
||||
// Make UPower client
|
||||
GError** gErr = NULL;
|
||||
upClient_ = up_client_new_full(NULL, gErr);
|
||||
if (upClient_ == NULL)
|
||||
spdlog::error("Upower. UPower client connection error. {}", (*gErr)->message);
|
||||
GError* gErr = NULL;
|
||||
upClient_ = up_client_new_full(NULL, &gErr);
|
||||
if (upClient_ == NULL) {
|
||||
spdlog::error("Upower. UPower client connection error. {}",
|
||||
gErr ? gErr->message : "unknown error");
|
||||
if (gErr) g_error_free(gErr);
|
||||
}
|
||||
|
||||
// Subscribe UPower events
|
||||
g_signal_connect(upClient_, "device-added", G_CALLBACK(deviceAdded_cb), this);
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
#include <bit>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <exception>
|
||||
#include <ranges>
|
||||
#include <thread>
|
||||
@ -32,7 +33,13 @@ auto pack_and_write(Sock& sock, std::string&& buf) -> void {
|
||||
|
||||
auto read_exact(Sock& sock, size_t n) -> std::string {
|
||||
auto buf = std::string(n, 0);
|
||||
for (size_t i = 0; i < n;) i += read(sock.fd, &buf[i], n - i);
|
||||
for (size_t i = 0; i < n;) {
|
||||
auto r = read(sock.fd, &buf[i], n - i);
|
||||
if (r <= 0) {
|
||||
throw std::runtime_error("Wayfire IPC: read failed");
|
||||
}
|
||||
i += static_cast<size_t>(r);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
@ -53,6 +60,9 @@ auto State::Wset::locate_ws(const Json::Value& geo) -> Workspace& {
|
||||
}
|
||||
|
||||
auto State::Wset::locate_ws(const Json::Value& geo) const -> const Workspace& {
|
||||
if (!output.has_value()) {
|
||||
throw std::runtime_error("Wayfire IPC: wset has no output assigned");
|
||||
}
|
||||
const auto& out = output.value().get();
|
||||
auto [qx, rx] = std::div(geo["x"].asInt(), out.w);
|
||||
auto [qy, ry] = std::div(geo["y"].asInt(), out.h);
|
||||
@ -88,7 +98,10 @@ auto State::update_view(const Json::Value& view) -> void {
|
||||
|
||||
auto IPC::get_instance() -> std::shared_ptr<IPC> {
|
||||
auto p = instance.lock();
|
||||
if (!p) instance = p = std::shared_ptr<IPC>(new IPC);
|
||||
if (!p) {
|
||||
instance = p = std::shared_ptr<IPC>(new IPC);
|
||||
p->start();
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
@ -116,7 +129,9 @@ auto IPC::connect() -> Sock {
|
||||
}
|
||||
|
||||
auto IPC::receive(Sock& sock) -> Json::Value {
|
||||
auto len = *reinterpret_cast<uint32_t*>(read_exact(sock, 4).data());
|
||||
auto len_buf = read_exact(sock, 4);
|
||||
uint32_t len;
|
||||
std::memcpy(&len, len_buf.data(), sizeof(len));
|
||||
if constexpr (std::endian::native != std::endian::little) len = byteswap(len);
|
||||
auto buf = read_exact(sock, len);
|
||||
|
||||
@ -153,15 +168,15 @@ auto IPC::start() -> void {
|
||||
send("window-rules/get-focused-view", {});
|
||||
send("window-rules/get-focused-output", {});
|
||||
|
||||
std::thread([&] {
|
||||
std::thread([self = shared_from_this()] {
|
||||
auto sock = connect();
|
||||
|
||||
{
|
||||
Json::Value json;
|
||||
json["method"] = "window-rules/events/watch";
|
||||
|
||||
pack_and_write(sock, Json::writeString(writer_builder, json));
|
||||
if (receive(sock)["result"] != "ok") {
|
||||
pack_and_write(sock, Json::writeString(self->writer_builder, json));
|
||||
if (self->receive(sock)["result"] != "ok") {
|
||||
spdlog::error(
|
||||
"Wayfire IPC: method \"window-rules/events/watch\""
|
||||
" have failed");
|
||||
@ -169,10 +184,10 @@ auto IPC::start() -> void {
|
||||
}
|
||||
}
|
||||
|
||||
while (auto json = receive(sock)) {
|
||||
while (auto json = self->receive(sock)) {
|
||||
auto ev = json["event"].asString();
|
||||
spdlog::debug("Wayfire IPC: received event \"{}\"", ev);
|
||||
root_event_handler(ev, json);
|
||||
self->root_event_handler(ev, json);
|
||||
}
|
||||
}).detach();
|
||||
}
|
||||
|
||||
@ -34,8 +34,12 @@ auto Window::update() -> void {
|
||||
auto Window::update_icon_label() -> void {
|
||||
auto _ = ipc->lock_state();
|
||||
|
||||
const auto& output = ipc->get_outputs().at(bar_.output->name);
|
||||
const auto& wset = ipc->get_wsets().at(output.wset_idx);
|
||||
auto out_it = ipc->get_outputs().find(bar_.output->name);
|
||||
if (out_it == ipc->get_outputs().end()) return;
|
||||
const auto& output = out_it->second;
|
||||
auto wset_it = ipc->get_wsets().find(output.wset_idx);
|
||||
if (wset_it == ipc->get_wsets().end()) return;
|
||||
const auto& wset = wset_it->second;
|
||||
const auto& views = ipc->get_views();
|
||||
auto ctx = bar_.window.get_style_context();
|
||||
|
||||
|
||||
@ -70,8 +70,12 @@ auto Workspaces::handleScroll(GdkEventScroll* e) -> bool {
|
||||
Json::Value data;
|
||||
{
|
||||
auto _ = ipc->lock_state();
|
||||
const auto& output = ipc->get_outputs().at(bar_.output->name);
|
||||
const auto& wset = ipc->get_wsets().at(output.wset_idx);
|
||||
auto out_it = ipc->get_outputs().find(bar_.output->name);
|
||||
if (out_it == ipc->get_outputs().end()) return true;
|
||||
const auto& output = out_it->second;
|
||||
auto wset_it = ipc->get_wsets().find(output.wset_idx);
|
||||
if (wset_it == ipc->get_wsets().end()) return true;
|
||||
const auto& wset = wset_it->second;
|
||||
auto n = wset.ws_w * wset.ws_h;
|
||||
auto i = (wset.ws_idx() + delta + n) % n;
|
||||
data["x"] = Json::Value((uint64_t)i % wset.ws_w);
|
||||
@ -92,8 +96,12 @@ auto Workspaces::update_box() -> void {
|
||||
auto _ = ipc->lock_state();
|
||||
|
||||
const auto& output_name = bar_.output->name;
|
||||
const auto& output = ipc->get_outputs().at(output_name);
|
||||
const auto& wset = ipc->get_wsets().at(output.wset_idx);
|
||||
auto out_it = ipc->get_outputs().find(output_name);
|
||||
if (out_it == ipc->get_outputs().end()) return;
|
||||
const auto& output = out_it->second;
|
||||
auto wset_it = ipc->get_wsets().find(output.wset_idx);
|
||||
if (wset_it == ipc->get_wsets().end()) return;
|
||||
const auto& wset = wset_it->second;
|
||||
|
||||
auto output_focused = ipc->get_focused_output_name() == output_name;
|
||||
auto ws_w = wset.ws_w;
|
||||
|
||||
@ -81,7 +81,7 @@ void waybar::modules::Wireplumber::updateNodeName(waybar::modules::Wireplumber*
|
||||
if (proxy == nullptr) {
|
||||
auto err = fmt::format("Object '{}' not found\n", id);
|
||||
spdlog::error("[{}]: {}", self->name_, err);
|
||||
throw std::runtime_error(err);
|
||||
return;
|
||||
}
|
||||
|
||||
g_autoptr(WpProperties) properties =
|
||||
@ -153,7 +153,7 @@ void waybar::modules::Wireplumber::updateVolume(waybar::modules::Wireplumber* se
|
||||
if (variant == nullptr) {
|
||||
auto err = fmt::format("Node {} does not support volume\n", id);
|
||||
spdlog::error("[{}]: {}", self->name_, err);
|
||||
throw std::runtime_error(err);
|
||||
return;
|
||||
}
|
||||
|
||||
g_variant_lookup(variant, "volume", "d", &self->volume_);
|
||||
@ -287,14 +287,14 @@ void waybar::modules::Wireplumber::onObjectManagerInstalled(waybar::modules::Wir
|
||||
|
||||
if (self->def_nodes_api_ == nullptr) {
|
||||
spdlog::error("[{}]: default nodes api is not loaded.", self->name_);
|
||||
throw std::runtime_error("Default nodes API is not loaded\n");
|
||||
return;
|
||||
}
|
||||
|
||||
self->mixer_api_ = wp_plugin_find(self->wp_core_, "mixer-api");
|
||||
|
||||
if (self->mixer_api_ == nullptr) {
|
||||
spdlog::error("[{}]: mixer api is not loaded.", self->name_);
|
||||
throw std::runtime_error("Mixer api is not loaded\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get default sink
|
||||
@ -336,7 +336,7 @@ void waybar::modules::Wireplumber::onPluginActivated(WpObject* p, GAsyncResult*
|
||||
|
||||
if (wp_object_activate_finish(p, res, &error) == 0) {
|
||||
spdlog::error("[{}]: error activating plugin: {}", self->name_, error->message);
|
||||
throw std::runtime_error(error->message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (--self->pending_plugins_ == 0) {
|
||||
@ -373,7 +373,7 @@ void waybar::modules::Wireplumber::onDefaultNodesApiLoaded(WpObject* p, GAsyncRe
|
||||
|
||||
if (success == FALSE) {
|
||||
spdlog::error("[{}]: default nodes API load failed", self->name_);
|
||||
throw std::runtime_error(error->message);
|
||||
return;
|
||||
}
|
||||
spdlog::debug("[{}]: loaded default nodes api", self->name_);
|
||||
g_ptr_array_add(self->apis_, wp_plugin_find(self->wp_core_, "default-nodes-api"));
|
||||
@ -392,7 +392,7 @@ void waybar::modules::Wireplumber::onMixerApiLoaded(WpObject* p, GAsyncResult* r
|
||||
|
||||
if (success == FALSE) {
|
||||
spdlog::error("[{}]: mixer API load failed", self->name_);
|
||||
throw std::runtime_error(error->message);
|
||||
return;
|
||||
}
|
||||
|
||||
spdlog::debug("[{}]: loaded mixer API", self->name_);
|
||||
@ -479,12 +479,12 @@ auto waybar::modules::Wireplumber::update() -> void {
|
||||
}
|
||||
|
||||
if (!tooltipFormat.empty()) {
|
||||
label_.set_tooltip_text(fmt::format(
|
||||
label_.set_tooltip_markup(fmt::format(
|
||||
fmt::runtime(tooltipFormat), fmt::arg("node_name", node_name_), fmt::arg("volume", vol),
|
||||
fmt::arg("icon", getIcon(vol)), fmt::arg("format_source", formatted_source),
|
||||
fmt::arg("source_volume", source_vol), fmt::arg("source_desc", source_name_)));
|
||||
} else {
|
||||
label_.set_tooltip_text(node_name_);
|
||||
label_.set_tooltip_markup(node_name_);
|
||||
}
|
||||
}
|
||||
|
||||
@ -524,6 +524,7 @@ bool waybar::modules::Wireplumber::handleScroll(GdkEventScroll* e) {
|
||||
}
|
||||
}
|
||||
if (newVol != volume_) {
|
||||
if (mixer_api_ == nullptr) return true;
|
||||
GVariant* variant = g_variant_new_double(newVol);
|
||||
gboolean ret;
|
||||
g_signal_emit_by_name(mixer_api_, "set-volume", node_id_, variant, &ret);
|
||||
|
||||
@ -437,7 +437,9 @@ void Task::handle_drag_data_get(const Glib::RefPtr<Gdk::DragContext>& context,
|
||||
void Task::handle_drag_data_received(const Glib::RefPtr<Gdk::DragContext>& context, int x, int y,
|
||||
Gtk::SelectionData selection_data, guint info, guint time) {
|
||||
spdlog::debug("drag_data_received");
|
||||
gpointer handle = *(gpointer*)selection_data.get_data();
|
||||
auto* raw = selection_data.get_data();
|
||||
if (!raw || selection_data.get_length() < static_cast<int>(sizeof(gpointer))) return;
|
||||
gpointer handle = *(gpointer*)raw;
|
||||
auto dragged_button = (Gtk::Button*)handle;
|
||||
|
||||
if (dragged_button == &this->button) return;
|
||||
@ -509,7 +511,7 @@ void Task::update() {
|
||||
if (markup)
|
||||
button.set_tooltip_markup(txt);
|
||||
else
|
||||
button.set_tooltip_text(txt);
|
||||
button.set_tooltip_markup(txt);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -237,9 +237,10 @@ void AudioBackend::sourceInfoCb(pa_context* /*context*/, const pa_source_info* i
|
||||
*/
|
||||
void AudioBackend::serverInfoCb(pa_context* context, const pa_server_info* i, void* data) {
|
||||
auto* backend = static_cast<AudioBackend*>(data);
|
||||
backend->current_sink_name_ = i->default_sink_name;
|
||||
backend->default_sink_name = i->default_sink_name;
|
||||
backend->default_source_name_ = i->default_source_name;
|
||||
if (i == nullptr) return;
|
||||
backend->current_sink_name_ = i->default_sink_name ? i->default_sink_name : "";
|
||||
backend->default_sink_name = i->default_sink_name ? i->default_sink_name : "";
|
||||
backend->default_source_name_ = i->default_source_name ? i->default_source_name : "";
|
||||
|
||||
pa_context_get_sink_info_list(context, sinkInfoCb, data);
|
||||
pa_context_get_source_info_list(context, sourceInfoCb, data);
|
||||
@ -355,6 +356,7 @@ void AudioBackend::changeVolume(ChangeType change_type, double step, uint16_t ma
|
||||
}
|
||||
|
||||
void AudioBackend::toggleSinkMute() {
|
||||
if (context_ == nullptr || pa_context_get_state(context_) != PA_CONTEXT_READY) return;
|
||||
muted_ = !muted_;
|
||||
pa_threaded_mainloop_lock(mainloop_);
|
||||
pa_context_set_sink_mute_by_index(context_, sink_idx_, static_cast<int>(muted_), nullptr,
|
||||
@ -363,6 +365,7 @@ void AudioBackend::toggleSinkMute() {
|
||||
}
|
||||
|
||||
void AudioBackend::toggleSinkMute(bool mute) {
|
||||
if (context_ == nullptr || pa_context_get_state(context_) != PA_CONTEXT_READY) return;
|
||||
muted_ = mute;
|
||||
pa_threaded_mainloop_lock(mainloop_);
|
||||
pa_context_set_sink_mute_by_index(context_, sink_idx_, static_cast<int>(muted_), nullptr,
|
||||
@ -371,7 +374,8 @@ void AudioBackend::toggleSinkMute(bool mute) {
|
||||
}
|
||||
|
||||
void AudioBackend::toggleSourceMute() {
|
||||
source_muted_ = !muted_;
|
||||
if (context_ == nullptr || pa_context_get_state(context_) != PA_CONTEXT_READY) return;
|
||||
source_muted_ = !source_muted_;
|
||||
pa_threaded_mainloop_lock(mainloop_);
|
||||
pa_context_set_source_mute_by_index(context_, source_idx_, static_cast<int>(source_muted_),
|
||||
nullptr, nullptr);
|
||||
@ -379,6 +383,7 @@ void AudioBackend::toggleSourceMute() {
|
||||
}
|
||||
|
||||
void AudioBackend::toggleSourceMute(bool mute) {
|
||||
if (context_ == nullptr || pa_context_get_state(context_) != PA_CONTEXT_READY) return;
|
||||
source_muted_ = mute;
|
||||
pa_threaded_mainloop_lock(mainloop_);
|
||||
pa_context_set_source_mute_by_index(context_, source_idx_, static_cast<int>(source_muted_),
|
||||
|
||||
@ -79,18 +79,38 @@ static void upsert_device(std::vector<BacklightDevice>& devices, udev_device* de
|
||||
});
|
||||
if (found != devices.end()) {
|
||||
if (actual != nullptr) {
|
||||
found->set_actual(std::stoi(actual));
|
||||
try {
|
||||
found->set_actual(std::stoi(actual));
|
||||
} catch (const std::exception&) {
|
||||
}
|
||||
}
|
||||
if (max != nullptr) {
|
||||
found->set_max(std::stoi(max));
|
||||
try {
|
||||
found->set_max(std::stoi(max));
|
||||
} catch (const std::exception&) {
|
||||
}
|
||||
}
|
||||
if (power != nullptr) {
|
||||
found->set_powered(std::stoi(power) == 0);
|
||||
try {
|
||||
found->set_powered(std::stoi(power) == 0);
|
||||
} catch (const std::exception&) {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const int actual_int = actual == nullptr ? 0 : std::stoi(actual);
|
||||
const int max_int = max == nullptr ? 0 : std::stoi(max);
|
||||
const bool power_bool = power == nullptr ? true : std::stoi(power) == 0;
|
||||
int actual_int = 0, max_int = 0;
|
||||
bool power_bool = true;
|
||||
try {
|
||||
if (actual != nullptr) actual_int = std::stoi(actual);
|
||||
} catch (const std::exception&) {
|
||||
}
|
||||
try {
|
||||
if (max != nullptr) max_int = std::stoi(max);
|
||||
} catch (const std::exception&) {
|
||||
}
|
||||
try {
|
||||
if (power != nullptr) power_bool = std::stoi(power) == 0;
|
||||
} catch (const std::exception&) {
|
||||
}
|
||||
devices.emplace_back(name, actual_int, max_int, power_bool);
|
||||
}
|
||||
}
|
||||
@ -261,6 +281,11 @@ void BacklightBackend::set_brightness(const std::string& preferred_device, Chang
|
||||
|
||||
void BacklightBackend::set_brightness_internal(const std::string& device_name, int brightness,
|
||||
int max_brightness) {
|
||||
if (!login_proxy_) {
|
||||
spdlog::error("Login proxy not available, cannot set brightness");
|
||||
return;
|
||||
}
|
||||
|
||||
brightness = std::clamp(brightness, 0, max_brightness);
|
||||
|
||||
auto call_args = Glib::VariantContainerBase(
|
||||
@ -273,6 +298,7 @@ int BacklightBackend::get_scaled_brightness(const std::string& preferred_device)
|
||||
GET_BEST_DEVICE(best, (*this), preferred_device);
|
||||
|
||||
if (best != nullptr) {
|
||||
if (best->get_max() == 0) return 0;
|
||||
return static_cast<int>(std::round(best->get_actual() * 100.0F / best->get_max()));
|
||||
}
|
||||
|
||||
|
||||
@ -115,11 +115,15 @@ std::vector<std::string> waybar::CssReloadHelper::parseImports(const std::string
|
||||
auto maxIterations = 100U;
|
||||
do {
|
||||
previousSize = imports.size();
|
||||
std::vector<std::string> to_parse;
|
||||
for (const auto& [file, parsed] : imports) {
|
||||
if (!parsed) {
|
||||
parseImports(file, imports);
|
||||
to_parse.push_back(file);
|
||||
}
|
||||
}
|
||||
for (const auto& file : to_parse) {
|
||||
parseImports(file, imports);
|
||||
}
|
||||
|
||||
} while (imports.size() > previousSize && maxIterations-- > 0);
|
||||
|
||||
|
||||
@ -5,8 +5,11 @@
|
||||
std::vector<std::string> IconLoader::search_prefix() {
|
||||
std::vector<std::string> prefixes = {""};
|
||||
|
||||
std::string home_dir = std::getenv("HOME");
|
||||
prefixes.push_back(home_dir + "/.local/share/");
|
||||
const char* home_env = std::getenv("HOME");
|
||||
std::string home_dir = home_env ? home_env : "";
|
||||
if (!home_dir.empty()) {
|
||||
prefixes.push_back(home_dir + "/.local/share/");
|
||||
}
|
||||
|
||||
auto xdg_data_dirs = std::getenv("XDG_DATA_DIRS");
|
||||
if (!xdg_data_dirs) {
|
||||
@ -139,7 +142,7 @@ bool IconLoader::image_load_icon(Gtk::Image& image, const Glib::RefPtr<Gtk::Icon
|
||||
}
|
||||
|
||||
if (pixbuf) {
|
||||
if (pixbuf->get_width() != scaled_icon_size) {
|
||||
if (pixbuf->get_width() != scaled_icon_size && pixbuf->get_height() > 0) {
|
||||
int width = scaled_icon_size * pixbuf->get_width() / pixbuf->get_height();
|
||||
pixbuf = pixbuf->scale_simple(width, scaled_icon_size, Gdk::InterpType::INTERP_BILINEAR);
|
||||
}
|
||||
|
||||
@ -54,11 +54,17 @@ PipewireBackend::PipewireBackend(PrivateConstructorTag tag)
|
||||
context_ = pw_context_new(pw_thread_loop_get_loop(mainloop_), nullptr, 0);
|
||||
if (context_ == nullptr) {
|
||||
pw_thread_loop_unlock(mainloop_);
|
||||
pw_thread_loop_destroy(mainloop_);
|
||||
mainloop_ = nullptr;
|
||||
throw std::runtime_error("pa_context_new() failed.");
|
||||
}
|
||||
core_ = pw_context_connect(context_, nullptr, 0);
|
||||
if (core_ == nullptr) {
|
||||
pw_thread_loop_unlock(mainloop_);
|
||||
pw_context_destroy(context_);
|
||||
context_ = nullptr;
|
||||
pw_thread_loop_destroy(mainloop_);
|
||||
mainloop_ = nullptr;
|
||||
throw std::runtime_error("pw_context_connect() failed");
|
||||
}
|
||||
registry_ = pw_core_get_registry(core_, PW_VERSION_REGISTRY, 0);
|
||||
@ -136,7 +142,10 @@ void PipewireBackend::handleRegistryEventGlobal(uint32_t id, uint32_t permission
|
||||
|
||||
pw_proxy_add_object_listener(proxy, &pNodeInfo->object_listener, &NODE_EVENTS, pNodeInfo);
|
||||
|
||||
privacy_nodes.insert_or_assign(id, pNodeInfo);
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
privacy_nodes.insert_or_assign(id, pNodeInfo);
|
||||
}
|
||||
}
|
||||
|
||||
void PipewireBackend::handleRegistryEventGlobalRemove(uint32_t id) {
|
||||
|
||||
@ -58,21 +58,27 @@ void waybar::Portal::refreshAppearance() {
|
||||
// xdg-desktop-portal 1.17 will fix this issue with a new `ReadOne` method,
|
||||
// but this version is not yet released.
|
||||
// TODO(xdg-desktop-portal v1.17): switch to ReadOne
|
||||
auto container = Glib::VariantBase::cast_dynamic<Glib::VariantContainerBase>(response);
|
||||
Glib::VariantBase modev;
|
||||
container.get_child(modev, 0);
|
||||
auto mode =
|
||||
Glib::VariantBase::cast_dynamic<Glib::Variant<Glib::Variant<Glib::Variant<uint32_t>>>>(modev)
|
||||
.get()
|
||||
.get()
|
||||
.get();
|
||||
auto newMode = Appearance(mode);
|
||||
if (newMode == currentMode) {
|
||||
try {
|
||||
auto container = Glib::VariantBase::cast_dynamic<Glib::VariantContainerBase>(response);
|
||||
Glib::VariantBase modev;
|
||||
container.get_child(modev, 0);
|
||||
auto mode =
|
||||
Glib::VariantBase::cast_dynamic<Glib::Variant<Glib::Variant<Glib::Variant<uint32_t>>>>(
|
||||
modev)
|
||||
.get()
|
||||
.get()
|
||||
.get();
|
||||
auto newMode = Appearance(mode);
|
||||
if (newMode == currentMode) {
|
||||
return;
|
||||
}
|
||||
spdlog::info("Discovered appearance '{}'", newMode);
|
||||
currentMode = newMode;
|
||||
m_signal_appearance_changed.emit(currentMode);
|
||||
} catch (const std::bad_cast& e) {
|
||||
spdlog::error("Unexpected appearance variant format: {}", e.what());
|
||||
return;
|
||||
}
|
||||
spdlog::info("Discovered appearance '{}'", newMode);
|
||||
currentMode = newMode;
|
||||
m_signal_appearance_changed.emit(currentMode);
|
||||
}
|
||||
|
||||
waybar::Appearance waybar::Portal::getAppearance() { return currentMode; };
|
||||
|
||||
Reference in New Issue
Block a user