Compare commits

..

48 Commits

Author SHA1 Message Date
3ecd4030e3 chore: v0.9.20 2023-07-18 08:29:32 +02:00
841a004acd fix: lint 2023-07-18 08:28:19 +02:00
839975c348 Merge pull request #2318 from calvinchd/hyprland-window-remove-empty-css
hyprland/window remove duplicate empty css
2023-07-18 08:22:27 +02:00
185aa104b0 Merge pull request #2326 from czM1K3/master
hyprland/language: Differentiating keyboard layout variants
2023-07-18 08:21:52 +02:00
b2279c9565 Differencing keyboard layout variant for hyprland/language 2023-07-17 22:20:50 +02:00
388912d4a7 Merge pull request #2324 from xytovl/fix-crash-on-resume 2023-07-17 08:26:53 +02:00
f62b3d0e9d Ensure signal is disconnected in destructor 2023-07-16 23:29:28 +02:00
1e2b9cb5ed Merge pull request #2306 from stefonarch/patch-1
Fix wrong name for {name}
2023-07-16 18:21:00 +02:00
85b4ff4f81 Merge pull request #2321 from calvinchd/hyprland-runtime-err 2023-07-16 15:10:03 +02:00
0f6eff1f20 hyprland: fix json parser runtime err from socket read ending early 2023-07-16 22:18:27 +10:00
7a5e702334 Merge pull request #2317 from m4rch3n1ng/backlight-percent 2023-07-16 09:34:47 +02:00
8687ed2068 Update man for hyprland/window to replace #window.empty with window#waybar.empty #window 2023-07-16 16:41:50 +10:00
2be0e966e1 hyprland/window: remove .empty css class for #window 2023-07-16 16:40:54 +10:00
may
facb53e81f backlight: do not convert percent to string in fmt 2023-07-16 04:14:43 +02:00
2211a79840 Merge pull request #2308 from gardenappl/hidden-fix
hyprland/window: rename .hidden to .swallowing (and fix grouped windows)
2023-07-12 18:13:50 +02:00
daca57129f hyprland/window: rename .hidden to .swallowing (and fix grouped windows) 2023-07-12 19:01:45 +03:00
19c7c0763f Merge pull request #2307 from gardenappl/hidden-fix
hyprland/window: Fix overlap with .hidden class from default style
2023-07-12 17:27:37 +02:00
14c6550593 hyprland/window: Fix overlap with .hidden class from default style 2023-07-12 17:56:12 +03:00
7aae93e7ed Fix wrong name for {name} 2023-07-12 16:31:58 +02:00
dffba78401 Merge pull request #2303 from Mr-Pine/hyprland-window-icon 2023-07-11 08:08:20 +02:00
a8a1a4985f Add removed secondary identifier
(class for xwayland under sway)
2023-07-10 23:48:18 +02:00
31683d9e2a Implemented AAppIconLabel for sway/window 2023-07-10 22:55:46 +02:00
00e143d47e Introducce AAppIconLabel class
Implemented for hyprland
2023-07-10 22:50:58 +02:00
6e9ba3fc01 Fix spacing if icon is false 2023-07-10 22:26:02 +02:00
a373f6b654 Icon working 2023-07-10 22:02:03 +02:00
91bd28d410 Merge pull request #2294 from Mr-Pine/hyprland-window-data
`hyprland/window` expose more data
2023-07-09 10:18:26 +02:00
acde076913 Merge pull request #2288 from LukashonakV/arch_cava
Arch CI cava
2023-07-09 10:17:55 +02:00
f5655526d0 Merge pull request #2296 from m-bdf/clock-tz-changes 2023-07-09 04:22:15 +02:00
56f956ff90 clock: handle timezone changes (again) 2023-07-09 01:44:39 +02:00
f97c1c7136 remove getWindowData 2023-07-08 23:22:29 +02:00
9ee883ee1b No dashes is format arg name 2023-07-08 23:11:11 +02:00
1887512ba1 Update scd 2023-07-08 22:40:16 +02:00
2ae13c4092 consitent naming 2023-07-08 22:40:16 +02:00
c5f1771375 Use already existing queryActiveWorkspace() 2023-07-08 22:40:16 +02:00
c4bace504c Separate query and struct 2023-07-08 22:40:16 +02:00
3bfeed31bc Merge pull request #2293 from sigboe/master 2023-07-08 21:47:04 +02:00
d774de6c46 fix, default to true, sway/workspaces: warp-on-scroll 2023-07-08 21:32:19 +02:00
b20041d85d cava dependencies 2023-07-08 08:41:37 +00:00
e4900db9a2 Merge pull request #2286 from calvinchd/hyprland-window-noinfo 2023-07-06 08:24:11 +02:00
e2bfa5e019 hyprland/window: fix no info with separate-outputs=true 2023-07-06 11:01:24 +10:00
423d8495e4 Merge pull request #2284 from jbeich/dbus 2023-07-05 20:17:51 +02:00
1fb2b8efd5 fix(util): don't abort modules from SleeperThread after 3c9cbc99d7
[warning] module sway/workspaces: Disabling module "sway/workspaces", Unable to connect to the SYSTEM Bus!...
[warning] module sway/mode: Disabling module "sway/mode", Unable to connect to the SYSTEM Bus!...
[warning] module sway/scratchpad: Disabling module "sway/scratchpad", Unable to connect to the SYSTEM Bus!...
[warning] module custom/media: Disabling module "custom/media", Unable to connect to the SYSTEM Bus!...
[warning] module sway/window: Disabling module "sway/window", Unable to connect to the SYSTEM Bus!...
[warning] module cpu: Disabling module "cpu", Unable to connect to the SYSTEM Bus!...
[warning] module memory: Disabling module "memory", Unable to connect to the SYSTEM Bus!...
[warning] module temperature: Disabling module "temperature", Unable to connect to the SYSTEM Bus!...
[warning] module sway/language: Disabling module "sway/language", Unable to connect to the SYSTEM Bus!...
[warning] module battery: Disabling module "battery", Unable to connect to the SYSTEM Bus!...
[warning] module battery#bat2: Disabling module "battery#bat2", Unable to connect to the SYSTEM Bus!...
2023-07-05 17:47:24 +00:00
3299d4a25c Merge pull request #2270 from gardenappl/hypr-backend
hyprland/window: Add .hidden CSS class, account for hidden & fullscreen windows
2023-07-05 08:14:49 +02:00
e125bbeb4d hyprland/window: properly check visibility for .floating class 2023-07-05 03:43:03 +03:00
55c59253d6 Update man pages 2023-07-05 03:15:59 +03:00
e7deab92c7 Merge pull request #2282 from zjeffer/fix/build-warnings
Fixed build warnings
2023-07-04 23:09:13 +02:00
d21f29cb14 Fixed build warnings 2023-07-04 23:05:26 +02:00
cdece498c1 hyprland/window: .solo class accounts for hidden & fullscreen windows 2023-07-02 20:58:42 +03:00
19 changed files with 305 additions and 173 deletions

