Merge remote-tracking branch 'origin/master' into pr-4543

# Conflicts:
#	man/waybar-wlr-taskbar.5.scd
#	src/modules/wlr/taskbar.cpp
This commit is contained in:
Alex
2026-07-04 00:38:45 +02:00
273 changed files with 11625 additions and 3277 deletions
+378 -88
View File
@@ -29,42 +29,42 @@ namespace waybar::modules::wlr {
/* Task class implementation */
uint32_t Task::global_id = 0;
static void tl_handle_title(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle,
const char *title) {
return static_cast<Task *>(data)->handle_title(title);
static void tl_handle_title(void* data, struct zwlr_foreign_toplevel_handle_v1* handle,
const char* title) {
return static_cast<Task*>(data)->handle_title(title);
}
static void tl_handle_app_id(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle,
const char *app_id) {
return static_cast<Task *>(data)->handle_app_id(app_id);
static void tl_handle_app_id(void* data, struct zwlr_foreign_toplevel_handle_v1* handle,
const char* app_id) {
return static_cast<Task*>(data)->handle_app_id(app_id);
}
static void tl_handle_output_enter(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle,
struct wl_output *output) {
return static_cast<Task *>(data)->handle_output_enter(output);
static void tl_handle_output_enter(void* data, struct zwlr_foreign_toplevel_handle_v1* handle,
struct wl_output* output) {
return static_cast<Task*>(data)->handle_output_enter(output);
}
static void tl_handle_output_leave(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle,
struct wl_output *output) {
return static_cast<Task *>(data)->handle_output_leave(output);
static void tl_handle_output_leave(void* data, struct zwlr_foreign_toplevel_handle_v1* handle,
struct wl_output* output) {
return static_cast<Task*>(data)->handle_output_leave(output);
}
static void tl_handle_state(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle,
struct wl_array *state) {
return static_cast<Task *>(data)->handle_state(state);
static void tl_handle_state(void* data, struct zwlr_foreign_toplevel_handle_v1* handle,
struct wl_array* state) {
return static_cast<Task*>(data)->handle_state(state);
}
static void tl_handle_done(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle) {
return static_cast<Task *>(data)->handle_done();
static void tl_handle_done(void* data, struct zwlr_foreign_toplevel_handle_v1* handle) {
return static_cast<Task*>(data)->handle_done();
}
static void tl_handle_parent(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle,
struct zwlr_foreign_toplevel_handle_v1 *parent) {
static void tl_handle_parent(void* data, struct zwlr_foreign_toplevel_handle_v1* handle,
struct zwlr_foreign_toplevel_handle_v1* parent) {
/* This is explicitly left blank */
}
static void tl_handle_closed(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle) {
return static_cast<Task *>(data)->handle_closed();
static void tl_handle_closed(void* data, struct zwlr_foreign_toplevel_handle_v1* handle) {
return static_cast<Task*>(data)->handle_closed();
}
static const struct zwlr_foreign_toplevel_handle_v1_listener toplevel_handle_impl = {
@@ -81,8 +81,8 @@ static const struct zwlr_foreign_toplevel_handle_v1_listener toplevel_handle_imp
static const std::vector<Gtk::TargetEntry> target_entries = {
Gtk::TargetEntry("WAYBAR_TOPLEVEL", Gtk::TARGET_SAME_APP, 0)};
Task::Task(const waybar::Bar &bar, const Json::Value &config, Taskbar *tbar,
struct zwlr_foreign_toplevel_handle_v1 *tl_handle, struct wl_seat *seat)
Task::Task(const waybar::Bar& bar, const Json::Value& config, Taskbar* tbar,
struct zwlr_foreign_toplevel_handle_v1* tl_handle, struct wl_seat* seat)
: bar_{bar},
config_{config},
tbar_{tbar},
@@ -94,9 +94,46 @@ Task::Task(const waybar::Bar &bar, const Json::Value &config, Taskbar *tbar,
button.set_relief(Gtk::RELIEF_NONE);
content_.add(text_before_);
content_.add(icon_);
content_.add(text_after_);
/* When "expand" is enabled the buttons stretch to fill the taskbar and the
* titles ellipsize to fit within the available space. This only makes sense
* on a horizontal bar; on a vertical bar the box grows along the vertical
* axis, so ellipsizing/forcing width_chars(1) would truncate every title to
* "…". Keep the historical behavior (buttons sized to their content) as the
* default and when the bar is vertical. */
bool expand = config_["expand"].isBool() && config_["expand"].asBool();
bool horizontal = bar.orientation == Gtk::ORIENTATION_HORIZONTAL;
if (expand && horizontal) {
button.set_hexpand(true);
content_.set_hexpand(true);
text_before_.set_ellipsize(Pango::ELLIPSIZE_END);
text_before_.set_single_line_mode(true);
text_before_.set_width_chars(1);
text_before_.set_xalign(0.0);
text_after_.set_ellipsize(Pango::ELLIPSIZE_END);
text_after_.set_single_line_mode(true);
text_after_.set_width_chars(1);
text_after_.set_xalign(0.0);
content_.pack_start(text_before_, true, true, 0);
content_.pack_start(icon_, false, false, 0);
content_.pack_start(text_after_, true, true, 0);
} else {
content_.add(text_before_);
content_.add(icon_);
content_.add(text_after_);
}
if (config_["justify"].isString()) {
auto justify_str = config_["justify"].asString();
if (justify_str == "left") {
content_.set_halign(Gtk::ALIGN_START);
} else if (justify_str == "right") {
content_.set_halign(Gtk::ALIGN_END);
} else if (justify_str == "center") {
content_.set_halign(Gtk::ALIGN_CENTER);
}
}
content_.show();
button.add(content_);
@@ -189,7 +226,7 @@ std::string Task::state_string(bool shortened) const {
return res.substr(0, res.size() - 1);
}
void Task::handle_title(const char *title) {
void Task::handle_title(const char* title) {
if (title_.empty()) {
spdlog::debug(fmt::format("Task ({}) setting title to {}", id_, title_));
} else {
@@ -198,7 +235,7 @@ void Task::handle_title(const char *title) {
title_ = title;
hide_if_ignored();
if (!with_icon_ && !with_name_ || app_info_) {
if ((!with_icon_ && !with_name_) || app_info_) {
return;
}
@@ -238,7 +275,7 @@ void Task::hide_if_ignored() {
}
}
void Task::handle_app_id(const char *app_id) {
void Task::handle_app_id(const char* app_id) {
if (app_id_.empty()) {
spdlog::debug(fmt::format("Task ({}) setting app_id to {}", id_, app_id));
} else {
@@ -273,34 +310,37 @@ void Task::handle_app_id(const char *app_id) {
spdlog::debug("Couldn't find icon for {}", app_id_);
}
void Task::on_button_size_allocated(Gtk::Allocation &alloc) {
void Task::on_button_size_allocated(Gtk::Allocation& alloc) {
gtk_widget_translate_coordinates(GTK_WIDGET(button.gobj()), GTK_WIDGET(bar_.window.gobj()), 0, 0,
&minimize_hint.x, &minimize_hint.y);
minimize_hint.w = button.get_width();
minimize_hint.h = button.get_height();
}
void Task::handle_output_enter(struct wl_output *output) {
void Task::handle_output_enter(struct wl_output* output) {
if (ignored_) {
spdlog::debug("{} is ignored", repr());
return;
}
spdlog::debug("{} entered output {}", repr(), (void *)output);
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 */
button.signal_size_allocate().connect_notify(
sigc::mem_fun(this, &Task::on_button_size_allocated));
tbar_->add_button(button);
button.show();
if (!config_["active-only"].asBool() || active()) {
button.show();
}
button_visible_ = true;
spdlog::debug("{} now visible on {}", repr(), bar_.output->name);
tbar_->update_bar_css_classes();
}
}
void Task::handle_output_leave(struct wl_output *output) {
spdlog::debug("{} left output {}", repr(), (void *)output);
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 */
@@ -308,14 +348,15 @@ void Task::handle_output_leave(struct wl_output *output) {
button.hide();
button_visible_ = false;
spdlog::debug("{} now invisible on {}", repr(), bar_.output->name);
tbar_->update_bar_css_classes();
}
}
void Task::handle_state(struct wl_array *state) {
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];
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;
@@ -350,6 +391,20 @@ void Task::handle_done() {
button.get_style_context()->remove_class("fullscreen");
}
if (button_visible_ && config_["active-only"].asBool()) {
if (active()) {
button.show();
} else {
button.hide();
}
}
if (active()) {
tbar_->assign_current_workspace(*this);
}
tbar_->update_bar_css_classes();
if (config_["active-first"].isBool() && config_["active-first"].asBool() && active())
tbar_->move_button(button, 0);
@@ -367,7 +422,7 @@ void Task::handle_closed() {
tbar_->remove_task(id_);
}
bool Task::handle_clicked(GdkEventButton *bt) {
bool Task::handle_clicked(GdkEventButton* bt) {
/* filter out additional events for double/triple clicks */
if (bt->type == GDK_BUTTON_PRESS) {
/* save where the button press occurred in case it becomes a drag */
@@ -412,7 +467,7 @@ bool Task::handle_clicked(GdkEventButton *bt) {
return true;
}
bool Task::handle_motion_notify(GdkEventMotion *mn) {
bool Task::handle_motion_notify(GdkEventMotion* mn) {
if (drag_start_button == -1) return false;
if (button.drag_check_threshold(drag_start_x, drag_start_y, mn->x, mn->y)) {
@@ -420,25 +475,27 @@ bool Task::handle_motion_notify(GdkEventMotion *mn) {
auto target_list = Gtk::TargetList::create(target_entries);
auto refptr = Glib::RefPtr<Gtk::TargetList>(target_list);
auto drag_context =
button.drag_begin(refptr, Gdk::DragAction::ACTION_MOVE, drag_start_button, (GdkEvent *)mn);
button.drag_begin(refptr, Gdk::DragAction::ACTION_MOVE, drag_start_button, (GdkEvent*)mn);
}
return false;
}
void Task::handle_drag_data_get(const Glib::RefPtr<Gdk::DragContext> &context,
Gtk::SelectionData &selection_data, guint info, guint time) {
void Task::handle_drag_data_get(const Glib::RefPtr<Gdk::DragContext>& context,
Gtk::SelectionData& selection_data, guint info, guint time) {
spdlog::debug("drag_data_get");
void *button_addr = (void *)&this->button;
void* button_addr = (void*)&this->button;
selection_data.set("WAYBAR_TOPLEVEL", 32, (const guchar *)&button_addr, sizeof(gpointer));
selection_data.set("WAYBAR_TOPLEVEL", 32, (const guchar*)&button_addr, sizeof(gpointer));
}
void Task::handle_drag_data_received(const Glib::RefPtr<Gdk::DragContext> &context, int x, int y,
void Task::handle_drag_data_received(const Glib::RefPtr<Gdk::DragContext>& context, int x, int y,
Gtk::SelectionData selection_data, guint info, guint time) {
spdlog::debug("drag_data_received");
gpointer handle = *(gpointer *)selection_data.get_data();
auto dragged_button = (Gtk::Button *)handle;
auto* raw = selection_data.get_data();
if (!raw || selection_data.get_length() < static_cast<int>(sizeof(gpointer))) return;
gpointer handle = *(gpointer*)raw;
auto dragged_button = (Gtk::Button*)handle;
if (dragged_button == &this->button) return;
@@ -447,7 +504,7 @@ void Task::handle_drag_data_received(const Glib::RefPtr<Gdk::DragContext> &conte
if (parent_of_dragged != parent_of_dest) return;
auto box = (Gtk::Box *)parent_of_dragged;
auto box = (Gtk::Box*)parent_of_dragged;
auto position_prop = box->child_property_position(this->button);
auto position = position_prop.get_value();
@@ -455,9 +512,9 @@ void Task::handle_drag_data_received(const Glib::RefPtr<Gdk::DragContext> &conte
box->reorder_child(*dragged_button, position);
}
bool Task::operator==(const Task &o) const { return o.id_ == id_; }
bool Task::operator==(const Task& o) const { return o.id_ == id_; }
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;
@@ -509,7 +566,7 @@ void Task::update() {
if (markup)
button.set_tooltip_markup(txt);
else
button.set_tooltip_text(txt);
button.set_tooltip_markup(txt);
}
}
@@ -545,27 +602,30 @@ void Task::fullscreen(bool set) {
void Task::close() { zwlr_foreign_toplevel_handle_v1_close(handle_); }
/* Taskbar class implementation */
static void handle_global(void *data, struct wl_registry *registry, uint32_t name,
const char *interface, uint32_t version) {
static void handle_global(void* data, struct wl_registry* registry, uint32_t name,
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);
static_cast<Taskbar*>(data)->register_manager(registry, name, version);
} else if (std::strcmp(interface, ext_workspace_manager_v1_interface.name) == 0) {
static_cast<Taskbar*>(data)->register_workspace_manager(registry, name, version);
} else if (std::strcmp(interface, wl_seat_interface.name) == 0) {
static_cast<Taskbar *>(data)->register_seat(registry, name, version);
static_cast<Taskbar*>(data)->register_seat(registry, name, version);
}
}
static void handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) {
static void handle_global_remove(void* data, struct wl_registry* registry, uint32_t name) {
/* Nothing to do here */
}
static const wl_registry_listener registry_listener_impl = {.global = handle_global,
.global_remove = handle_global_remove};
Taskbar::Taskbar(const std::string &id, const waybar::Bar &bar, const Json::Value &config)
Taskbar::Taskbar(const std::string& id, const waybar::Bar& bar, const Json::Value& config)
: waybar::AModule(config, "taskbar", id, false, false),
bar_(bar),
box_{bar.orientation, 0},
manager_{nullptr},
workspace_manager_{nullptr},
seat_{nullptr} {
box_.set_name("taskbar");
if (!id.empty()) {
@@ -581,8 +641,8 @@ Taskbar::Taskbar(const std::string &id, const waybar::Bar &bar, const Json::Valu
box_.set_hexpand(true);
}
struct wl_display *display = Client::inst()->wl_display;
struct wl_registry *registry = wl_display_get_registry(display);
struct wl_display* display = Client::inst()->wl_display;
struct wl_registry* registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, &registry_listener_impl, this);
wl_display_roundtrip(display);
@@ -598,7 +658,7 @@ Taskbar::Taskbar(const std::string &id, const waybar::Bar &bar, const Json::Valu
/* Get the configured icon theme if specified */
if (config_["icon-theme"].isArray()) {
for (auto &c : config_["icon-theme"]) {
for (auto& c : config_["icon-theme"]) {
icon_loader_.add_custom_icon_theme(c.asString());
}
} else if (config_["icon-theme"].isString()) {
@@ -607,28 +667,49 @@ Taskbar::Taskbar(const std::string &id, const waybar::Bar &bar, const Json::Valu
// Load ignore-list
if (config_["ignore-list"].isArray()) {
for (auto &app_name : config_["ignore-list"]) {
for (auto& app_name : config_["ignore-list"]) {
ignore_list_.emplace(app_name.asString());
}
}
// Load app_id remappings
if (config_["app_ids-mapping"].isObject()) {
const Json::Value &mapping = config_["app_ids-mapping"];
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) {
for (auto& app_id : app_ids) {
app_ids_replace_map_.emplace(app_id, mapping[app_id].asString());
}
}
for (auto &t : tasks_) {
for (auto& t : tasks_) {
t->handle_app_id(t->app_id().c_str());
}
}
Taskbar::~Taskbar() {
for (auto& workspace : workspaces_) {
ext_workspace_handle_v1_destroy(workspace->handle);
}
workspaces_.clear();
for (auto* group : workspace_groups_) {
ext_workspace_group_handle_v1_destroy(group);
}
workspace_groups_.clear();
if (workspace_manager_) {
struct wl_display* display = Client::inst()->wl_display;
ext_workspace_manager_v1_stop(workspace_manager_);
wl_display_roundtrip(display);
if (workspace_manager_) {
spdlog::warn("Workspace manager destroyed before .finished event");
ext_workspace_manager_v1_destroy(workspace_manager_);
workspace_manager_ = nullptr;
}
}
if (manager_) {
struct wl_display *display = Client::inst()->wl_display;
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,
@@ -643,16 +724,23 @@ Taskbar::~Taskbar() {
manager_ = nullptr;
}
}
if (config_["bar-css-states"].asBool()) {
set_bar_css_class("toplevel-active", false);
set_bar_css_class("toplevel-maximized", false);
set_bar_css_class("toplevel-minimized", false);
set_bar_css_class("toplevel-fullscreen", false);
}
}
void Taskbar::update() {
for (auto &t : tasks_) {
for (auto& t : tasks_) {
t->update();
}
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) {
[](const std::unique_ptr<Task>& a, const std::unique_ptr<Task>& b) {
return a->app_id() < b->app_id();
});
@@ -664,13 +752,13 @@ void Taskbar::update() {
AModule::update();
}
static void tm_handle_toplevel(void *data, struct zwlr_foreign_toplevel_manager_v1 *manager,
struct zwlr_foreign_toplevel_handle_v1 *tl_handle) {
return static_cast<Taskbar *>(data)->handle_toplevel_create(tl_handle);
static void tm_handle_toplevel(void* data, struct zwlr_foreign_toplevel_manager_v1* manager,
struct zwlr_foreign_toplevel_handle_v1* tl_handle) {
return static_cast<Taskbar*>(data)->handle_toplevel_create(tl_handle);
}
static void tm_handle_finished(void *data, struct zwlr_foreign_toplevel_manager_v1 *manager) {
return static_cast<Taskbar *>(data)->handle_finished();
static void tm_handle_finished(void* data, struct zwlr_foreign_toplevel_manager_v1* manager) {
return static_cast<Taskbar*>(data)->handle_finished();
}
static const struct zwlr_foreign_toplevel_manager_v1_listener toplevel_manager_impl = {
@@ -678,7 +766,81 @@ static const struct zwlr_foreign_toplevel_manager_v1_listener toplevel_manager_i
.finished = tm_handle_finished,
};
void Taskbar::register_manager(struct wl_registry *registry, uint32_t name, uint32_t version) {
static void workspace_handle_id(void*, struct ext_workspace_handle_v1*, const char*) {}
static void workspace_handle_name(void*, struct ext_workspace_handle_v1*, const char*) {}
static void workspace_handle_coordinates(void*, struct ext_workspace_handle_v1*, struct wl_array*) {
}
static void workspace_handle_state(void* data, struct ext_workspace_handle_v1*, uint32_t state) {
static_cast<Taskbar::WorkspaceState*>(data)->state = state;
}
static void workspace_handle_capabilities(void*, struct ext_workspace_handle_v1*, uint32_t) {}
static void workspace_handle_removed(void* data, struct ext_workspace_handle_v1* handle) {
static_cast<Taskbar::WorkspaceState*>(data)->taskbar->handle_workspace_removed(handle);
}
static const struct ext_workspace_handle_v1_listener workspace_handle_impl = {
.id = workspace_handle_id,
.name = workspace_handle_name,
.coordinates = workspace_handle_coordinates,
.state = workspace_handle_state,
.capabilities = workspace_handle_capabilities,
.removed = workspace_handle_removed,
};
static void workspace_group_handle_capabilities(void*, struct ext_workspace_group_handle_v1*,
uint32_t) {}
static void workspace_group_handle_output_enter(void*, struct ext_workspace_group_handle_v1*,
struct wl_output*) {}
static void workspace_group_handle_output_leave(void*, struct ext_workspace_group_handle_v1*,
struct wl_output*) {}
static void workspace_group_handle_workspace_enter(void*, struct ext_workspace_group_handle_v1*,
struct ext_workspace_handle_v1*) {}
static void workspace_group_handle_workspace_leave(void*, struct ext_workspace_group_handle_v1*,
struct ext_workspace_handle_v1*) {}
static void workspace_group_handle_removed(void* data,
struct ext_workspace_group_handle_v1* group) {
static_cast<Taskbar*>(data)->handle_workspace_group_removed(group);
}
static const struct ext_workspace_group_handle_v1_listener workspace_group_impl = {
.capabilities = workspace_group_handle_capabilities,
.output_enter = workspace_group_handle_output_enter,
.output_leave = workspace_group_handle_output_leave,
.workspace_enter = workspace_group_handle_workspace_enter,
.workspace_leave = workspace_group_handle_workspace_leave,
.removed = workspace_group_handle_removed,
};
static void workspace_manager_handle_group(void* data, struct ext_workspace_manager_v1*,
struct ext_workspace_group_handle_v1* group) {
static_cast<Taskbar*>(data)->handle_workspace_group_create(group);
}
static void workspace_manager_handle_workspace(void* data, struct ext_workspace_manager_v1*,
struct ext_workspace_handle_v1* workspace) {
static_cast<Taskbar*>(data)->handle_workspace_create(workspace);
}
static void workspace_manager_handle_done(void* data, struct ext_workspace_manager_v1*) {
static_cast<Taskbar*>(data)->handle_workspace_done();
}
static void workspace_manager_handle_finished(void* data, struct ext_workspace_manager_v1*) {
static_cast<Taskbar*>(data)->handle_workspace_finished();
}
static const struct ext_workspace_manager_v1_listener workspace_manager_impl = {
.workspace_group = workspace_manager_handle_group,
.workspace = workspace_manager_handle_workspace,
.done = workspace_manager_handle_done,
.finished = workspace_manager_handle_finished,
};
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;
@@ -693,7 +855,7 @@ void Taskbar::register_manager(struct wl_registry *registry, uint32_t name, uint
// limit version to a highest supported by the client protocol file
version = std::min<uint32_t>(version, zwlr_foreign_toplevel_manager_v1_interface.version);
manager_ = static_cast<struct zwlr_foreign_toplevel_manager_v1 *>(
manager_ = static_cast<struct zwlr_foreign_toplevel_manager_v1*>(
wl_registry_bind(registry, name, &zwlr_foreign_toplevel_manager_v1_interface, version));
if (manager_)
@@ -702,17 +864,29 @@ void Taskbar::register_manager(struct wl_registry *registry, uint32_t name, uint
spdlog::debug("Failed to register manager");
}
void Taskbar::register_seat(struct wl_registry *registry, uint32_t name, uint32_t version) {
void Taskbar::register_workspace_manager(struct wl_registry* registry, uint32_t name,
uint32_t version) {
if (workspace_manager_) {
return;
}
version = std::min<uint32_t>(version, ext_workspace_manager_v1_interface.version);
workspace_manager_ = static_cast<struct ext_workspace_manager_v1*>(
wl_registry_bind(registry, name, &ext_workspace_manager_v1_interface, version));
ext_workspace_manager_v1_add_listener(workspace_manager_, &workspace_manager_impl, this);
}
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);
seat_ = static_cast<wl_seat *>(wl_registry_bind(registry, name, &wl_seat_interface, version));
seat_ = static_cast<wl_seat*>(wl_registry_bind(registry, name, &wl_seat_interface, version));
}
void Taskbar::handle_toplevel_create(struct zwlr_foreign_toplevel_handle_v1 *tl_handle) {
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_));
}
@@ -721,21 +895,87 @@ void Taskbar::handle_finished() {
manager_ = nullptr;
}
void Taskbar::add_button(Gtk::Button &bt) {
void Taskbar::handle_workspace_group_create(struct ext_workspace_group_handle_v1* handle) {
ext_workspace_group_handle_v1_add_listener(handle, &workspace_group_impl, this);
workspace_groups_.push_back(handle);
}
// When homogeneous is enabled, allow children to expand and fill so they divide space equally.
const bool homogeneous = (config_["homogeneous"].isBool() && config_["homogeneous"].asBool());
box_.pack_start(bt, homogeneous, homogeneous);
void Taskbar::handle_workspace_group_removed(struct ext_workspace_group_handle_v1* handle) {
const auto group = std::find(workspace_groups_.begin(), workspace_groups_.end(), handle);
if (group != workspace_groups_.end()) {
ext_workspace_group_handle_v1_destroy(*group);
workspace_groups_.erase(group);
}
}
void Taskbar::handle_workspace_create(struct ext_workspace_handle_v1* handle) {
auto workspace = std::make_unique<WorkspaceState>(WorkspaceState{this, handle});
ext_workspace_handle_v1_add_listener(handle, &workspace_handle_impl, workspace.get());
workspaces_.push_back(std::move(workspace));
}
void Taskbar::handle_workspace_done() {
const auto active_workspace =
std::find_if(workspaces_.begin(), workspaces_.end(), [](const auto& workspace) {
return workspace->state & EXT_WORKSPACE_HANDLE_V1_STATE_ACTIVE;
});
current_workspace_ =
active_workspace == workspaces_.end() ? nullptr : (*active_workspace)->handle;
if (current_workspace_) {
const auto active_task =
std::find_if(tasks_.begin(), tasks_.end(), [](const auto& task) { return task->active(); });
if (active_task != tasks_.end()) {
(*active_task)->set_workspace(current_workspace_);
}
}
update_bar_css_classes();
}
void Taskbar::handle_workspace_finished() { workspace_manager_ = nullptr; }
void Taskbar::handle_workspace_removed(struct ext_workspace_handle_v1* handle) {
if (current_workspace_ == handle) {
current_workspace_ = nullptr;
}
for (auto& task : tasks_) {
if (task->workspace() == handle) {
task->set_workspace(nullptr);
}
}
const auto workspace =
std::find_if(workspaces_.begin(), workspaces_.end(),
[handle](const auto& workspace) { return workspace->handle == handle; });
if (workspace != workspaces_.end()) {
ext_workspace_handle_v1_destroy((*workspace)->handle);
workspaces_.erase(workspace);
}
update_bar_css_classes();
}
void Taskbar::add_button(Gtk::Button& bt) {
/* When "homogeneous" is enabled, let every child expand and fill so the buttons
* divide the available width equally. Otherwise, only let the buttons expand to
* fill the taskbar when "expand" is enabled and the bar is horizontal (see the
* Task constructor for details). */
const bool homogeneous = config_["homogeneous"].isBool() && config_["homogeneous"].asBool();
bool expand = config_["expand"].isBool() && config_["expand"].asBool();
bool horizontal = bar_.orientation == Gtk::ORIENTATION_HORIZONTAL;
if (homogeneous) {
box_.pack_start(bt, true, true);
bt.set_hexpand(true);
bt.set_halign(Gtk::ALIGN_FILL);
} else if (expand && horizontal) {
box_.pack_start(bt, true, true);
} else {
box_.pack_start(bt, false, false);
}
box_.get_style_context()->remove_class("empty");
}
void Taskbar::move_button(Gtk::Button &bt, int pos) { box_.reorder_child(bt, pos); }
void Taskbar::move_button(Gtk::Button& bt, int pos) { box_.reorder_child(bt, pos); }
void Taskbar::remove_button(Gtk::Button &bt) {
void Taskbar::remove_button(Gtk::Button& bt) {
box_.remove(bt);
if (box_.get_children().empty()) {
box_.get_style_context()->add_class("empty");
@@ -744,7 +984,7 @@ void Taskbar::remove_button(Gtk::Button &bt) {
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; });
[id](const TaskPtr& p) { return p->id() == id; });
if (it == std::end(tasks_)) {
spdlog::warn("Can't find task with id {}", id);
@@ -752,9 +992,59 @@ void Taskbar::remove_task(uint32_t id) {
}
tasks_.erase(it);
update_bar_css_classes();
}
bool Taskbar::show_output(struct wl_output *output) const {
void Taskbar::assign_current_workspace(Task& task) {
if (current_workspace_) {
task.set_workspace(current_workspace_);
}
}
void Taskbar::update_bar_css_classes() {
if (!config_["bar-css-states"].asBool()) {
return;
}
const auto active_task = std::find_if(tasks_.begin(), tasks_.end(), [](const TaskPtr& task) {
return task->visible() && task->active();
});
const bool has_active_task = active_task != tasks_.end();
const auto on_current_workspace = [this](const TaskPtr& task) {
if (!current_workspace_) {
return task->active();
}
return task->workspace() == current_workspace_;
};
const bool has_maximized_task =
std::any_of(tasks_.begin(), tasks_.end(), [&on_current_workspace](const TaskPtr& task) {
return task->visible() && !task->minimized() && on_current_workspace(task) &&
task->maximized();
});
const bool has_fullscreen_task =
std::any_of(tasks_.begin(), tasks_.end(), [&on_current_workspace](const TaskPtr& task) {
return task->visible() && !task->minimized() && on_current_workspace(task) &&
task->fullscreen();
});
set_bar_css_class("toplevel-active", has_active_task);
set_bar_css_class("toplevel-maximized", has_maximized_task);
set_bar_css_class("toplevel-minimized", has_active_task && (*active_task)->minimized());
set_bar_css_class("toplevel-fullscreen", has_fullscreen_task);
}
void Taskbar::set_bar_css_class(const std::string& class_name, bool enabled) {
const auto style = bar_.window.get_style_context();
if (enabled && !style->has_class(class_name)) {
spdlog::trace("Adding bar class: {}", class_name);
style->add_class(class_name);
} else if (!enabled && style->has_class(class_name)) {
spdlog::trace("Removing bar class: {}", class_name);
style->remove_class(class_name);
}
}
bool Taskbar::show_output(struct wl_output* output) const {
return output == gdk_wayland_monitor_get_wl_output(bar_.output->monitor->gobj());
}
@@ -762,11 +1052,11 @@ bool Taskbar::all_outputs() const {
return config_["all-outputs"].isBool() && config_["all-outputs"].asBool();
}
const IconLoader &Taskbar::icon_loader() const { return icon_loader_; }
const IconLoader& Taskbar::icon_loader() const { return icon_loader_; }
const std::unordered_set<std::string> &Taskbar::ignore_list() const { return ignore_list_; }
const std::unordered_set<std::string>& Taskbar::ignore_list() const { return ignore_list_; }
const std::map<std::string, std::string> &Taskbar::app_ids_replace_map() const {
const std::map<std::string, std::string>& Taskbar::app_ids_replace_map() const {
return app_ids_replace_map_;
}