diff --git a/include/bar.hpp b/include/bar.hpp index 7566b088..60c5283c 100644 --- a/include/bar.hpp +++ b/include/bar.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -15,10 +16,11 @@ namespace waybar { class Factory; struct waybar_output { - struct wl_output * output = nullptr; - std::string name; - uint32_t wl_name; - struct zxdg_output_v1 *xdg_output = nullptr; + Glib::RefPtr monitor; + std::string name; + + std::unique_ptr xdg_output = { + nullptr, &zxdg_output_v1_destroy}; }; class Bar { diff --git a/include/client.hpp b/include/client.hpp index 715ae586..65a814fc 100644 --- a/include/client.hpp +++ b/include/client.hpp @@ -34,17 +34,15 @@ class Client { bool isValidOutput(const Json::Value &config, std::unique_ptr &output); auto setupConfig(const std::string &config_file) -> void; auto setupCss(const std::string &css_file) -> void; - std::unique_ptr &getOutput(uint32_t wl_name); + std::unique_ptr &getOutput(void *); std::vector getOutputConfigs(std::unique_ptr &output); static void handleGlobal(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version); static void handleGlobalRemove(void *data, struct wl_registry *registry, uint32_t name); - static void handleLogicalPosition(void *, struct zxdg_output_v1 *, int32_t, int32_t); - static void handleLogicalSize(void *, struct zxdg_output_v1 *, int32_t, int32_t); - static void handleDone(void *, struct zxdg_output_v1 *); - static void handleName(void *, struct zxdg_output_v1 *, const char *); - static void handleDescription(void *, struct zxdg_output_v1 *, const char *); + static void handleOutputName(void *, struct zxdg_output_v1 *, const char *); + void handleMonitorAdded(Glib::RefPtr monitor); + void handleMonitorRemoved(Glib::RefPtr monitor); Json::Value config_; Glib::RefPtr style_context_; diff --git a/meson.build b/meson.build index a9d44eeb..f7ae5d8c 100644 --- a/meson.build +++ b/meson.build @@ -52,7 +52,7 @@ spdlog = dependency('spdlog', version : ['>=1.3.1'], fallback : ['spdlog', 'spdl wayland_client = dependency('wayland-client') wayland_cursor = dependency('wayland-cursor') wayland_protos = dependency('wayland-protocols') -gtkmm = dependency('gtkmm-3.0') +gtkmm = dependency('gtkmm-3.0', version : ['>=3.22.0']) dbusmenu_gtk = dependency('dbusmenu-gtk3-0.4', required: get_option('dbusmenu-gtk')) giounix = dependency('gio-unix-2.0', required: get_option('dbusmenu-gtk')) jsoncpp = dependency('jsoncpp') diff --git a/src/bar.cpp b/src/bar.cpp index fc885119..15bdeeaf 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -146,10 +146,12 @@ void waybar::Bar::onMap(GdkEventAny* ev) { surface = gdk_wayland_window_get_wl_surface(gdk_window); auto client = waybar::Client::inst(); + // owned by output->monitor; no need to destroy + auto wl_output = gdk_wayland_monitor_get_wl_output(output->monitor->gobj()); auto layer = config["layer"] == "top" ? ZWLR_LAYER_SHELL_V1_LAYER_TOP : ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM; layer_surface = zwlr_layer_shell_v1_get_layer_surface( - client->layer_shell, surface, output->output, layer, "waybar"); + client->layer_shell, surface, wl_output, layer, "waybar"); zwlr_layer_surface_v1_set_keyboard_interactivity(layer_surface, false); zwlr_layer_surface_v1_set_anchor(layer_surface, anchor_); diff --git a/src/client.cpp b/src/client.cpp index 34efdf10..dd4cd50e 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -1,4 +1,5 @@ #include "client.hpp" +#include #include #include #include @@ -33,11 +34,6 @@ void waybar::Client::handleGlobal(void *data, struct wl_registry *registry, uint if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { client->layer_shell = static_cast( wl_registry_bind(registry, name, &zwlr_layer_shell_v1_interface, version)); - } else if (strcmp(interface, wl_output_interface.name) == 0) { - auto wl_output = static_cast( - wl_registry_bind(registry, name, &wl_output_interface, version)); - client->outputs_.emplace_back(new struct waybar_output({wl_output, "", name, nullptr})); - client->handleOutput(client->outputs_.back()); } else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0 && version >= ZXDG_OUTPUT_V1_NAME_SINCE_VERSION) { client->xdg_output_manager = static_cast(wl_registry_bind( @@ -50,58 +46,21 @@ void waybar::Client::handleGlobal(void *data, struct wl_registry *registry, uint void waybar::Client::handleGlobalRemove(void * data, struct wl_registry * /*registry*/, uint32_t name) { - auto client = static_cast(data); - for (auto it = client->bars.begin(); it != client->bars.end();) { - if ((*it)->output->wl_name == name) { - auto output_name = (*it)->output->name; - (*it)->window.close(); - it = client->bars.erase(it); - spdlog::info("Bar removed from output: {}", output_name); - } else { - ++it; - } - } - auto it = std::find_if(client->outputs_.begin(), - client->outputs_.end(), - [&name](const auto &output) { return output->wl_name == name; }); - if (it != client->outputs_.end()) { - if ((*it)->xdg_output != nullptr) { - zxdg_output_v1_destroy((*it)->xdg_output); - (*it)->xdg_output = nullptr; - } - if ((*it)->output != nullptr) { - wl_output_destroy((*it)->output); - (*it)->output = nullptr; - } - client->outputs_.erase(it); - } + // Nothing here } void waybar::Client::handleOutput(std::unique_ptr &output) { static const struct zxdg_output_v1_listener xdgOutputListener = { - .logical_position = handleLogicalPosition, - .logical_size = handleLogicalSize, - .done = handleDone, - .name = handleName, - .description = handleDescription, + .logical_position = [](void *, struct zxdg_output_v1 *, int32_t, int32_t) {}, + .logical_size = [](void *, struct zxdg_output_v1 *, int32_t, int32_t) {}, + .done = [](void *, struct zxdg_output_v1 *) {}, + .name = &handleOutputName, + .description = [](void *, struct zxdg_output_v1 *, const char *) {}, }; - output->xdg_output = zxdg_output_manager_v1_get_xdg_output(xdg_output_manager, output->output); - zxdg_output_v1_add_listener(output->xdg_output, &xdgOutputListener, &output->wl_name); -} - -void waybar::Client::handleLogicalPosition(void * /*data*/, - struct zxdg_output_v1 * /*zxdg_output_v1*/, - int32_t /*x*/, int32_t /*y*/) { - // Nothing here -} - -void waybar::Client::handleLogicalSize(void * /*data*/, struct zxdg_output_v1 * /*zxdg_output_v1*/, - int32_t /*width*/, int32_t /*height*/) { - // Nothing here -} - -void waybar::Client::handleDone(void * /*data*/, struct zxdg_output_v1 * /*zxdg_output_v1*/) { - // Nothing here + // owned by output->monitor; no need to destroy + auto wl_output = gdk_wayland_monitor_get_wl_output(output->monitor->gobj()); + output->xdg_output.reset(zxdg_output_manager_v1_get_xdg_output(xdg_output_manager, wl_output)); + zxdg_output_v1_add_listener(output->xdg_output.get(), &xdgOutputListener, output.get()); } bool waybar::Client::isValidOutput(const Json::Value & config, @@ -123,9 +82,9 @@ bool waybar::Client::isValidOutput(const Json::Value & config return found; } -std::unique_ptr &waybar::Client::getOutput(uint32_t wl_name) { - auto it = std::find_if(outputs_.begin(), outputs_.end(), [&wl_name](const auto &output) { - return output->wl_name == wl_name; +std::unique_ptr &waybar::Client::getOutput(void *addr) { + auto it = std::find_if(outputs_.begin(), outputs_.end(), [&addr](const auto &output) { + return output.get() == addr; }); if (it == outputs_.end()) { throw std::runtime_error("Unable to find valid output"); @@ -148,23 +107,19 @@ std::vector waybar::Client::getOutputConfigs( return configs; } -void waybar::Client::handleName(void * data, struct zxdg_output_v1 * /*xdg_output*/, - const char *name) { - auto wl_name = *static_cast(data); +void waybar::Client::handleOutputName(void * data, struct zxdg_output_v1 * /*xdg_output*/, + const char *name) { auto client = waybar::Client::inst(); try { - auto &output = client->getOutput(wl_name); + auto &output = client->getOutput(data); output->name = name; + spdlog::debug("Output detected: {} ({} {})", + name, + output->monitor->get_manufacturer(), + output->monitor->get_model()); auto configs = client->getOutputConfigs(output); if (configs.empty()) { - if (output->output != nullptr) { - wl_output_destroy(output->output); - output->output = nullptr; - } - if (output->xdg_output != nullptr) { - zxdg_output_v1_destroy(output->xdg_output); - output->xdg_output = nullptr; - } + output->xdg_output.reset(); } else { wl_display_roundtrip(client->wl_display); for (const auto &config : configs) { @@ -179,9 +134,26 @@ void waybar::Client::handleName(void * data, struct zxdg_output_v1 * /*xdg_ } } -void waybar::Client::handleDescription(void * /*data*/, struct zxdg_output_v1 * /*zxdg_output_v1*/, - const char * /*description*/) { - // Nothing here +void waybar::Client::handleMonitorAdded(Glib::RefPtr monitor) { + auto &output = outputs_.emplace_back(new struct waybar_output({monitor})); + handleOutput(output); +} + +void waybar::Client::handleMonitorRemoved(Glib::RefPtr monitor) { + spdlog::debug("Output removed: {} {}", monitor->get_manufacturer(), monitor->get_model()); + for (auto it = bars.begin(); it != bars.end();) { + if ((*it)->output->monitor == monitor) { + auto output_name = (*it)->output->name; + (*it)->window.close(); + it = bars.erase(it); + spdlog::info("Bar removed from output: {}", output_name); + } else { + ++it; + } + } + std::remove_if(outputs_.begin(), outputs_.end(), [&monitor](const auto &output) { + return output->monitor == monitor; + }); } std::tuple waybar::Client::getConfigs( @@ -240,6 +212,14 @@ void waybar::Client::bindInterfaces() { if (layer_shell == nullptr || xdg_output_manager == nullptr) { throw std::runtime_error("Failed to acquire required resources."); } + // add existing outputs and subscribe to updates + for (auto i = 0; i < gdk_display->get_n_monitors(); ++i) { + auto monitor = gdk_display->get_monitor(i); + handleMonitorAdded(monitor); + } + gdk_display->signal_monitor_added().connect(sigc::mem_fun(*this, &Client::handleMonitorAdded)); + gdk_display->signal_monitor_removed().connect( + sigc::mem_fun(*this, &Client::handleMonitorRemoved)); } int waybar::Client::main(int argc, char *argv[]) {