View File

@ -3,5 +3,5 @@
FROM archlinux:base-devel FROM archlinux:base-devel
RUN pacman -Syu --noconfirm && \ RUN pacman -Syu --noconfirm && \
pacman -S --noconfirm git meson base-devel libinput wayland wayland-protocols pixman libxkbcommon mesa gtkmm3 jsoncpp pugixml scdoc libpulse libdbusmenu-gtk3 libmpdclient gobject-introspection libxkbcommon playerctl && \ pacman -S --noconfirm git meson base-devel libinput wayland wayland-protocols pixman libxkbcommon mesa gtkmm3 jsoncpp pugixml scdoc libpulse libdbusmenu-gtk3 libmpdclient gobject-introspection libxkbcommon playerctl iniparser fftw && \
sed -Ei 's/#(en_(US|GB)\.UTF)/\1/' /etc/locale.gen && locale-gen sed -Ei 's/#(en_(US|GB)\.UTF)/\1/' /etc/locale.gen && locale-gen

27
include/AAppIconLabel.hpp Normal file
View File

@ -0,0 +1,27 @@
#pragma once
#include <gtkmm/box.h>
#include <gtkmm/image.h>
#include "AIconLabel.hpp"
namespace waybar {
class AAppIconLabel : public AIconLabel {
public:
AAppIconLabel(const Json::Value &config, const std::string &name, const std::string &id,
const std::string &format, uint16_t interval = 0, bool ellipsize = false,
bool enable_click = false, bool enable_scroll = false);
virtual ~AAppIconLabel() = default;
auto update() -> void override;
protected:
void updateAppIconName(const std::string &app_identifier,
const std::string &alternative_app_identifier);
void updateAppIcon();
unsigned app_icon_size_{24};
bool update_app_icon_{true};
std::string app_icon_name_;
};
} // namespace waybar

View File

