diff --git a/include/modules/sway/workspaces.hpp b/include/modules/sway/workspaces.hpp index 92ec0516..c6443836 100644 --- a/include/modules/sway/workspaces.hpp +++ b/include/modules/sway/workspaces.hpp @@ -20,7 +20,7 @@ class Workspaces : public AModule, public sigc::trackable { auto update() -> void; private: - static inline const std::string workspace_switch_cmd_ = "workspace --no-auto-back-and-forth \"{}\""; + static inline const std::string workspace_switch_cmd_ = "workspace {} \"{}\""; static int convertWorkspaceNameToNum(std::string name); diff --git a/include/util/ustring_clen.hpp b/include/util/ustring_clen.hpp new file mode 100644 index 00000000..cddd2e1a --- /dev/null +++ b/include/util/ustring_clen.hpp @@ -0,0 +1,5 @@ +#pragma once +#include + +// calculate column width of ustring +int ustring_clen(const Glib::ustring &str); \ No newline at end of file diff --git a/man/waybar-backlight.5.scd b/man/waybar-backlight.5.scd index e6116e3e..d14e4f24 100644 --- a/man/waybar-backlight.5.scd +++ b/man/waybar-backlight.5.scd @@ -24,6 +24,14 @@ The *backlight* module displays the current backlight level. typeof: integer ++ The maximum length in characters the module should display. +*min-length*: ++ + typeof: integer ++ + The minimum length in characters the module should take up. + +*align*: ++ + typeof: float ++ + The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + *rotate*: ++ typeof: integer ++ Positive value to rotate the text label. diff --git a/man/waybar-battery.5.scd b/man/waybar-battery.5.scd index a4650cd6..48c2ee19 100644 --- a/man/waybar-battery.5.scd +++ b/man/waybar-battery.5.scd @@ -55,6 +55,14 @@ The *battery* module displays the current capacity and state (eg. charging) of y typeof: integer++ The maximum length in character the module should display. +*min-length*: ++ + typeof: integer ++ + The minimum length in characters the module should take up. + +*align*: ++ + typeof: float ++ + The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + *rotate*: ++ typeof: integer++ Positive value to rotate the text label. diff --git a/man/waybar-bluetooth.5.scd b/man/waybar-bluetooth.5.scd index 5d7d7dd5..0cd9386a 100644 --- a/man/waybar-bluetooth.5.scd +++ b/man/waybar-bluetooth.5.scd @@ -35,6 +35,14 @@ Addressed by *bluetooth* typeof: integer ++ The maximum length in character the module should display. +*min-length*: ++ + typeof: integer ++ + The minimum length in characters the module should take up. + +*align*: ++ + typeof: float ++ + The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + *on-click*: ++ typeof: string ++ Command to execute when clicked on the module. diff --git a/man/waybar-clock.5.scd b/man/waybar-clock.5.scd index 9f36c435..28688ee9 100644 --- a/man/waybar-clock.5.scd +++ b/man/waybar-clock.5.scd @@ -45,6 +45,14 @@ The *clock* module displays the current date and time. typeof: integer ++ The maximum length in character the module should display. +*min-length*: ++ + typeof: integer ++ + The minimum length in characters the module should take up. + +*align*: ++ + typeof: float ++ + The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + *rotate*: ++ typeof: integer ++ Positive value to rotate the text label. diff --git a/man/waybar-cpu.5.scd b/man/waybar-cpu.5.scd index cb83134f..c8b12e26 100644 --- a/man/waybar-cpu.5.scd +++ b/man/waybar-cpu.5.scd @@ -24,6 +24,14 @@ The *cpu* module displays the current cpu utilization. typeof: integer ++ The maximum length in character the module should display. +*min-length*: ++ + typeof: integer ++ + The minimum length in characters the module should take up. + +*align*: ++ + typeof: float ++ + The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + *rotate*: ++ typeof: integer ++ Positive value to rotate the text label. diff --git a/man/waybar-custom.5.scd b/man/waybar-custom.5.scd index 3e820c63..8f9dcfaf 100644 --- a/man/waybar-custom.5.scd +++ b/man/waybar-custom.5.scd @@ -67,6 +67,14 @@ Addressed by *custom/* typeof: integer ++ The maximum length in character the module should display. +*min-length*: ++ + typeof: integer ++ + The minimum length in characters the module should take up. + +*align*: ++ + typeof: float ++ + The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + *on-click*: ++ typeof: string ++ Command to execute when clicked on the module. diff --git a/man/waybar-disk.5.scd b/man/waybar-disk.5.scd index 431d7c8f..58797141 100644 --- a/man/waybar-disk.5.scd +++ b/man/waybar-disk.5.scd @@ -39,6 +39,14 @@ Addressed by *disk* typeof: integer ++ The maximum length in character the module should display. +*min-length*: ++ + typeof: integer ++ + The minimum length in characters the module should take up. + +*align*: ++ + typeof: float ++ + The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + *on-click*: ++ typeof: string ++ Command to execute when clicked on the module. diff --git a/man/waybar-idle-inhibitor.5.scd b/man/waybar-idle-inhibitor.5.scd index 9d231d86..0b0bdd03 100644 --- a/man/waybar-idle-inhibitor.5.scd +++ b/man/waybar-idle-inhibitor.5.scd @@ -27,6 +27,14 @@ screensaving, also known as "presentation mode". typeof: integer ++ The maximum length in character the module should display. +*min-length*: ++ + typeof: integer ++ + The minimum length in characters the module should take up. + +*align*: ++ + typeof: float ++ + The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + *on-click*: ++ typeof: string ++ Command to execute when clicked on the module. A click also toggles the state diff --git a/man/waybar-memory.5.scd b/man/waybar-memory.5.scd index 81c62165..3ff4c35b 100644 --- a/man/waybar-memory.5.scd +++ b/man/waybar-memory.5.scd @@ -34,6 +34,14 @@ Addressed by *memory* typeof: integer ++ The maximum length in character the module should display. +*min-length*: ++ + typeof: integer ++ + The minimum length in characters the module should take up. + +*align*: ++ + typeof: float ++ + The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + *on-click*: ++ typeof: string ++ Command to execute when clicked on the module. diff --git a/man/waybar-mpd.5.scd b/man/waybar-mpd.5.scd index 3f1ef9be..b8e96648 100644 --- a/man/waybar-mpd.5.scd +++ b/man/waybar-mpd.5.scd @@ -97,6 +97,14 @@ Addressed by *mpd* typeof: integer ++ The maximum length in character the module should display. +*min-length*: ++ + typeof: integer ++ + The minimum length in characters the module should take up. + +*align*: ++ + typeof: float ++ + The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + *on-click*: ++ typeof: string ++ Command to execute when clicked on the module. diff --git a/man/waybar-network.5.scd b/man/waybar-network.5.scd index ab459ae0..f274881e 100644 --- a/man/waybar-network.5.scd +++ b/man/waybar-network.5.scd @@ -64,6 +64,14 @@ Addressed by *network* typeof: integer ++ The maximum length in character the module should display. +*min-length*: ++ + typeof: integer ++ + The minimum length in characters the module should take up. + +*align*: ++ + typeof: float ++ + The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + *on-click*: ++ typeof: string ++ Command to execute when clicked on the module. diff --git a/man/waybar-pulseaudio.5.scd b/man/waybar-pulseaudio.5.scd index c3f50e0b..d894290f 100644 --- a/man/waybar-pulseaudio.5.scd +++ b/man/waybar-pulseaudio.5.scd @@ -50,6 +50,14 @@ Additionally you can control the volume by scrolling *up* or *down* while the cu typeof: integer ++ The maximum length in character the module should display. +*min-length*: ++ + typeof: integer ++ + The minimum length in characters the module should take up. + +*align*: ++ + typeof: float ++ + The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + *scroll-step*: ++ typeof: float ++ default: 1.0 ++ diff --git a/man/waybar-sndio.5.scd b/man/waybar-sndio.5.scd index a61c3325..90a73f48 100644 --- a/man/waybar-sndio.5.scd +++ b/man/waybar-sndio.5.scd @@ -26,6 +26,14 @@ cursor is over the module, and clicking on the module toggles mute. typeof: integer ++ The maximum length in character the module should display. +*min-length*: ++ + typeof: integer ++ + The minimum length in characters the module should take up. + +*align*: ++ + typeof: float ++ + The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + *scroll-step*: ++ typeof: int ++ default: 5 ++ diff --git a/man/waybar-sway-language.5.scd b/man/waybar-sway-language.5.scd index a288cca9..769924f0 100644 --- a/man/waybar-sway-language.5.scd +++ b/man/waybar-sway-language.5.scd @@ -25,6 +25,14 @@ Addressed by *sway/language* typeof: integer ++ The maximum length in character the module should display. +*min-length*: ++ + typeof: integer ++ + The minimum length in characters the module should take up. + +*align*: ++ + typeof: float ++ + The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + *on-click*: ++ typeof: string ++ Command to execute when clicked on the module. diff --git a/man/waybar-sway-mode.5.scd b/man/waybar-sway-mode.5.scd index 958a1edb..b8b59cd3 100644 --- a/man/waybar-sway-mode.5.scd +++ b/man/waybar-sway-mode.5.scd @@ -25,6 +25,14 @@ Addressed by *sway/mode* typeof: integer ++ The maximum length in character the module should display. +*min-length*: ++ + typeof: integer ++ + The minimum length in characters the module should take up. + +*align*: ++ + typeof: float ++ + The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + *on-click*: ++ typeof: string ++ Command to execute when clicked on the module. diff --git a/man/waybar-sway-window.5.scd b/man/waybar-sway-window.5.scd index 4863a76c..40250e6f 100644 --- a/man/waybar-sway-window.5.scd +++ b/man/waybar-sway-window.5.scd @@ -25,6 +25,14 @@ Addressed by *sway/window* typeof: integer ++ The maximum length in character the module should display. +*min-length*: ++ + typeof: integer ++ + The minimum length in characters the module should take up. + +*align*: ++ + typeof: float ++ + The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + *on-click*: ++ typeof: string ++ Command to execute when clicked on the module. diff --git a/man/waybar-sway-workspaces.5.scd b/man/waybar-sway-workspaces.5.scd index 5e516892..f2808b90 100644 --- a/man/waybar-sway-workspaces.5.scd +++ b/man/waybar-sway-workspaces.5.scd @@ -66,12 +66,16 @@ Addressed by *sway/workspaces* Lists workspaces that should always be shown, even when non existent *on-update*: ++ - typeof: string ++ - Command to execute when the module is updated. + typeof: string ++ + Command to execute when the module is updated. *numeric-first*: ++ - typeof: bool ++ - Whether to put workspaces starting with numbers before workspaces that do not start with a number. + typeof: bool ++ + Whether to put workspaces starting with numbers before workspaces that do not start with a number. + +*disable-auto-back-and-forth*: ++ + typeof: bool ++ + Whether to disable *workspace_auto_back_and_forth* when clicking on workspaces. If this is set to *true*, clicking on a workspace you are already on won't do anything, even if *workspace_auto_back_and_forth* is enabled in the Sway configuration. # FORMAT REPLACEMENTS diff --git a/man/waybar-temperature.5.scd b/man/waybar-temperature.5.scd index 7810a59b..8d11e517 100644 --- a/man/waybar-temperature.5.scd +++ b/man/waybar-temperature.5.scd @@ -63,6 +63,14 @@ Addressed by *temperature* typeof: integer ++ The maximum length in characters the module should display. +*min-length*: ++ + typeof: integer ++ + The minimum length in characters the module should take up. + +*align*: ++ + typeof: float ++ + The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + *on-click*: ++ typeof: string ++ Command to execute when you clicked on the module. diff --git a/meson.build b/meson.build index 49e71f26..373ec86b 100644 --- a/meson.build +++ b/meson.build @@ -147,6 +147,7 @@ src_files = files( 'src/main.cpp', 'src/bar.cpp', 'src/client.cpp', + 'src/util/ustring_clen.cpp' ) if is_linux diff --git a/src/ALabel.cpp b/src/ALabel.cpp index 9371a0e7..8d9c9b41 100644 --- a/src/ALabel.cpp +++ b/src/ALabel.cpp @@ -20,7 +20,7 @@ ALabel::ALabel(const Json::Value& config, const std::string& name, const std::st } event_box_.add(label_); if (config_["max-length"].isUInt()) { - label_.set_max_width_chars(config_["max-length"].asUInt()); + label_.set_max_width_chars(config_["max-length"].asInt()); label_.set_ellipsize(Pango::EllipsizeMode::ELLIPSIZE_END); label_.set_single_line_mode(true); } else if (ellipsize && label_.get_max_width_chars() == -1) { @@ -28,9 +28,28 @@ ALabel::ALabel(const Json::Value& config, const std::string& name, const std::st label_.set_single_line_mode(true); } - if (config_["rotate"].isUInt()) { - label_.set_angle(config["rotate"].asUInt()); + if (config_["min-length"].isUInt()) { + label_.set_width_chars(config_["min-length"].asUInt()); } + + uint rotate = 0; + + if (config_["rotate"].isUInt()) { + rotate = config["rotate"].asUInt(); + label_.set_angle(rotate); + } + + if (config_["align"].isDouble()) { + auto align = config_["align"].asFloat(); + if (rotate == 90 || rotate == 270) { + label_.set_yalign(align); + } else { + label_.set_xalign(align); + } + + } + + } auto ALabel::update() -> void { diff --git a/src/client.cpp b/src/client.cpp index 0ad4e6bb..fcfcd98c 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -120,17 +120,26 @@ void waybar::Client::handleOutputDone(void *data, struct zxdg_output_v1 * /*xdg_ auto client = waybar::Client::inst(); try { auto &output = client->getOutput(data); - spdlog::debug("Output detection done: {} ({})", output.name, output.identifier); + /** + * Multiple .done events may arrive in batch. In this case libwayland would queue + * xdg_output.destroy and dispatch all pending events, triggering this callback several times + * for the same output. .done events can also arrive after that for a scale or position changes. + * We wouldn't want to draw a duplicate bar for each such event either. + * + * All the properties we care about are immutable so it's safe to delete the xdg_output object + * on the first event and use the ptr value to check that the callback was already invoked. + */ + if (output.xdg_output) { + output.xdg_output.reset(); + spdlog::debug("Output detection done: {} ({})", output.name, output.identifier); - auto configs = client->getOutputConfigs(output); - if (!configs.empty()) { - wl_display_roundtrip(client->wl_display); - for (const auto &config : configs) { - client->bars.emplace_back(std::make_unique(&output, config)); + auto configs = client->getOutputConfigs(output); + if (!configs.empty()) { + for (const auto &config : configs) { + client->bars.emplace_back(std::make_unique(&output, config)); + } } } - // unsubscribe - output.xdg_output.reset(); } catch (const std::exception &e) { std::cerr << e.what() << std::endl; } diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 5b2c3f43..22bedc78 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -5,6 +5,7 @@ #include #include +#include "util/ustring_clen.hpp" #ifdef HAVE_LANGINFO_1STDAY #include #include @@ -154,12 +155,14 @@ auto waybar::modules::Clock::weekdays_header(const date::weekday& first_dow, std do { if (wd != first_dow) os << ' '; Glib::ustring wd_ustring(date::format(locale_, "%a", wd)); - auto wd_len = wd_ustring.length(); - if (wd_len > 2) { - wd_ustring = wd_ustring.substr(0, 2); - wd_len = 2; + auto clen = ustring_clen(wd_ustring); + auto wd_len = wd_ustring.length(); + while (clen > 2) { + wd_ustring = wd_ustring.substr(0, wd_len-1); + wd_len--; + clen = ustring_clen(wd_ustring); } - const std::string pad(2 - wd_len, ' '); + const std::string pad(2 - clen, ' '); os << pad << wd_ustring; } while (++wd != first_dow); os << "\n"; diff --git a/src/modules/idle_inhibitor.cpp b/src/modules/idle_inhibitor.cpp index 9978bbac..26889c23 100644 --- a/src/modules/idle_inhibitor.cpp +++ b/src/modules/idle_inhibitor.cpp @@ -12,6 +12,10 @@ waybar::modules::IdleInhibitor::IdleInhibitor(const std::string& id, const Bar& bar_(bar), idle_inhibitor_(nullptr), pid_(-1) { + if (waybar::Client::inst()->idle_inhibit_manager == nullptr) { + throw std::runtime_error("idle-inhibit not available"); + } + event_box_.add_events(Gdk::BUTTON_PRESS_MASK); event_box_.signal_button_press_event().connect( sigc::mem_fun(*this, &IdleInhibitor::handleToggle)); diff --git a/src/modules/sway/window.cpp b/src/modules/sway/window.cpp index e9203451..d8f113f7 100644 --- a/src/modules/sway/window.cpp +++ b/src/modules/sway/window.cpp @@ -56,7 +56,8 @@ auto Window::update() -> void { bar_.window.get_style_context()->remove_class("solo"); bar_.window.get_style_context()->remove_class("empty"); } - label_.set_markup(fmt::format(format_, window_)); + label_.set_markup(fmt::format(format_, fmt::arg("title", window_), + fmt::arg("app_id", app_id_))); if (tooltipEnabled()) { label_.set_tooltip_text(window_); } diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index d0c24632..43dcf33c 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -257,11 +257,19 @@ Gtk::Button &Workspaces::addButton(const Json::Value &node) { ipc_.sendCmd( IPC_COMMAND, fmt::format(workspace_switch_cmd_ + "; move workspace to output \"{}\"; " + workspace_switch_cmd_, + "--no-auto-back-and-forth", node["name"].asString(), node["target_output"].asString(), + "--no-auto-back-and-forth", node["name"].asString())); } else { - ipc_.sendCmd(IPC_COMMAND, fmt::format(workspace_switch_cmd_, node["name"].asString())); + ipc_.sendCmd( + IPC_COMMAND, + fmt::format("workspace {} \"{}\"", + config_["disable-auto-back-and-forth"].asBool() + ? "--no-auto-back-and-forth" + : "", + node["name"].asString())); } } catch (const std::exception &e) { spdlog::error("Workspaces: {}", e.what()); @@ -322,7 +330,9 @@ bool Workspaces::handleScroll(GdkEventScroll *e) { } } try { - ipc_.sendCmd(IPC_COMMAND, fmt::format(workspace_switch_cmd_, name)); + ipc_.sendCmd( + IPC_COMMAND, + fmt::format(workspace_switch_cmd_, "--no-auto-back-and-forth", name)); } catch (const std::exception &e) { spdlog::error("Workspaces: {}", e.what()); } diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index 83ce3662..ef46f36a 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -1,5 +1,7 @@ #include "modules/wlr/taskbar.hpp" +#include "glibmm/error.h" +#include "glibmm/fileutils.h" #include "glibmm/refptr.h" #include "util/format.hpp" @@ -15,6 +17,7 @@ #include #include +#include #include @@ -64,12 +67,25 @@ static std::vector search_prefix() } while(end != std::string::npos); } + std::string home_dir = std::getenv("HOME"); + prefixes.push_back(home_dir + "/.local/share/"); + for (auto& p : prefixes) spdlog::debug("Using 'desktop' search path prefix: {}", p); return prefixes; } +static Glib::RefPtr load_icon_from_file(std::string icon_path, int size) +{ + try { + auto pb = Gdk::Pixbuf::create_from_file(icon_path, size, size); + return pb; + } catch(...) { + return {}; + } +} + /* Method 1 - get the correct icon name from the desktop file */ static std::string get_from_desktop_app_info(const std::string &app_id) { @@ -111,6 +127,33 @@ static std::string get_from_icon_theme(const Glib::RefPtr& icon_ return ""; } +/* Method 3 - as last resort perform a search for most appropriate desktop info file */ +static std::string get_from_desktop_app_info_search(const std::string &app_id) +{ + std::string desktop_file = ""; + + gchar*** desktop_list = g_desktop_app_info_search(app_id.c_str()); + if (desktop_list != nullptr && desktop_list[0] != nullptr) { + for (size_t i=0; desktop_list[0][i]; i++) { + if (desktop_file == "") { + desktop_file = desktop_list[0][i]; + } else { + auto tmp_info = Gio::DesktopAppInfo::create(desktop_list[0][i]); + auto startup_class = tmp_info->get_startup_wm_class(); + + if (startup_class == app_id) { + desktop_file = desktop_list[0][i]; + break; + } + } + } + g_strfreev(desktop_list[0]); + } + g_free(desktop_list); + + return get_from_desktop_app_info(desktop_file); +} + static bool image_load_icon(Gtk::Image& image, const Glib::RefPtr& icon_theme, const std::string &app_id_list, int size) { @@ -142,11 +185,23 @@ static bool image_load_icon(Gtk::Image& image, const Glib::RefPtr pixbuf; + + try { + pixbuf = icon_theme->load_icon(icon_name, size, Gtk::ICON_LOOKUP_FORCE_SIZE); + } catch(...) { + if (Glib::file_test(icon_name, Glib::FILE_TEST_EXISTS)) + pixbuf = load_icon_from_file(icon_name, size); + else + pixbuf = {}; + } - auto pixbuf = icon_theme->load_icon(icon_name, size, Gtk::ICON_LOOKUP_FORCE_SIZE); if (pixbuf) { image.set(pixbuf); found = true; diff --git a/src/util/ustring_clen.cpp b/src/util/ustring_clen.cpp new file mode 100644 index 00000000..cd7d9cf5 --- /dev/null +++ b/src/util/ustring_clen.cpp @@ -0,0 +1,9 @@ +#include "util/ustring_clen.hpp" + +int ustring_clen(const Glib::ustring &str){ + int total = 0; + for (auto i = str.begin(); i != str.end(); ++i) { + total += g_unichar_iswide(*i) + 1; + } + return total; +} \ No newline at end of file