2020-05-30 10:07:38 +00:00
|
|
|
#include "modules/wlr/taskbar.hpp"
|
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
#include <fmt/core.h>
|
|
|
|
#include <gdkmm/monitor.h>
|
|
|
|
#include <gio/gdesktopappinfo.h>
|
|
|
|
#include <giomm/desktopappinfo.h>
|
|
|
|
#include <gtkmm/icontheme.h>
|
|
|
|
#include <spdlog/spdlog.h>
|
2020-05-30 10:07:38 +00:00
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
#include <cctype>
|
2020-07-05 11:10:23 +00:00
|
|
|
#include <cstdlib>
|
2020-05-30 10:07:38 +00:00
|
|
|
#include <cstring>
|
|
|
|
#include <memory>
|
|
|
|
#include <sstream>
|
2021-08-22 17:18:03 +00:00
|
|
|
#include <utility>
|
2020-05-30 10:07:38 +00:00
|
|
|
|
2022-08-30 15:13:38 +00:00
|
|
|
#include "gdkmm/general.h"
|
2022-04-06 06:37:19 +00:00
|
|
|
#include "glibmm/error.h"
|
|
|
|
#include "glibmm/fileutils.h"
|
|
|
|
#include "glibmm/refptr.h"
|
|
|
|
#include "util/format.hpp"
|
|
|
|
#include "util/string.hpp"
|
2020-05-30 10:07:38 +00:00
|
|
|
|
2023-07-02 10:23:36 +00:00
|
|
|
#include "util/rewrite_string.hpp"
|
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
namespace waybar::modules::wlr {
|
2020-05-30 10:07:38 +00:00
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
/* Icon loading functions */
|
|
|
|
static std::vector<std::string> search_prefix() {
|
|
|
|
std::vector<std::string> prefixes = {""};
|
2020-05-30 10:07:38 +00:00
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
auto xdg_data_dirs = std::getenv("XDG_DATA_DIRS");
|
|
|
|
if (!xdg_data_dirs) {
|
|
|
|
prefixes.emplace_back("/usr/share/");
|
|
|
|
prefixes.emplace_back("/usr/local/share/");
|
|
|
|
} else {
|
|
|
|
std::string xdg_data_dirs_str(xdg_data_dirs);
|
|
|
|
size_t start = 0, end = 0;
|
2020-05-30 10:07:38 +00:00
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
do {
|
|
|
|
end = xdg_data_dirs_str.find(':', start);
|
|
|
|
auto p = xdg_data_dirs_str.substr(start, end - start);
|
|
|
|
prefixes.push_back(trim(p) + "/");
|
2020-05-30 10:07:38 +00:00
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
start = end == std::string::npos ? end : end + 1;
|
|
|
|
} while (end != std::string::npos);
|
|
|
|
}
|
2020-05-30 10:07:38 +00:00
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
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<Gdk::Pixbuf> 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 {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static Glib::RefPtr<Gio::DesktopAppInfo> get_app_info_by_name(const std::string &app_id) {
|
|
|
|
static std::vector<std::string> prefixes = search_prefix();
|
2020-07-05 11:10:23 +00:00
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
std::vector<std::string> app_folders = {"", "applications/", "applications/kde/",
|
|
|
|
"applications/org.kde."};
|
2020-07-05 11:10:23 +00:00
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
std::vector<std::string> suffixes = {"", ".desktop"};
|
|
|
|
|
|
|
|
for (auto &prefix : prefixes) {
|
|
|
|
for (auto &folder : app_folders) {
|
|
|
|
for (auto &suffix : suffixes) {
|
|
|
|
auto app_info_ =
|
|
|
|
Gio::DesktopAppInfo::create_from_filename(prefix + folder + app_id + suffix);
|
|
|
|
if (!app_info_) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
return app_info_;
|
|
|
|
}
|
2020-07-05 11:10:23 +00:00
|
|
|
}
|
2022-04-06 06:37:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return {};
|
|
|
|
}
|
2020-07-05 11:10:23 +00:00
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
Glib::RefPtr<Gio::DesktopAppInfo> get_desktop_app_info(const std::string &app_id) {
|
|
|
|
auto app_info = get_app_info_by_name(app_id);
|
|
|
|
if (app_info) {
|
|
|
|
return app_info;
|
|
|
|
}
|
2021-02-02 05:03:28 +00:00
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
std::string desktop_file = "";
|
2020-07-05 11:10:23 +00:00
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
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]);
|
2023-01-11 22:02:09 +00:00
|
|
|
if (!tmp_info)
|
|
|
|
// see https://github.com/Alexays/Waybar/issues/1446
|
|
|
|
continue;
|
2022-04-06 06:37:19 +00:00
|
|
|
|
2023-01-11 22:02:09 +00:00
|
|
|
auto startup_class = tmp_info->get_startup_wm_class();
|
2022-04-06 06:37:19 +00:00
|
|
|
if (startup_class == app_id) {
|
|
|
|
desktop_file = desktop_list[0][i];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
g_strfreev(desktop_list[0]);
|
|
|
|
}
|
|
|
|
g_free(desktop_list);
|
|
|
|
|
|
|
|
return get_app_info_by_name(desktop_file);
|
2020-07-05 11:10:23 +00:00
|
|
|
}
|
2020-05-30 10:07:38 +00:00
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
void Task::set_app_info_from_app_id_list(const std::string &app_id_list) {
|
|
|
|
std::string app_id;
|
|
|
|
std::istringstream stream(app_id_list);
|
|
|
|
|
|
|
|
/* Wayfire sends a list of app-id's in space separated format, other compositors
|
|
|
|
* send a single app-id, but in any case this works fine */
|
|
|
|
while (stream >> app_id) {
|
|
|
|
app_info_ = get_desktop_app_info(app_id);
|
|
|
|
if (app_info_) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto lower_app_id = app_id;
|
|
|
|
std::transform(lower_app_id.begin(), lower_app_id.end(), lower_app_id.begin(),
|
|
|
|
[](char c) { return std::tolower(c); });
|
|
|
|
app_info_ = get_desktop_app_info(lower_app_id);
|
|
|
|
if (app_info_) {
|
|
|
|
return;
|
2021-02-04 01:04:10 +00:00
|
|
|
}
|
2022-04-06 06:37:19 +00:00
|
|
|
|
|
|
|
size_t start = 0, end = app_id.size();
|
|
|
|
start = app_id.rfind(".", end);
|
|
|
|
std::string app_name = app_id.substr(start + 1, app_id.size());
|
|
|
|
app_info_ = get_desktop_app_info(app_name);
|
|
|
|
if (app_info_) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
start = app_id.find("-");
|
|
|
|
app_name = app_id.substr(0, start);
|
|
|
|
app_info_ = get_desktop_app_info(app_name);
|
|
|
|
}
|
2021-02-04 01:04:10 +00:00
|
|
|
}
|
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
static std::string get_icon_name_from_icon_theme(const Glib::RefPtr<Gtk::IconTheme> &icon_theme,
|
|
|
|
const std::string &app_id) {
|
|
|
|
if (icon_theme->lookup_icon(app_id, 24)) return app_id;
|
|
|
|
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Task::image_load_icon(Gtk::Image &image, const Glib::RefPtr<Gtk::IconTheme> &icon_theme,
|
|
|
|
Glib::RefPtr<Gio::DesktopAppInfo> app_info, int size) {
|
|
|
|
std::string ret_icon_name = "unknown";
|
|
|
|
if (app_info) {
|
|
|
|
std::string icon_name =
|
|
|
|
get_icon_name_from_icon_theme(icon_theme, app_info->get_startup_wm_class());
|
|
|
|
if (!icon_name.empty()) {
|
|
|
|
ret_icon_name = icon_name;
|
|
|
|
} else {
|
|
|
|
if (app_info->get_icon()) {
|
|
|
|
ret_icon_name = app_info->get_icon()->to_string();
|
|
|
|
}
|
2021-02-02 05:03:28 +00:00
|
|
|
}
|
2022-04-06 06:37:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Glib::RefPtr<Gdk::Pixbuf> pixbuf;
|
2022-08-30 15:13:38 +00:00
|
|
|
auto scaled_icon_size = size * image.get_scale_factor();
|
2022-04-06 06:37:19 +00:00
|
|
|
|
|
|
|
try {
|
2022-08-30 15:13:38 +00:00
|
|
|
pixbuf = icon_theme->load_icon(ret_icon_name, scaled_icon_size, Gtk::ICON_LOOKUP_FORCE_SIZE);
|
2022-04-06 06:37:19 +00:00
|
|
|
} catch (...) {
|
|
|
|
if (Glib::file_test(ret_icon_name, Glib::FILE_TEST_EXISTS))
|
2022-08-30 15:13:38 +00:00
|
|
|
pixbuf = load_icon_from_file(ret_icon_name, scaled_icon_size);
|
2022-04-06 06:37:19 +00:00
|
|
|
else
|
|
|
|
pixbuf = {};
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pixbuf) {
|
2022-08-30 15:13:38 +00:00
|
|
|
if (pixbuf->get_width() != scaled_icon_size) {
|
2022-08-31 07:50:22 +00:00
|
|
|
int width = scaled_icon_size * pixbuf->get_width() / pixbuf->get_height();
|
|
|
|
pixbuf = pixbuf->scale_simple(width, scaled_icon_size, Gdk::InterpType::INTERP_BILINEAR);
|
2022-08-30 15:13:38 +00:00
|
|
|
}
|
2022-08-31 07:50:22 +00:00
|
|
|
auto surface = Gdk::Cairo::create_surface_from_pixbuf(pixbuf, image.get_scale_factor(),
|
|
|
|
image.get_window());
|
2022-08-30 15:13:38 +00:00
|
|
|
image.set(surface);
|
2022-04-06 06:37:19 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
2020-05-30 10:07:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Task class implementation */
|
|
|
|
uint32_t Task::global_id = 0;
|
|
|
|
|
|
|
|
static void tl_handle_title(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle,
|
2022-04-06 06:37:19 +00:00
|
|
|
const char *title) {
|
|
|
|
return static_cast<Task *>(data)->handle_title(title);
|
2020-05-30 10:07:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void tl_handle_app_id(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle,
|
2022-04-06 06:37:19 +00:00
|
|
|
const char *app_id) {
|
|
|
|
return static_cast<Task *>(data)->handle_app_id(app_id);
|
2020-05-30 10:07:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void tl_handle_output_enter(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle,
|
2022-04-06 06:37:19 +00:00
|
|
|
struct wl_output *output) {
|
|
|
|
return static_cast<Task *>(data)->handle_output_enter(output);
|
2020-05-30 10:07:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void tl_handle_output_leave(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle,
|
2022-04-06 06:37:19 +00:00
|
|
|
struct wl_output *output) {
|
|
|
|
return static_cast<Task *>(data)->handle_output_leave(output);
|
2020-05-30 10:07:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void tl_handle_state(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle,
|
2022-04-06 06:37:19 +00:00
|
|
|
struct wl_array *state) {
|
|
|
|
return static_cast<Task *>(data)->handle_state(state);
|
2020-05-30 10:07:38 +00:00
|
|
|
}
|
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
static void tl_handle_done(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle) {
|
|
|
|
return static_cast<Task *>(data)->handle_done();
|
2020-05-30 10:07:38 +00:00
|
|
|
}
|
|
|
|
|
2020-12-03 20:52:20 +00:00
|
|
|
static void tl_handle_parent(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle,
|
2022-04-06 06:37:19 +00:00
|
|
|
struct zwlr_foreign_toplevel_handle_v1 *parent) {
|
|
|
|
/* This is explicitly left blank */
|
2020-12-03 20:52:20 +00:00
|
|
|
}
|
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
static void tl_handle_closed(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle) {
|
|
|
|
return static_cast<Task *>(data)->handle_closed();
|
2020-05-30 10:07:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static const struct zwlr_foreign_toplevel_handle_v1_listener toplevel_handle_impl = {
|
|
|
|
.title = tl_handle_title,
|
|
|
|
.app_id = tl_handle_app_id,
|
|
|
|
.output_enter = tl_handle_output_enter,
|
|
|
|
.output_leave = tl_handle_output_leave,
|
|
|
|
.state = tl_handle_state,
|
|
|
|
.done = tl_handle_done,
|
|
|
|
.closed = tl_handle_closed,
|
2020-12-03 20:52:20 +00:00
|
|
|
.parent = tl_handle_parent,
|
2020-05-30 10:07:38 +00:00
|
|
|
};
|
|
|
|
|
2022-10-25 19:39:23 +00:00
|
|
|
static const std::vector<Gtk::TargetEntry> target_entries = {
|
2022-10-26 15:26:15 +00:00
|
|
|
Gtk::TargetEntry("WAYBAR_TOPLEVEL", Gtk::TARGET_SAME_APP, 0)};
|
2022-10-25 19:39:23 +00:00
|
|
|
|
2020-05-30 10:07:38 +00:00
|
|
|
Task::Task(const waybar::Bar &bar, const Json::Value &config, Taskbar *tbar,
|
2022-04-06 06:37:19 +00:00
|
|
|
struct zwlr_foreign_toplevel_handle_v1 *tl_handle, struct wl_seat *seat)
|
|
|
|
: bar_{bar},
|
|
|
|
config_{config},
|
|
|
|
tbar_{tbar},
|
|
|
|
handle_{tl_handle},
|
|
|
|
seat_{seat},
|
|
|
|
id_{global_id++},
|
|
|
|
content_{bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0} {
|
|
|
|
zwlr_foreign_toplevel_handle_v1_add_listener(handle_, &toplevel_handle_impl, this);
|
|
|
|
|
2023-01-10 17:32:10 +00:00
|
|
|
button.set_relief(Gtk::RELIEF_NONE);
|
2022-04-06 06:37:19 +00:00
|
|
|
|
|
|
|
content_.add(text_before_);
|
|
|
|
content_.add(icon_);
|
|
|
|
content_.add(text_after_);
|
|
|
|
|
|
|
|
content_.show();
|
2023-01-10 17:32:10 +00:00
|
|
|
button.add(content_);
|
2022-04-06 06:37:19 +00:00
|
|
|
|
|
|
|
format_before_.clear();
|
|
|
|
format_after_.clear();
|
|
|
|
|
|
|
|
if (config_["format"].isString()) {
|
|
|
|
/* The user defined a format string, use it */
|
|
|
|
auto format = config_["format"].asString();
|
|
|
|
if (format.find("{name}") != std::string::npos) {
|
|
|
|
with_name_ = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto icon_pos = format.find("{icon}");
|
|
|
|
if (icon_pos == 0) {
|
|
|
|
with_icon_ = true;
|
|
|
|
format_after_ = format.substr(6);
|
|
|
|
} else if (icon_pos == std::string::npos) {
|
|
|
|
format_before_ = format;
|
2020-05-30 10:07:38 +00:00
|
|
|
} else {
|
2022-04-06 06:37:19 +00:00
|
|
|
with_icon_ = true;
|
|
|
|
format_before_ = format.substr(0, icon_pos);
|
|
|
|
format_after_ = format.substr(icon_pos + 6);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* The default is to only show the icon */
|
|
|
|
with_icon_ = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Strip spaces at the beginning and end of the format strings */
|
|
|
|
format_tooltip_.clear();
|
|
|
|
if (!config_["tooltip"].isBool() || config_["tooltip"].asBool()) {
|
|
|
|
if (config_["tooltip-format"].isString())
|
|
|
|
format_tooltip_ = config_["tooltip-format"].asString();
|
2020-05-30 10:07:38 +00:00
|
|
|
else
|
2022-04-06 06:37:19 +00:00
|
|
|
format_tooltip_ = "{title}";
|
|
|
|
}
|
2020-05-30 10:07:38 +00:00
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
/* Handle click events if configured */
|
|
|
|
if (config_["on-click"].isString() || config_["on-click-middle"].isString() ||
|
|
|
|
config_["on-click-right"].isString()) {
|
|
|
|
}
|
2022-10-25 19:39:23 +00:00
|
|
|
|
2023-01-10 17:32:10 +00:00
|
|
|
button.add_events(Gdk::BUTTON_PRESS_MASK);
|
|
|
|
button.signal_button_press_event().connect(sigc::mem_fun(*this, &Task::handle_clicked), false);
|
|
|
|
button.signal_button_release_event().connect(sigc::mem_fun(*this, &Task::handle_button_release),
|
2022-10-26 15:26:15 +00:00
|
|
|
false);
|
2022-10-25 19:39:23 +00:00
|
|
|
|
2023-01-10 17:32:10 +00:00
|
|
|
button.signal_motion_notify_event().connect(sigc::mem_fun(*this, &Task::handle_motion_notify),
|
|
|
|
false);
|
|
|
|
|
|
|
|
button.drag_source_set(target_entries, Gdk::BUTTON1_MASK, Gdk::ACTION_MOVE);
|
|
|
|
button.drag_dest_set(target_entries, Gtk::DEST_DEFAULT_ALL, Gdk::ACTION_MOVE);
|
2022-10-25 19:39:23 +00:00
|
|
|
|
2023-01-10 17:32:10 +00:00
|
|
|
button.signal_drag_data_get().connect(sigc::mem_fun(*this, &Task::handle_drag_data_get), false);
|
|
|
|
button.signal_drag_data_received().connect(sigc::mem_fun(*this, &Task::handle_drag_data_received),
|
|
|
|
false);
|
2021-10-19 22:23:00 +00:00
|
|
|
}
|
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
Task::~Task() {
|
|
|
|
if (handle_) {
|
|
|
|
zwlr_foreign_toplevel_handle_v1_destroy(handle_);
|
|
|
|
handle_ = nullptr;
|
|
|
|
}
|
|
|
|
if (button_visible_) {
|
2023-01-10 17:32:10 +00:00
|
|
|
tbar_->remove_button(button);
|
2022-04-06 06:37:19 +00:00
|
|
|
button_visible_ = false;
|
|
|
|
}
|
2020-05-30 10:07:38 +00:00
|
|
|
}
|
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
std::string Task::repr() const {
|
|
|
|
std::stringstream ss;
|
|
|
|
ss << "Task (" << id_ << ") " << title_ << " [" << app_id_ << "] <" << (active() ? "A" : "a")
|
|
|
|
<< (maximized() ? "M" : "m") << (minimized() ? "I" : "i") << (fullscreen() ? "F" : "f") << ">";
|
2021-10-19 22:23:00 +00:00
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
return ss.str();
|
2020-05-30 10:07:38 +00:00
|
|
|
}
|
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
std::string Task::state_string(bool shortened) const {
|
|
|
|
std::stringstream ss;
|
|
|
|
if (shortened)
|
|
|
|
ss << (minimized() ? "m" : "") << (maximized() ? "M" : "") << (active() ? "A" : "")
|
|
|
|
<< (fullscreen() ? "F" : "");
|
|
|
|
else
|
|
|
|
ss << (minimized() ? "minimized " : "") << (maximized() ? "maximized " : "")
|
|
|
|
<< (active() ? "active " : "") << (fullscreen() ? "fullscreen " : "");
|
2020-05-30 10:07:38 +00:00
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
std::string res = ss.str();
|
|
|
|
if (shortened || res.empty())
|
|
|
|
return res;
|
|
|
|
else
|
|
|
|
return res.substr(0, res.size() - 1);
|
2020-05-30 10:07:38 +00:00
|
|
|
}
|
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
void Task::handle_title(const char *title) {
|
|
|
|
title_ = title;
|
|
|
|
hide_if_ignored();
|
2020-05-30 10:07:38 +00:00
|
|
|
}
|
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
void Task::hide_if_ignored() {
|
|
|
|
if (tbar_->ignore_list().count(app_id_) || tbar_->ignore_list().count(title_)) {
|
|
|
|
ignored_ = true;
|
2020-05-30 10:07:38 +00:00
|
|
|
if (button_visible_) {
|
2022-04-06 06:37:19 +00:00
|
|
|
auto output = gdk_wayland_monitor_get_wl_output(bar_.output->monitor->gobj());
|
|
|
|
handle_output_leave(output);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
bool is_was_ignored = ignored_;
|
|
|
|
ignored_ = false;
|
|
|
|
if (is_was_ignored) {
|
|
|
|
auto output = gdk_wayland_monitor_get_wl_output(bar_.output->monitor->gobj());
|
|
|
|
handle_output_enter(output);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Task::handle_app_id(const char *app_id) {
|
|
|
|
app_id_ = app_id;
|
|
|
|
hide_if_ignored();
|
|
|
|
|
|
|
|
auto ids_replace_map = tbar_->app_ids_replace_map();
|
|
|
|
if (ids_replace_map.count(app_id_)) {
|
|
|
|
auto replaced_id = ids_replace_map[app_id_];
|
|
|
|
spdlog::debug(
|
|
|
|
fmt::format("Task ({}) [{}] app_id was replaced with {}", id_, app_id_, replaced_id));
|
|
|
|
app_id_ = replaced_id;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!with_icon_ && !with_name_) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
set_app_info_from_app_id_list(app_id_);
|
|
|
|
name_ = app_info_ ? app_info_->get_display_name() : app_id;
|
|
|
|
|
|
|
|
if (!with_icon_) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
int icon_size = config_["icon-size"].isInt() ? config_["icon-size"].asInt() : 16;
|
|
|
|
bool found = false;
|
|
|
|
for (auto &icon_theme : tbar_->icon_themes()) {
|
|
|
|
if (image_load_icon(icon_, icon_theme, app_info_, icon_size)) {
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (found)
|
|
|
|
icon_.show();
|
|
|
|
else
|
|
|
|
spdlog::debug("Couldn't find icon for {}", app_id_);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Task::handle_output_enter(struct wl_output *output) {
|
|
|
|
if (ignored_) {
|
|
|
|
spdlog::debug("{} is ignored", repr());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
spdlog::debug("{} entered output {}", repr(), (void *)output);
|
|
|
|
|
|
|
|
if (!button_visible_ && (tbar_->all_outputs() || tbar_->show_output(output))) {
|
|
|
|
/* The task entered the output of the current bar make the button visible */
|
2023-01-10 17:32:10 +00:00
|
|
|
tbar_->add_button(button);
|
|
|
|
button.show();
|
2022-04-06 06:37:19 +00:00
|
|
|
button_visible_ = true;
|
|
|
|
spdlog::debug("{} now visible on {}", repr(), bar_.output->name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Task::handle_output_leave(struct wl_output *output) {
|
|
|
|
spdlog::debug("{} left output {}", repr(), (void *)output);
|
|
|
|
|
|
|
|
if (button_visible_ && !tbar_->all_outputs() && tbar_->show_output(output)) {
|
|
|
|
/* The task left the output of the current bar, make the button invisible */
|
2023-01-10 17:32:10 +00:00
|
|
|
tbar_->remove_button(button);
|
|
|
|
button.hide();
|
2022-04-06 06:37:19 +00:00
|
|
|
button_visible_ = false;
|
|
|
|
spdlog::debug("{} now invisible on {}", repr(), bar_.output->name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Task::handle_state(struct wl_array *state) {
|
|
|
|
state_ = 0;
|
|
|
|
size_t size = state->size / sizeof(uint32_t);
|
|
|
|
for (size_t i = 0; i < size; ++i) {
|
|
|
|
auto entry = static_cast<uint32_t *>(state->data)[i];
|
|
|
|
if (entry == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED) state_ |= MAXIMIZED;
|
|
|
|
if (entry == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED) state_ |= MINIMIZED;
|
|
|
|
if (entry == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED) state_ |= ACTIVE;
|
|
|
|
if (entry == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN) state_ |= FULLSCREEN;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Task::handle_done() {
|
|
|
|
spdlog::debug("{} changed", repr());
|
|
|
|
|
|
|
|
if (state_ & MAXIMIZED) {
|
2023-01-10 17:32:10 +00:00
|
|
|
button.get_style_context()->add_class("maximized");
|
2022-04-06 06:37:19 +00:00
|
|
|
} else if (!(state_ & MAXIMIZED)) {
|
2023-01-10 17:32:10 +00:00
|
|
|
button.get_style_context()->remove_class("maximized");
|
2022-04-06 06:37:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (state_ & MINIMIZED) {
|
2023-01-10 17:32:10 +00:00
|
|
|
button.get_style_context()->add_class("minimized");
|
2022-04-06 06:37:19 +00:00
|
|
|
} else if (!(state_ & MINIMIZED)) {
|
2023-01-10 17:32:10 +00:00
|
|
|
button.get_style_context()->remove_class("minimized");
|
2022-04-06 06:37:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (state_ & ACTIVE) {
|
2023-01-10 17:32:10 +00:00
|
|
|
button.get_style_context()->add_class("active");
|
2022-04-06 06:37:19 +00:00
|
|
|
} else if (!(state_ & ACTIVE)) {
|
2023-01-10 17:32:10 +00:00
|
|
|
button.get_style_context()->remove_class("active");
|
2022-04-06 06:37:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (state_ & FULLSCREEN) {
|
2023-01-10 17:32:10 +00:00
|
|
|
button.get_style_context()->add_class("fullscreen");
|
2022-04-06 06:37:19 +00:00
|
|
|
} else if (!(state_ & FULLSCREEN)) {
|
2023-01-10 17:32:10 +00:00
|
|
|
button.get_style_context()->remove_class("fullscreen");
|
2022-04-06 06:37:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (config_["active-first"].isBool() && config_["active-first"].asBool() && active())
|
2023-01-10 17:32:10 +00:00
|
|
|
tbar_->move_button(button, 0);
|
2022-04-06 06:37:19 +00:00
|
|
|
|
|
|
|
tbar_->dp.emit();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Task::handle_closed() {
|
|
|
|
spdlog::debug("{} closed", repr());
|
|
|
|
zwlr_foreign_toplevel_handle_v1_destroy(handle_);
|
|
|
|
handle_ = nullptr;
|
2023-06-26 21:07:40 +00:00
|
|
|
tbar_->remove_task(id_);
|
2022-04-06 06:37:19 +00:00
|
|
|
if (button_visible_) {
|
2023-01-10 17:32:10 +00:00
|
|
|
tbar_->remove_button(button);
|
2022-04-06 06:37:19 +00:00
|
|
|
button_visible_ = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Task::handle_clicked(GdkEventButton *bt) {
|
2022-10-25 19:39:23 +00:00
|
|
|
/* filter out additional events for double/triple clicks */
|
|
|
|
if (bt->type == GDK_BUTTON_PRESS) {
|
|
|
|
/* save where the button press ocurred in case it becomes a drag */
|
|
|
|
drag_start_button = bt->button;
|
|
|
|
drag_start_x = bt->x;
|
|
|
|
drag_start_y = bt->y;
|
|
|
|
}
|
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
std::string action;
|
|
|
|
if (config_["on-click"].isString() && bt->button == 1)
|
|
|
|
action = config_["on-click"].asString();
|
|
|
|
else if (config_["on-click-middle"].isString() && bt->button == 2)
|
|
|
|
action = config_["on-click-middle"].asString();
|
|
|
|
else if (config_["on-click-right"].isString() && bt->button == 3)
|
|
|
|
action = config_["on-click-right"].asString();
|
|
|
|
|
|
|
|
if (action.empty())
|
2020-05-30 10:07:38 +00:00
|
|
|
return true;
|
2022-04-06 06:37:19 +00:00
|
|
|
else if (action == "activate")
|
|
|
|
activate();
|
|
|
|
else if (action == "minimize")
|
|
|
|
minimize(!minimized());
|
|
|
|
else if (action == "minimize-raise") {
|
|
|
|
if (minimized())
|
|
|
|
minimize(false);
|
|
|
|
else if (active())
|
|
|
|
minimize(true);
|
|
|
|
else
|
|
|
|
activate();
|
|
|
|
} else if (action == "maximize")
|
|
|
|
maximize(!maximized());
|
|
|
|
else if (action == "fullscreen")
|
|
|
|
fullscreen(!fullscreen());
|
|
|
|
else if (action == "close")
|
|
|
|
close();
|
|
|
|
else
|
|
|
|
spdlog::warn("Unknown action {}", action);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-10-25 19:39:23 +00:00
|
|
|
bool Task::handle_button_release(GdkEventButton *bt) {
|
|
|
|
drag_start_button = -1;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Task::handle_motion_notify(GdkEventMotion *mn) {
|
2022-10-26 15:26:15 +00:00
|
|
|
if (drag_start_button == -1) return false;
|
2022-10-25 19:39:23 +00:00
|
|
|
|
2023-01-10 17:32:10 +00:00
|
|
|
if (button.drag_check_threshold(drag_start_x, drag_start_y, mn->x, mn->y)) {
|
2022-10-25 19:39:23 +00:00
|
|
|
/* start drag in addition to other assigned action */
|
|
|
|
auto target_list = Gtk::TargetList::create(target_entries);
|
|
|
|
auto refptr = Glib::RefPtr<Gtk::TargetList>(target_list);
|
2022-10-26 15:26:15 +00:00
|
|
|
auto drag_context =
|
2023-01-10 17:32:10 +00:00
|
|
|
button.drag_begin(refptr, Gdk::DragAction::ACTION_MOVE, drag_start_button, (GdkEvent *)mn);
|
2022-10-25 19:39:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-10-26 15:26:15 +00:00
|
|
|
void Task::handle_drag_data_get(const Glib::RefPtr<Gdk::DragContext> &context,
|
|
|
|
Gtk::SelectionData &selection_data, guint info, guint time) {
|
2022-10-25 19:39:23 +00:00
|
|
|
spdlog::debug("drag_data_get");
|
2023-01-10 17:32:10 +00:00
|
|
|
void *button_addr = (void *)&this->button;
|
2022-10-25 19:39:23 +00:00
|
|
|
|
2022-10-26 15:26:15 +00:00
|
|
|
selection_data.set("WAYBAR_TOPLEVEL", 32, (const guchar *)&button_addr, sizeof(gpointer));
|
2022-10-25 19:39:23 +00:00
|
|
|
}
|
|
|
|
|
2022-10-26 15:26:15 +00:00
|
|
|
void Task::handle_drag_data_received(const Glib::RefPtr<Gdk::DragContext> &context, int x, int y,
|
|
|
|
Gtk::SelectionData selection_data, guint info, guint time) {
|
2022-10-25 19:39:23 +00:00
|
|
|
spdlog::debug("drag_data_received");
|
2022-10-26 15:26:15 +00:00
|
|
|
gpointer handle = *(gpointer *)selection_data.get_data();
|
|
|
|
auto dragged_button = (Gtk::Button *)handle;
|
2022-10-25 19:39:23 +00:00
|
|
|
|
2023-01-10 17:32:10 +00:00
|
|
|
if (dragged_button == &this->button) return;
|
2022-10-25 19:39:23 +00:00
|
|
|
|
|
|
|
auto parent_of_dragged = dragged_button->get_parent();
|
2023-01-10 17:32:10 +00:00
|
|
|
auto parent_of_dest = this->button.get_parent();
|
2022-10-25 19:39:23 +00:00
|
|
|
|
2022-10-26 15:26:15 +00:00
|
|
|
if (parent_of_dragged != parent_of_dest) return;
|
2022-10-25 19:39:23 +00:00
|
|
|
|
2022-10-26 15:26:15 +00:00
|
|
|
auto box = (Gtk::Box *)parent_of_dragged;
|
2022-10-25 19:39:23 +00:00
|
|
|
|
2023-01-10 17:32:10 +00:00
|
|
|
auto position_prop = box->child_property_position(this->button);
|
2022-10-25 19:39:23 +00:00
|
|
|
auto position = position_prop.get_value();
|
|
|
|
|
|
|
|
box->reorder_child(*dragged_button, position);
|
|
|
|
}
|
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
bool Task::operator==(const Task &o) const { return o.id_ == id_; }
|
|
|
|
|
|
|
|
bool Task::operator!=(const Task &o) const { return o.id_ != id_; }
|
|
|
|
|
|
|
|
void Task::update() {
|
|
|
|
bool markup = config_["markup"].isBool() ? config_["markup"].asBool() : false;
|
|
|
|
std::string title = title_;
|
|
|
|
std::string name = name_;
|
|
|
|
std::string app_id = app_id_;
|
|
|
|
if (markup) {
|
|
|
|
title = Glib::Markup::escape_text(title);
|
|
|
|
name = Glib::Markup::escape_text(name);
|
|
|
|
app_id = Glib::Markup::escape_text(app_id);
|
|
|
|
}
|
|
|
|
if (!format_before_.empty()) {
|
2023-01-16 21:24:55 +00:00
|
|
|
auto txt =
|
|
|
|
fmt::format(fmt::runtime(format_before_), fmt::arg("title", title), fmt::arg("name", name),
|
|
|
|
fmt::arg("app_id", app_id), fmt::arg("state", state_string()),
|
|
|
|
fmt::arg("short_state", state_string(true)));
|
2023-07-02 10:23:36 +00:00
|
|
|
|
|
|
|
txt = waybar::util::rewriteString(txt, config_["rewrite"]);
|
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
if (markup)
|
|
|
|
text_before_.set_markup(txt);
|
|
|
|
else
|
|
|
|
text_before_.set_label(txt);
|
|
|
|
text_before_.show();
|
|
|
|
}
|
|
|
|
if (!format_after_.empty()) {
|
2023-01-16 21:24:55 +00:00
|
|
|
auto txt =
|
|
|
|
fmt::format(fmt::runtime(format_after_), fmt::arg("title", title), fmt::arg("name", name),
|
|
|
|
fmt::arg("app_id", app_id), fmt::arg("state", state_string()),
|
|
|
|
fmt::arg("short_state", state_string(true)));
|
2023-07-02 10:23:36 +00:00
|
|
|
|
|
|
|
txt = waybar::util::rewriteString(txt, config_["rewrite"]);
|
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
if (markup)
|
|
|
|
text_after_.set_markup(txt);
|
2020-05-30 10:07:38 +00:00
|
|
|
else
|
2022-04-06 06:37:19 +00:00
|
|
|
text_after_.set_label(txt);
|
|
|
|
text_after_.show();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!format_tooltip_.empty()) {
|
2023-01-16 21:24:55 +00:00
|
|
|
auto txt =
|
|
|
|
fmt::format(fmt::runtime(format_tooltip_), fmt::arg("title", title), fmt::arg("name", name),
|
|
|
|
fmt::arg("app_id", app_id), fmt::arg("state", state_string()),
|
|
|
|
fmt::arg("short_state", state_string(true)));
|
2022-04-06 06:37:19 +00:00
|
|
|
if (markup)
|
2023-01-10 17:32:10 +00:00
|
|
|
button.set_tooltip_markup(txt);
|
2022-04-06 06:37:19 +00:00
|
|
|
else
|
2023-01-10 17:32:10 +00:00
|
|
|
button.set_tooltip_text(txt);
|
2022-04-06 06:37:19 +00:00
|
|
|
}
|
2020-05-30 10:07:38 +00:00
|
|
|
}
|
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
void Task::maximize(bool set) {
|
|
|
|
if (set)
|
|
|
|
zwlr_foreign_toplevel_handle_v1_set_maximized(handle_);
|
|
|
|
else
|
|
|
|
zwlr_foreign_toplevel_handle_v1_unset_maximized(handle_);
|
2020-05-30 10:07:38 +00:00
|
|
|
}
|
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
void Task::minimize(bool set) {
|
|
|
|
if (set)
|
|
|
|
zwlr_foreign_toplevel_handle_v1_set_minimized(handle_);
|
|
|
|
else
|
|
|
|
zwlr_foreign_toplevel_handle_v1_unset_minimized(handle_);
|
2020-05-30 10:07:38 +00:00
|
|
|
}
|
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
void Task::activate() { zwlr_foreign_toplevel_handle_v1_activate(handle_, seat_); }
|
2020-12-04 07:04:02 +00:00
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
void Task::fullscreen(bool set) {
|
|
|
|
if (zwlr_foreign_toplevel_handle_v1_get_version(handle_) <
|
|
|
|
ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_FULLSCREEN_SINCE_VERSION) {
|
|
|
|
spdlog::warn("Foreign toplevel manager server does not support for set/unset fullscreen.");
|
|
|
|
return;
|
|
|
|
}
|
2020-05-30 10:07:38 +00:00
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
if (set)
|
|
|
|
zwlr_foreign_toplevel_handle_v1_set_fullscreen(handle_, nullptr);
|
|
|
|
else
|
|
|
|
zwlr_foreign_toplevel_handle_v1_unset_fullscreen(handle_);
|
2020-05-30 10:07:38 +00:00
|
|
|
}
|
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
void Task::close() { zwlr_foreign_toplevel_handle_v1_close(handle_); }
|
2020-05-30 10:07:38 +00:00
|
|
|
|
|
|
|
/* Taskbar class implementation */
|
|
|
|
static void handle_global(void *data, struct wl_registry *registry, uint32_t name,
|
2022-04-06 06:37:19 +00:00
|
|
|
const char *interface, uint32_t version) {
|
|
|
|
if (std::strcmp(interface, zwlr_foreign_toplevel_manager_v1_interface.name) == 0) {
|
|
|
|
static_cast<Taskbar *>(data)->register_manager(registry, name, version);
|
|
|
|
} else if (std::strcmp(interface, wl_seat_interface.name) == 0) {
|
|
|
|
static_cast<Taskbar *>(data)->register_seat(registry, name, version);
|
|
|
|
}
|
2020-05-30 10:07:38 +00:00
|
|
|
}
|
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
static void handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) {
|
|
|
|
/* Nothing to do here */
|
2020-05-30 10:07:38 +00:00
|
|
|
}
|
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
static const wl_registry_listener registry_listener_impl = {.global = handle_global,
|
|
|
|
.global_remove = handle_global_remove};
|
2020-05-30 10:07:38 +00:00
|
|
|
|
2021-01-31 19:37:26 +00:00
|
|
|
Taskbar::Taskbar(const std::string &id, const waybar::Bar &bar, const Json::Value &config)
|
2020-05-30 10:07:38 +00:00
|
|
|
: waybar::AModule(config, "taskbar", id, false, false),
|
|
|
|
bar_(bar),
|
|
|
|
box_{bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0},
|
2022-04-06 06:37:19 +00:00
|
|
|
manager_{nullptr},
|
|
|
|
seat_{nullptr} {
|
|
|
|
box_.set_name("taskbar");
|
|
|
|
if (!id.empty()) {
|
|
|
|
box_.get_style_context()->add_class(id);
|
|
|
|
}
|
2023-06-26 21:07:40 +00:00
|
|
|
box_.get_style_context()->add_class("empty");
|
2022-04-06 06:37:19 +00:00
|
|
|
event_box_.add(box_);
|
2020-05-30 10:07:38 +00:00
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
struct wl_display *display = Client::inst()->wl_display;
|
|
|
|
struct wl_registry *registry = wl_display_get_registry(display);
|
2020-05-30 10:07:38 +00:00
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
wl_registry_add_listener(registry, ®istry_listener_impl, this);
|
|
|
|
wl_display_roundtrip(display);
|
2020-05-30 10:07:38 +00:00
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
if (!manager_) {
|
|
|
|
spdlog::error("Failed to register as toplevel manager");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!seat_) {
|
|
|
|
spdlog::error("Failed to get wayland seat");
|
|
|
|
return;
|
|
|
|
}
|
2020-05-30 10:07:38 +00:00
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
/* Get the configured icon theme if specified */
|
|
|
|
if (config_["icon-theme"].isArray()) {
|
|
|
|
for (auto &c : config_["icon-theme"]) {
|
|
|
|
auto it_name = c.asString();
|
2020-07-05 11:07:12 +00:00
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
auto it = Gtk::IconTheme::create();
|
|
|
|
it->set_custom_theme(it_name);
|
|
|
|
spdlog::debug("Use custom icon theme: {}", it_name);
|
2020-07-05 11:07:12 +00:00
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
icon_themes_.push_back(it);
|
|
|
|
}
|
|
|
|
} else if (config_["icon-theme"].isString()) {
|
|
|
|
auto it_name = config_["icon-theme"].asString();
|
2020-07-05 11:07:12 +00:00
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
auto it = Gtk::IconTheme::create();
|
|
|
|
it->set_custom_theme(it_name);
|
|
|
|
spdlog::debug("Use custom icon theme: {}", it_name);
|
2020-07-07 09:22:08 +00:00
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
icon_themes_.push_back(it);
|
|
|
|
}
|
2021-04-18 18:34:29 +00:00
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
// Load ignore-list
|
|
|
|
if (config_["ignore-list"].isArray()) {
|
|
|
|
for (auto &app_name : config_["ignore-list"]) {
|
|
|
|
ignore_list_.emplace(app_name.asString());
|
2021-04-18 18:34:29 +00:00
|
|
|
}
|
2022-04-06 06:37:19 +00:00
|
|
|
}
|
2021-04-18 18:34:29 +00:00
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
// Load app_id remappings
|
|
|
|
if (config_["app_ids-mapping"].isObject()) {
|
|
|
|
const Json::Value &mapping = config_["app_ids-mapping"];
|
|
|
|
const std::vector<std::string> app_ids = config_["app_ids-mapping"].getMemberNames();
|
|
|
|
for (auto &app_id : app_ids) {
|
|
|
|
app_ids_replace_map_.emplace(app_id, mapping[app_id].asString());
|
2021-08-22 17:18:03 +00:00
|
|
|
}
|
2022-04-06 06:37:19 +00:00
|
|
|
}
|
2021-08-22 17:18:03 +00:00
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
icon_themes_.push_back(Gtk::IconTheme::get_default());
|
2020-05-30 10:07:38 +00:00
|
|
|
}
|
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
Taskbar::~Taskbar() {
|
|
|
|
if (manager_) {
|
|
|
|
struct wl_display *display = Client::inst()->wl_display;
|
|
|
|
/*
|
|
|
|
* Send `stop` request and wait for one roundtrip.
|
|
|
|
* This is not quite correct as the protocol encourages us to wait for the .finished event,
|
|
|
|
* but it should work with wlroots foreign toplevel manager implementation.
|
|
|
|
*/
|
|
|
|
zwlr_foreign_toplevel_manager_v1_stop(manager_);
|
|
|
|
wl_display_roundtrip(display);
|
|
|
|
|
2020-05-30 10:07:38 +00:00
|
|
|
if (manager_) {
|
2022-04-06 06:37:19 +00:00
|
|
|
spdlog::warn("Foreign toplevel manager destroyed before .finished event");
|
|
|
|
zwlr_foreign_toplevel_manager_v1_destroy(manager_);
|
|
|
|
manager_ = nullptr;
|
2020-05-30 10:07:38 +00:00
|
|
|
}
|
2022-04-06 06:37:19 +00:00
|
|
|
}
|
2020-05-30 10:07:38 +00:00
|
|
|
}
|
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
void Taskbar::update() {
|
|
|
|
for (auto &t : tasks_) {
|
|
|
|
t->update();
|
|
|
|
}
|
2020-05-30 10:07:38 +00:00
|
|
|
|
2023-01-10 17:32:10 +00:00
|
|
|
if (config_["sort-by-app-id"].asBool()) {
|
|
|
|
std::stable_sort(tasks_.begin(), tasks_.end(),
|
|
|
|
[](const std::unique_ptr<Task> &a, const std::unique_ptr<Task> &b) {
|
|
|
|
return a->app_id() < b->app_id();
|
|
|
|
});
|
|
|
|
|
|
|
|
for (unsigned long i = 0; i < tasks_.size(); i++) {
|
|
|
|
move_button(tasks_[i]->button, i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
AModule::update();
|
2020-05-30 10:07:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void tm_handle_toplevel(void *data, struct zwlr_foreign_toplevel_manager_v1 *manager,
|
2022-04-06 06:37:19 +00:00
|
|
|
struct zwlr_foreign_toplevel_handle_v1 *tl_handle) {
|
|
|
|
return static_cast<Taskbar *>(data)->handle_toplevel_create(tl_handle);
|
2020-05-30 10:07:38 +00:00
|
|
|
}
|
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
static void tm_handle_finished(void *data, struct zwlr_foreign_toplevel_manager_v1 *manager) {
|
|
|
|
return static_cast<Taskbar *>(data)->handle_finished();
|
2020-05-30 10:07:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static const struct zwlr_foreign_toplevel_manager_v1_listener toplevel_manager_impl = {
|
|
|
|
.toplevel = tm_handle_toplevel,
|
|
|
|
.finished = tm_handle_finished,
|
|
|
|
};
|
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
void Taskbar::register_manager(struct wl_registry *registry, uint32_t name, uint32_t version) {
|
|
|
|
if (manager_) {
|
|
|
|
spdlog::warn("Register foreign toplevel manager again although already existing!");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (version < ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_FULLSCREEN_SINCE_VERSION) {
|
|
|
|
spdlog::warn(
|
|
|
|
"Foreign toplevel manager server does not have the appropriate version."
|
|
|
|
" To be able to use all features, you need at least version 2, but server is version {}",
|
|
|
|
version);
|
|
|
|
}
|
2020-12-03 20:52:20 +00:00
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
// limit version to a highest supported by the client protocol file
|
|
|
|
version = std::min<uint32_t>(version, zwlr_foreign_toplevel_manager_v1_interface.version);
|
2020-05-30 10:07:38 +00:00
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
manager_ = static_cast<struct zwlr_foreign_toplevel_manager_v1 *>(
|
|
|
|
wl_registry_bind(registry, name, &zwlr_foreign_toplevel_manager_v1_interface, version));
|
2020-05-30 10:07:38 +00:00
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
if (manager_)
|
|
|
|
zwlr_foreign_toplevel_manager_v1_add_listener(manager_, &toplevel_manager_impl, this);
|
|
|
|
else
|
|
|
|
spdlog::debug("Failed to register manager");
|
2020-05-30 10:07:38 +00:00
|
|
|
}
|
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
void Taskbar::register_seat(struct wl_registry *registry, uint32_t name, uint32_t version) {
|
|
|
|
if (seat_) {
|
|
|
|
spdlog::warn("Register seat again although already existing!");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
version = std::min<uint32_t>(version, wl_seat_interface.version);
|
2020-05-30 10:07:38 +00:00
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
seat_ = static_cast<wl_seat *>(wl_registry_bind(registry, name, &wl_seat_interface, version));
|
2020-05-30 10:07:38 +00:00
|
|
|
}
|
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
void Taskbar::handle_toplevel_create(struct zwlr_foreign_toplevel_handle_v1 *tl_handle) {
|
|
|
|
tasks_.push_back(std::make_unique<Task>(bar_, config_, this, tl_handle, seat_));
|
2020-05-30 10:07:38 +00:00
|
|
|
}
|
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
void Taskbar::handle_finished() {
|
|
|
|
zwlr_foreign_toplevel_manager_v1_destroy(manager_);
|
|
|
|
manager_ = nullptr;
|
2020-05-30 10:07:38 +00:00
|
|
|
}
|
|
|
|
|
2023-06-26 21:07:40 +00:00
|
|
|
void Taskbar::add_button(Gtk::Button &bt) {
|
|
|
|
box_.pack_start(bt, false, false);
|
|
|
|
box_.get_style_context()->remove_class("empty");
|
|
|
|
}
|
2020-05-30 10:07:38 +00:00
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
void Taskbar::move_button(Gtk::Button &bt, int pos) { box_.reorder_child(bt, pos); }
|
2020-05-30 10:07:38 +00:00
|
|
|
|
2023-06-26 21:07:40 +00:00
|
|
|
void Taskbar::remove_button(Gtk::Button &bt) {
|
|
|
|
box_.remove(bt);
|
|
|
|
if (tasks_.empty()) {
|
|
|
|
box_.get_style_context()->add_class("empty");
|
|
|
|
}
|
|
|
|
}
|
2020-05-30 10:07:38 +00:00
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
void Taskbar::remove_task(uint32_t id) {
|
|
|
|
auto it = std::find_if(std::begin(tasks_), std::end(tasks_),
|
|
|
|
[id](const TaskPtr &p) { return p->id() == id; });
|
2020-05-30 10:07:38 +00:00
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
if (it == std::end(tasks_)) {
|
|
|
|
spdlog::warn("Can't find task with id {}", id);
|
|
|
|
return;
|
|
|
|
}
|
2020-05-30 10:07:38 +00:00
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
tasks_.erase(it);
|
2020-05-30 10:07:38 +00:00
|
|
|
}
|
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
bool Taskbar::show_output(struct wl_output *output) const {
|
|
|
|
return output == gdk_wayland_monitor_get_wl_output(bar_.output->monitor->gobj());
|
2020-05-30 10:07:38 +00:00
|
|
|
}
|
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
bool Taskbar::all_outputs() const {
|
|
|
|
return config_["all-outputs"].isBool() && config_["all-outputs"].asBool();
|
2020-05-30 10:07:38 +00:00
|
|
|
}
|
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
const std::vector<Glib::RefPtr<Gtk::IconTheme>> &Taskbar::icon_themes() const {
|
|
|
|
return icon_themes_;
|
|
|
|
}
|
2021-08-22 17:18:03 +00:00
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
const std::unordered_set<std::string> &Taskbar::ignore_list() const { return ignore_list_; }
|
2021-08-22 17:18:03 +00:00
|
|
|
|
2022-04-06 06:37:19 +00:00
|
|
|
const std::map<std::string, std::string> &Taskbar::app_ids_replace_map() const {
|
|
|
|
return app_ids_replace_map_;
|
|
|
|
}
|
2020-05-30 10:07:38 +00:00
|
|
|
|
|
|
|
} /* namespace waybar::modules::wlr */
|