@ -1,13 +1,13 @@
#include <fmt/format.h> #include <fmt/format.h>
#include "ALabel.hpp" #include "AAppIconLabel.hpp"
#include "bar.hpp" #include "bar.hpp"
#include "modules/hyprland/backend.hpp" #include "modules/hyprland/backend.hpp"
#include "util/json.hpp" #include "util/json.hpp"
namespace waybar::modules::hyprland { namespace waybar::modules::hyprland {
class Window : public waybar::ALabel, public EventHandler { class Window : public waybar::AAppIconLabel, public EventHandler {
public: public:
Window(const std::string&, const waybar::Bar&, const Json::Value&); Window(const std::string&, const waybar::Bar&, const Json::Value&);
virtual ~Window(); virtual ~Window();
@ -24,6 +24,19 @@ class Window : public waybar::ALabel, public EventHandler {
static auto parse(const Json::Value&) -> Workspace; static auto parse(const Json::Value&) -> Workspace;
}; };
struct WindowData {
bool floating;
int monitor = -1;
std::string class_name;
std::string initial_class_name;
std::string title;
std::string initial_title;
bool fullscreen;
bool grouped;
static auto parse(const Json::Value&) -> WindowData;
};
auto getActiveWorkspace(const std::string&) -> Workspace; auto getActiveWorkspace(const std::string&) -> Workspace;
auto getActiveWorkspace() -> Workspace; auto getActiveWorkspace() -> Workspace;
void onEvent(const std::string&) override; void onEvent(const std::string&) override;
@ -34,12 +47,13 @@ class Window : public waybar::ALabel, public EventHandler {
std::mutex mutex_; std::mutex mutex_;
const Bar& bar_; const Bar& bar_;
util::JsonParser parser_; util::JsonParser parser_;
std::string last_title_; WindowData window_data_;
Workspace workspace_; Workspace workspace_;
std::string solo_class_; std::string solo_class_;
std::string last_solo_class_; std::string last_solo_class_;
bool solo_; bool solo_;
bool all_floating_; bool all_floating_;
bool swallowing_;
bool fullscreen_; bool fullscreen_;
}; };

View File

@ -66,9 +66,9 @@ class Mpris : public ALabel {
int album_len_; int album_len_;
int title_len_; int title_len_;
int dynamic_len_; int dynamic_len_;
std::string dynamic_separator_;
std::vector<std::string> dynamic_order_;
std::vector<std::string> dynamic_prio_; std::vector<std::string> dynamic_prio_;
std::vector<std::string> dynamic_order_;
std::string dynamic_separator_;
bool truncate_hours_; bool truncate_hours_;
bool tooltip_len_limits_; bool tooltip_len_limits_;
std::string ellipsis_; std::string ellipsis_;

View File

@ -4,7 +4,7 @@
#include <tuple> #include <tuple>
#include "AIconLabel.hpp" #include "AAppIconLabel.hpp"
#include "bar.hpp" #include "bar.hpp"
#include "client.hpp" #include "client.hpp"
#include "modules/sway/ipc/client.hpp" #include "modules/sway/ipc/client.hpp"
@ -12,7 +12,7 @@
namespace waybar::modules::sway { namespace waybar::modules::sway {
class Window : public AIconLabel, public sigc::trackable { class Window : public AAppIconLabel, public sigc::trackable {
public: public:
Window(const std::string&, const waybar::Bar&, const Json::Value&); Window(const std::string&, const waybar::Bar&, const Json::Value&);
virtual ~Window() = default; virtual ~Window() = default;
@ -25,8 +25,6 @@ class Window : public AIconLabel, public sigc::trackable {
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>
getFocusedNode(const Json::Value& nodes, std::string& output); getFocusedNode(const Json::Value& nodes, std::string& output);
void getTree(); void getTree();
void updateAppIconName();
void updateAppIcon();
const Bar& bar_; const Bar& bar_;
std::string window_; std::string window_;
@ -37,9 +35,6 @@ class Window : public AIconLabel, 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_;
unsigned app_icon_size_{24};
bool update_app_icon_{true};
std::string app_icon_name_;
int floating_count_; int floating_count_;
util::JsonParser parser_; util::JsonParser parser_;
std::mutex mutex_; std::mutex mutex_;

View File

@ -95,6 +95,7 @@ class SleeperThread {
} }
~SleeperThread() { ~SleeperThread() {
connection_.disconnect();
stop(); stop();
if (thread_.joinable()) { if (thread_.joinable()) {
thread_.join(); thread_.join();

View File

@ -14,13 +14,28 @@ Addressed by *hyprland/window*
*format*: ++ *format*: ++
typeof: string ++ typeof: string ++
default: {} ++ default: {title} ++
The format, how information should be displayed. On {} the current window title is displayed. The format, how information should be displayed. On {} the current window title is displayed.
*rewrite*: ++ *rewrite*: ++
typeof: object ++ typeof: object ++
Rules to rewrite window title. See *rewrite rules*. Rules to rewrite window title. See *rewrite rules*.
*separate-outputs*: ++
typeof: bool ++
Show the active window of the monitor the bar belongs to, instead of the focused window.
# FORMAT REPLACEMENTS
See the output of "hyprctl clients" for examples
*{title}*: The current title of the focused window.
*{initialTitle}*: The initial title of the focused window.
*{class}*: The current class of the focused window.
*{initialClass}*: The initial class of the focused 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
@ -48,3 +63,17 @@ Invalid expressions (e.g., mismatched parentheses) are skipped.
# STYLE # STYLE
- *#window* - *#window*
- *window#waybar.empty #window* When no windows are in 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 one tiled window is visible in the workspace
(floating windows may be present)
- *window#waybar.<app_id>* Where *<app_id>* is the *class* (e.g. *chromium*) of
the solo tiled window in the workspace (use *hyprctl clients* to see classes)
- *window#waybar.floating* When there are only floating windows in the workspace
- *window#waybar.fullscreen* When there is a fullscreen window in the workspace;
useful with Hyprland's *fullscreen, 1* mode
- *window#waybar.swallowing* When there is a swallowed window in the workspace

View File

@ -84,7 +84,7 @@ Addressed by *sway/window*
typeof: bool ++ typeof: bool ++
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.
*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*.
@ -136,6 +136,10 @@ Invalid expressions (e.g., mismatched parentheses) are skipped.
# STYLE # STYLE
- *#window* - *#window*
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, or screen is not focused and offscreen-css option is not set - *window#waybar.empty* When no windows are in the workspace, or screen is not focused and offscreen-css option is not set
- *window#waybar.solo* When one tiled window is in the workspace - *window#waybar.solo* When one tiled window is in the workspace
- *window#waybar.floating* When there are only floating windows in the workspace - *window#waybar.floating* When there are only floating windows in the workspace

View File

@ -89,7 +89,7 @@ Addressed by *wlr/taskbar*
*{icon}*: The icon of the application. *{icon}*: The icon of the application.
*{title}*: The application name as in desktop file if appropriate desktop fils found, otherwise same as {app_id} *{name}*: The application name as in desktop file if appropriate desktop fils found, otherwise same as {app_id}
*{title}*: The title of the application. *{title}*: The title of the application.

View File

@ -1,6 +1,6 @@
project( project(
'waybar', 'cpp', 'c', 'waybar', 'cpp', 'c',
version: '0.9.19', version: '0.9.20',
license: 'MIT', license: 'MIT',
meson_version: '>= 0.50.0', meson_version: '>= 0.50.0',
default_options : [ default_options : [
@ -159,6 +159,7 @@ src_files = files(
'src/AModule.cpp', 'src/AModule.cpp',
'src/ALabel.cpp', 'src/ALabel.cpp',
'src/AIconLabel.cpp', 'src/AIconLabel.cpp',
'src/AAppIconLabel.cpp',
'src/modules/custom.cpp', 'src/modules/custom.cpp',
'src/modules/disk.cpp', 'src/modules/disk.cpp',
'src/modules/idle_inhibitor.cpp', 'src/modules/idle_inhibitor.cpp',

133
src/AAppIconLabel.cpp Normal file
View File

@ -0,0 +1,133 @@
#include "AAppIconLabel.hpp"
#include <gdkmm/pixbuf.h>
#include <glibmm/fileutils.h>
#include <glibmm/keyfile.h>
#include <glibmm/miscutils.h>
#include <spdlog/spdlog.h>
#include <filesystem>
#include <optional>
#include "util/gtk_icon.hpp"
namespace waybar {
AAppIconLabel::AAppIconLabel(const Json::Value& config, const std::string& name,
const std::string& id, const std::string& format, uint16_t interval,
bool ellipsize, bool enable_click, bool enable_scroll)
: AIconLabel(config, name, id, format, interval, ellipsize, enable_click, enable_scroll) {
// Icon size
if (config["icon-size"].isUInt()) {
app_icon_size_ = config["icon-size"].asUInt();
}
image_.set_pixel_size(app_icon_size_);
}
std::optional<std::string> getDesktopFilePath(const std::string& app_identifier,
const std::string& alternative_app_identifier) {
const auto data_dirs = Glib::get_system_data_dirs();
for (const auto& data_dir : data_dirs) {
const auto data_app_dir = data_dir + "applications/";
auto desktop_file_path = data_app_dir + app_identifier + ".desktop";
if (std::filesystem::exists(desktop_file_path)) {
return desktop_file_path;
}
if (!alternative_app_identifier.empty()) {
desktop_file_path = data_app_dir + alternative_app_identifier + ".desktop";
if (std::filesystem::exists(desktop_file_path)) {
return desktop_file_path;
}
}
}
return {};
}
std::optional<Glib::ustring> getIconName(const std::string& app_identifier,
const std::string& alternative_app_identifier) {
const auto desktop_file_path = getDesktopFilePath(app_identifier, alternative_app_identifier);
if (!desktop_file_path.has_value()) {
// Try some heuristics to find a matching icon
if (DefaultGtkIconThemeWrapper::has_icon(app_identifier)) {
return app_identifier;
}
const auto app_identifier_desktop = app_identifier + "-desktop";
if (DefaultGtkIconThemeWrapper::has_icon(app_identifier_desktop)) {
return app_identifier_desktop;
}
const auto to_lower = [](const std::string& str) {
auto str_cpy = str;
std::transform(str_cpy.begin(), str_cpy.end(), str_cpy.begin(),
[](unsigned char c) { return std::tolower(c); });
return str;
};
const auto first_space = app_identifier.find_first_of(' ');
if (first_space != std::string::npos) {
const auto first_word = to_lower(app_identifier.substr(0, first_space));
if (DefaultGtkIconThemeWrapper::has_icon(first_word)) {
return first_word;
}
}
const auto first_dash = app_identifier.find_first_of('-');
if (first_dash != std::string::npos) {
const auto first_word = to_lower(app_identifier.substr(0, first_dash));
if (DefaultGtkIconThemeWrapper::has_icon(first_word)) {
return first_word;
}
}
return {};
}
try {
Glib::KeyFile desktop_file;
desktop_file.load_from_file(desktop_file_path.value());
return desktop_file.get_string("Desktop Entry", "Icon");
} catch (Glib::FileError& error) {
spdlog::warn("Error while loading desktop file {}: {}", desktop_file_path.value(),
error.what().c_str());
} catch (Glib::KeyFileError& error) {
spdlog::warn("Error while loading desktop file {}: {}", desktop_file_path.value(),
error.what().c_str());
}
return {};
}
void AAppIconLabel::updateAppIconName(const std::string& app_identifier,
const std::string& alternative_app_identifier) {
if (!iconEnabled()) {
return;
}
const auto icon_name = getIconName(app_identifier, alternative_app_identifier);
if (icon_name.has_value()) {
app_icon_name_ = icon_name.value();
} else {
app_icon_name_ = "";
}
update_app_icon_ = true;
}
void AAppIconLabel::updateAppIcon() {
if (update_app_icon_) {
update_app_icon_ = false;
if (app_icon_name_.empty()) {
image_.set_visible(false);
} else {
image_.set_from_icon_name(app_icon_name_, Gtk::ICON_SIZE_INVALID);
image_.set_visible(true);
}
}
}
auto AAppIconLabel::update() -> void {
updateAppIcon();
AIconLabel::update();
}
} // namespace waybar

View File

@ -190,9 +190,8 @@ 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 = std::string desc = fmt::format(fmt::runtime(format_), fmt::arg("percent", percent),
fmt::format(fmt::runtime(format_), fmt::arg("percent", std::to_string(percent)), fmt::arg("icon", getIcon(percent)));
fmt::arg("icon", getIcon(percent)));
label_.set_markup(desc); label_.set_markup(desc);
getState(percent); getState(percent);
if (tooltipEnabled()) { if (tooltipEnabled()) {
@ -202,7 +201,7 @@ auto waybar::modules::Backlight::update() -> void {
} }
if (!tooltip_format.empty()) { if (!tooltip_format.empty()) {
label_.set_tooltip_text(fmt::format(fmt::runtime(tooltip_format), label_.set_tooltip_text(fmt::format(fmt::runtime(tooltip_format),
fmt::arg("percent", std::to_string(percent)), fmt::arg("percent", percent),
fmt::arg("icon", getIcon(percent)))); fmt::arg("icon", getIcon(percent))));
} else { } else {
label_.set_tooltip_text(desc); label_.set_tooltip_text(desc);

View File

@ -24,7 +24,8 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
for (const auto& zone_name : config_["timezones"]) { for (const auto& zone_name : config_["timezones"]) {
if (!zone_name.isString()) continue; if (!zone_name.isString()) continue;
if (zone_name.asString().empty()) if (zone_name.asString().empty())
time_zones_.push_back(date::current_zone()); // nullptr means that local time should be shown
time_zones_.push_back(nullptr);
else else
try { try {
time_zones_.push_back(date::locate_zone(zone_name.asString())); time_zones_.push_back(date::locate_zone(zone_name.asString()));
@ -34,7 +35,8 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
} }
} else if (config_["timezone"].isString()) { } else if (config_["timezone"].isString()) {
if (config_["timezone"].asString().empty()) if (config_["timezone"].asString().empty())
time_zones_.push_back(date::current_zone()); // nullptr means that local time should be shown
time_zones_.push_back(nullptr);
else else
try { try {
time_zones_.push_back(date::locate_zone(config_["timezone"].asString())); time_zones_.push_back(date::locate_zone(config_["timezone"].asString()));
@ -43,10 +45,10 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
} }
} }
// If all timezones are parsed and no one is good, add current time zone. nullptr in timezones // If all timezones are parsed and no one is good
// vector means that local time should be shown
if (!time_zones_.size()) { if (!time_zones_.size()) {
time_zones_.push_back(date::current_zone()); // nullptr means that local time should be shown
time_zones_.push_back(nullptr);
} }
// Check if a particular placeholder is present in the tooltip format, to know what to calculate // Check if a particular placeholder is present in the tooltip format, to know what to calculate
@ -156,8 +158,7 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
} }
const date::time_zone* waybar::modules::Clock::current_timezone() { const date::time_zone* waybar::modules::Clock::current_timezone() {
return time_zones_[current_time_zone_idx_] ? time_zones_[current_time_zone_idx_] return is_timezone_fixed() ? time_zones_[current_time_zone_idx_] : date::current_zone();
: date::current_zone();
} }
bool waybar::modules::Clock::is_timezone_fixed() { bool waybar::modules::Clock::is_timezone_fixed() {

View File

@ -192,7 +192,7 @@ std::string IPC::getSocket1Reply(const std::string& rq) {
return ""; return "";
} }
response.append(buffer, sizeWritten); response.append(buffer, sizeWritten);
} while (sizeWritten == 8192); } while (sizeWritten > 0);
close(SERVERSOCKET); close(SERVERSOCKET);
return response; return response;

View File

@ -38,7 +38,10 @@ auto Language::update() -> void {
std::lock_guard<std::mutex> lg(mutex_); std::lock_guard<std::mutex> lg(mutex_);
std::string layoutName = std::string{}; std::string layoutName = std::string{};
if (config_.isMember("format-" + layout_.short_description)) { if (config_.isMember("format-" + layout_.short_description + "-" + layout_.variant)) {
const auto propName = "format-" + layout_.short_description + "-" + layout_.variant;
layoutName = fmt::format(fmt::runtime(format_), config_[propName].asString());
} else if (config_.isMember("format-" + layout_.short_description)) {
const auto propName = "format-" + layout_.short_description; const auto propName = "format-" + layout_.short_description;
layoutName = fmt::format(fmt::runtime(format_), config_[propName].asString()); layoutName = fmt::format(fmt::runtime(format_), config_[propName].asString());
} else { } else {

View File

@ -1,5 +1,8 @@
#include "modules/hyprland/window.hpp" #include "modules/hyprland/window.hpp"
#include <glibmm/fileutils.h>
#include <glibmm/keyfile.h>
#include <glibmm/miscutils.h>
#include <spdlog/spdlog.h> #include <spdlog/spdlog.h>
#include <algorithm> #include <algorithm>
@ -8,13 +11,12 @@
#include <vector> #include <vector>
#include "modules/hyprland/backend.hpp" #include "modules/hyprland/backend.hpp"
#include "util/json.hpp"
#include "util/rewrite_string.hpp" #include "util/rewrite_string.hpp"
namespace waybar::modules::hyprland { namespace waybar::modules::hyprland {
Window::Window(const std::string& id, const Bar& bar, const Json::Value& config) Window::Window(const std::string& id, const Bar& bar, const Json::Value& config)
: ALabel(config, "window", id, "{}", 0, true), bar_(bar) { : AAppIconLabel(config, "window", id, "{title}", 0, true), bar_(bar) {
modulesReady = true; modulesReady = true;
separate_outputs = config["separate-outputs"].asBool(); separate_outputs = config["separate-outputs"].asBool();
@ -44,28 +46,27 @@ auto Window::update() -> void {
std::lock_guard<std::mutex> lg(mutex_); std::lock_guard<std::mutex> lg(mutex_);
std::string window_name = waybar::util::sanitize_string(workspace_.last_window_title); std::string window_name = waybar::util::sanitize_string(workspace_.last_window_title);
std::string window_address = workspace_.last_window;
if (window_name != last_title_) { window_data_.title = window_name;
if (window_name.empty()) {
label_.get_style_context()->add_class("empty");
} else {
label_.get_style_context()->remove_class("empty");
}
last_title_ = window_name;
}
if (!format_.empty()) { if (!format_.empty()) {
label_.show(); label_.show();
label_.set_markup(fmt::format(fmt::runtime(format_), label_.set_markup(waybar::util::rewriteString(
waybar::util::rewriteString(window_name, config_["rewrite"]))); fmt::format(fmt::runtime(format_), fmt::arg("title", window_name),
fmt::arg("initialTitle", window_data_.initial_title),
fmt::arg("class", window_data_.class_name),
fmt::arg("initialClass", window_data_.initial_class_name)),
config_["rewrite"]));
} else { } else {
label_.hide(); label_.hide();
} }
setClass("empty", workspace_.windows == 0); setClass("empty", workspace_.windows == 0);
setClass("solo", solo_); setClass("solo", solo_);
setClass("fullscreen", fullscreen_);
setClass("floating", all_floating_); setClass("floating", all_floating_);
setClass("swallowing", swallowing_);
setClass("fullscreen", fullscreen_);
if (!last_solo_class_.empty() && solo_class_ != last_solo_class_) { if (!last_solo_class_.empty() && solo_class_ != last_solo_class_) {
if (bar_.window.get_style_context()->has_class(last_solo_class_)) { if (bar_.window.get_style_context()->has_class(last_solo_class_)) {
@ -80,7 +81,7 @@ auto Window::update() -> void {
} }
last_solo_class_ = solo_class_; last_solo_class_ = solo_class_;
ALabel::update(); AAppIconLabel::update();
} }
auto Window::getActiveWorkspace() -> Workspace { auto Window::getActiveWorkspace() -> Workspace {
@ -102,9 +103,9 @@ auto Window::getActiveWorkspace(const std::string& monitorName) -> Workspace {
const auto workspaces = gIPC->getSocket1JsonReply("workspaces"); const auto workspaces = gIPC->getSocket1JsonReply("workspaces");
assert(workspaces.isArray()); assert(workspaces.isArray());
auto workspace = std::find_if(monitors.begin(), monitors.end(), auto workspace = std::find_if(workspaces.begin(), workspaces.end(),
[&](Json::Value workspace) { return workspace["id"] == id; }); [&](Json::Value workspace) { return workspace["id"] == id; });
if (workspace == std::end(monitors)) { if (workspace == std::end(workspaces)) {
spdlog::warn("No workspace with id {}", id); spdlog::warn("No workspace with id {}", id);
return Workspace{-1, 0, "", ""}; return Workspace{-1, 0, "", ""};
} }
@ -116,6 +117,13 @@ auto Window::Workspace::parse(const Json::Value& value) -> Window::Workspace {
value["lastwindowtitle"].asString()}; value["lastwindowtitle"].asString()};
} }
auto Window::WindowData::parse(const Json::Value& value) -> Window::WindowData {
return WindowData{value["floating"].asBool(), value["monitor"].asInt(),
value["class"].asString(), value["initialClass"].asString(),
value["title"].asString(), value["initialTitle"].asString(),
value["fullscreen"].asBool(), !value["grouped"].empty()};
}
void Window::queryActiveWorkspace() { void Window::queryActiveWorkspace() {
std::lock_guard<std::mutex> lg(mutex_); std::lock_guard<std::mutex> lg(mutex_);
@ -126,36 +134,57 @@ void Window::queryActiveWorkspace() {
} }
if (workspace_.windows > 0) { if (workspace_.windows > 0) {
const auto clients = gIPC->getSocket1Reply("j/clients"); const auto clients = gIPC->getSocket1JsonReply("clients");
Json::Value json = parser_.parse(clients); assert(clients.isArray());
assert(json.isArray()); auto active_window = std::find_if(clients.begin(), clients.end(), [&](Json::Value window) {
auto active_window = std::find_if(json.begin(), json.end(), [&](Json::Value window) {
return window["address"] == workspace_.last_window; return window["address"] == workspace_.last_window;
}); });
if (active_window == std::end(json)) { if (active_window == std::end(clients)) {
return; return;
} }
if (workspace_.windows == 1 && !(*active_window)["floating"].asBool()) { window_data_ = WindowData::parse(*active_window);
solo_class_ = (*active_window)["class"].asString(); updateAppIconName(window_data_.class_name, window_data_.initial_class_name);
} else {
solo_class_ = "";
}
std::vector<Json::Value> workspace_windows; std::vector<Json::Value> workspace_windows;
std::copy_if(json.begin(), json.end(), std::back_inserter(workspace_windows), std::copy_if(clients.begin(), clients.end(), std::back_inserter(workspace_windows),
[&](Json::Value window) { [&](Json::Value window) {
return window["workspace"]["id"] == workspace_.id && window["mapped"].asBool(); return window["workspace"]["id"] == workspace_.id && window["mapped"].asBool();
}); });
solo_ = 1 == std::count_if(workspace_windows.begin(), workspace_windows.end(), swallowing_ = std::any_of(workspace_windows.begin(), workspace_windows.end(),
[&](Json::Value window) { return !window["swallowing"].isNull(); });
std::vector<Json::Value> visible_windows;
std::copy_if(workspace_windows.begin(), workspace_windows.end(),
std::back_inserter(visible_windows),
[&](Json::Value window) { return !window["hidden"].asBool(); });
solo_ = 1 == std::count_if(visible_windows.begin(), visible_windows.end(),
[&](Json::Value window) { return !window["floating"].asBool(); }); [&](Json::Value window) { return !window["floating"].asBool(); });
all_floating_ = std::all_of(workspace_windows.begin(), workspace_windows.end(), all_floating_ = std::all_of(visible_windows.begin(), visible_windows.end(),
[&](Json::Value window) { return window["floating"].asBool(); }); [&](Json::Value window) { return window["floating"].asBool(); });
fullscreen_ = (*active_window)["fullscreen"].asBool(); fullscreen_ = window_data_.fullscreen;
// Fullscreen windows look like they are solo
if (fullscreen_) {
solo_ = true;
}
// Grouped windows have a tab bar and therefore don't look fullscreen or solo
if (window_data_.grouped) {
fullscreen_ = false;
solo_ = false;
}
if (solo_) {
solo_class_ = window_data_.class_name;
} else {
solo_class_ = "";
}
} else { } else {
solo_class_ = ""; window_data_ = WindowData{};
solo_ = false;
all_floating_ = false; all_floating_ = false;
swallowing_ = false;
fullscreen_ = false; fullscreen_ = false;
solo_ = false;
solo_class_ = "";
} }
} }

View File

@ -17,13 +17,7 @@
namespace waybar::modules::sway { namespace waybar::modules::sway {
Window::Window(const std::string& id, const Bar& bar, const Json::Value& config) Window::Window(const std::string& id, const Bar& bar, const Json::Value& config)
: AIconLabel(config, "window", id, "{}", 0, true), bar_(bar), windowId_(-1) { : AAppIconLabel(config, "window", id, "{}", 0, true), bar_(bar), windowId_(-1) {
// Icon size
if (config_["icon-size"].isUInt()) {
app_icon_size_ = config["icon-size"].asUInt();
}
image_.set_pixel_size(app_icon_size_);
ipc_.subscribe(R"(["window","workspace"])"); ipc_.subscribe(R"(["window","workspace"])");
ipc_.signal_event.connect(sigc::mem_fun(*this, &Window::onEvent)); ipc_.signal_event.connect(sigc::mem_fun(*this, &Window::onEvent));
ipc_.signal_cmd.connect(sigc::mem_fun(*this, &Window::onCmd)); ipc_.signal_cmd.connect(sigc::mem_fun(*this, &Window::onCmd));
@ -49,7 +43,7 @@ void Window::onCmd(const struct Ipc::ipc_response& res) {
auto output = payload["output"].isString() ? payload["output"].asString() : ""; auto output = payload["output"].isString() ? payload["output"].asString() : "";
std::tie(app_nb_, floating_count_, windowId_, window_, app_id_, app_class_, shell_, layout_) = std::tie(app_nb_, floating_count_, windowId_, window_, app_id_, app_class_, shell_, layout_) =
getFocusedNode(payload["nodes"], output); getFocusedNode(payload["nodes"], output);
updateAppIconName(); updateAppIconName(app_id_, app_class_);
dp.emit(); dp.emit();
} catch (const std::exception& e) { } catch (const std::exception& e) {
spdlog::error("Window: {}", e.what()); spdlog::error("Window: {}", e.what());
@ -57,105 +51,6 @@ void Window::onCmd(const struct Ipc::ipc_response& res) {
} }
} }
std::optional<std::string> getDesktopFilePath(const std::string& app_id,
const std::string& app_class) {
const auto data_dirs = Glib::get_system_data_dirs();
for (const auto& data_dir : data_dirs) {
const auto data_app_dir = data_dir + "applications/";
auto desktop_file_path = data_app_dir + app_id + ".desktop";
if (std::filesystem::exists(desktop_file_path)) {
return desktop_file_path;
}
if (!app_class.empty()) {
desktop_file_path = data_app_dir + app_class + ".desktop";
if (std::filesystem::exists(desktop_file_path)) {
return desktop_file_path;
}
}
}
return {};
}
std::optional<Glib::ustring> getIconName(const std::string& app_id, const std::string& app_class) {
const auto desktop_file_path = getDesktopFilePath(app_id, app_class);
if (!desktop_file_path.has_value()) {
// Try some heuristics to find a matching icon
if (DefaultGtkIconThemeWrapper::has_icon(app_id)) {
return app_id;
}
const auto app_id_desktop = app_id + "-desktop";
if (DefaultGtkIconThemeWrapper::has_icon(app_id_desktop)) {
return app_id_desktop;
}
const auto to_lower = [](const std::string& str) {
auto str_cpy = str;
std::transform(str_cpy.begin(), str_cpy.end(), str_cpy.begin(),
[](unsigned char c) { return std::tolower(c); });
return str;
};
const auto first_space = app_id.find_first_of(' ');
if (first_space != std::string::npos) {
const auto first_word = to_lower(app_id.substr(0, first_space));
if (DefaultGtkIconThemeWrapper::has_icon(first_word)) {
return first_word;
}
}
const auto first_dash = app_id.find_first_of('-');
if (first_dash != std::string::npos) {
const auto first_word = to_lower(app_id.substr(0, first_dash));
if (DefaultGtkIconThemeWrapper::has_icon(first_word)) {
return first_word;
}
}
return {};
}
try {
Glib::KeyFile desktop_file;
desktop_file.load_from_file(desktop_file_path.value());
return desktop_file.get_string("Desktop Entry", "Icon");
} catch (Glib::FileError& error) {
spdlog::warn("Error while loading desktop file {}: {}", desktop_file_path.value(),
error.what().c_str());
} catch (Glib::KeyFileError& error) {
spdlog::warn("Error while loading desktop file {}: {}", desktop_file_path.value(),
error.what().c_str());
}
return {};
}
void Window::updateAppIconName() {
if (!iconEnabled()) {
return;
}
const auto icon_name = getIconName(app_id_, app_class_);
if (icon_name.has_value()) {
app_icon_name_ = icon_name.value();
} else {
app_icon_name_ = "";
}
update_app_icon_ = true;
}
void Window::updateAppIcon() {
if (update_app_icon_) {
update_app_icon_ = false;
if (app_icon_name_.empty()) {
image_.set_visible(false);
} else {
image_.set_from_icon_name(app_icon_name_, Gtk::ICON_SIZE_INVALID);
image_.set_visible(true);
}
}
}
auto Window::update() -> void { auto Window::update() -> void {
spdlog::trace("workspace layout {}, tiled count {}, floating count {}", layout_, app_nb_, spdlog::trace("workspace layout {}, tiled count {}, floating count {}", layout_, app_nb_,
floating_count_); floating_count_);
@ -210,7 +105,7 @@ auto Window::update() -> void {
updateAppIcon(); updateAppIcon();
// Call parent update // Call parent update
AIconLabel::update(); AAppIconLabel::update();
} }
void Window::setClass(std::string classname, bool enable) { void Window::setClass(std::string classname, bool enable) {

View File

@ -327,7 +327,7 @@ bool Workspaces::handleScroll(GdkEventScroll *e) {
return true; return true;
} }
} }
if (!config_["warp-on-scroll"].asBool()) { if (!config_["warp-on-scroll"].isNull() && !config_["warp-on-scroll"].asBool()) {
ipc_.sendCmd(IPC_COMMAND, fmt::format("mouse_warping none")); ipc_.sendCmd(IPC_COMMAND, fmt::format("mouse_warping none"));
} }
try { try {
@ -335,7 +335,7 @@ bool Workspaces::handleScroll(GdkEventScroll *e) {
} catch (const std::exception &e) { } catch (const std::exception &e) {
spdlog::error("Workspaces: {}", e.what()); spdlog::error("Workspaces: {}", e.what());
} }
if (!config_["warp-on-scroll"].asBool()) { if (!config_["warp-on-scroll"].isNull() && !config_["warp-on-scroll"].asBool()) {
ipc_.sendCmd(IPC_COMMAND, fmt::format("mouse_warping container")); ipc_.sendCmd(IPC_COMMAND, fmt::format("mouse_warping container"));
} }
return true; return true;

View File

@ -1,6 +1,7 @@
#include "util/prepare_for_sleep.h" #include "util/prepare_for_sleep.h"
#include <gio/gio.h> #include <gio/gio.h>
#include <spdlog/spdlog.h>
namespace { namespace {
class PrepareForSleep { class PrepareForSleep {
@ -9,7 +10,7 @@ class PrepareForSleep {
GError *error = NULL; GError *error = NULL;
login1_connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error); login1_connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
if (!login1_connection) { if (!login1_connection) {
throw std::runtime_error("Unable to connect to the SYSTEM Bus!..."); spdlog::warn("Unable to connect to the SYSTEM Bus!...");
} else { } else {
login1_id = g_dbus_connection_signal_subscribe( login1_id = g_dbus_connection_signal_subscribe(
login1_connection, "org.freedesktop.login1", "org.freedesktop.login1.Manager", login1_connection, "org.freedesktop.login1", "org.freedesktop.login1.Manager",