diff options
Diffstat (limited to 'plugins')
| -rw-r--r-- | plugins/wayfire/.gitignore | 1 | ||||
| -rw-r--r-- | plugins/wayfire/CMakeLists.txt | 66 | ||||
| -rw-r--r-- | plugins/wayfire/ueberzugpp.cpp | 150 |
3 files changed, 217 insertions, 0 deletions
diff --git a/plugins/wayfire/.gitignore b/plugins/wayfire/.gitignore new file mode 100644 index 0000000..ceddaa3 --- /dev/null +++ b/plugins/wayfire/.gitignore @@ -0,0 +1 @@ +.cache/ diff --git a/plugins/wayfire/CMakeLists.txt b/plugins/wayfire/CMakeLists.txt new file mode 100644 index 0000000..8044b8f --- /dev/null +++ b/plugins/wayfire/CMakeLists.txt @@ -0,0 +1,66 @@ +# Display images inside a terminal +# Copyright (C) 2023 JustKidding +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +cmake_minimum_required(VERSION 3.18) + +set(CMAKE_C_STANDARD 17) +set(CMAKE_C_STANDARD_REQUIRED ON) +set(CMAKE_C_EXTENSIONS OFF) +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +set(CMAKE_BUILD_TYPE Debug CACHE STRING "Build type.") + +cmake_policy(SET CMP0065 NEW) +set(CMAKE_ENABLE_EXPORTS ON) + +project(wayfire-ueberzugpp LANGUAGES CXX C VERSION 0.0.1) +add_library(ueberzugpp SHARED) + +list(APPEND PLUGIN_SOURCES "ueberzugpp.cpp") + +find_package(PkgConfig REQUIRED) + +find_package(ECM REQUIRED NO_MODULE) +list(APPEND CMAKE_MODULE_PATH ${ECM_MODULE_PATH}) +find_package(WaylandProtocols REQUIRED) +find_package(WaylandScanner REQUIRED) + +ecm_add_wayland_server_protocol(PLUGIN_SOURCES + PROTOCOL "${WaylandProtocols_DATADIR}/stable/xdg-shell/xdg-shell.xml" + BASENAME "xdg-shell") +file(CREATE_LINK + "wayland-xdg-shell-server-protocol.h" + "${PROJECT_BINARY_DIR}/xdg-shell-protocol.h" SYMBOLIC) + +pkg_check_modules(WAYFIRE REQUIRED IMPORTED_TARGET wayfire) +pkg_check_modules(WLROOTS REQUIRED IMPORTED_TARGET wlroots) +pkg_check_modules(WFCONFIG REQUIRED IMPORTED_TARGET wf-config) + +add_compile_definitions(WLR_USE_UNSTABLE WAYFIRE_PLUGIN) +target_compile_options(ueberzugpp PRIVATE + $<$<CONFIG:Debug>: + -Wall -Wextra -Wpedantic -Werror + > +) + +target_include_directories(ueberzugpp PRIVATE "${PROJECT_BINARY_DIR}") +target_sources(ueberzugpp PRIVATE ${PLUGIN_SOURCES}) +target_link_libraries(ueberzugpp + PkgConfig::WAYFIRE + PkgConfig::WLROOTS + PkgConfig::WFCONFIG) diff --git a/plugins/wayfire/ueberzugpp.cpp b/plugins/wayfire/ueberzugpp.cpp new file mode 100644 index 0000000..cc70a16 --- /dev/null +++ b/plugins/wayfire/ueberzugpp.cpp @@ -0,0 +1,150 @@ +#include <memory> +#include <unordered_map> +#include <wayfire/core.hpp> +#include <wayfire/scene-render.hpp> +#include <wayfire/util.hpp> +#include <wayfire/view.hpp> +#include <wayfire/output.hpp> +#include <wayfire/plugin.hpp> +#include <wayfire/scene-operations.hpp> +#include <wayfire/plugins/ipc/ipc-method-repository.hpp> +#include <wayfire/plugins/common/shared-core-data.hpp> +#include <wayfire/unstable/wlr-view-events.hpp> +#include <wayfire/unstable/wlr-surface-node.hpp> +#include <wayfire/unstable/translation-node.hpp> + +namespace wf +{ +class ueberzugpp_surface_t +{ + std::weak_ptr<wf::view_interface_t> terminal; + std::shared_ptr<scene::wlr_surface_node_t> surface; + std::shared_ptr<scene::translation_node_t> translation; + wlr_xdg_toplevel *toplevel; + + wf::wl_listener_wrapper on_toplevel_destroy; + + public: + + ueberzugpp_surface_t(const std::weak_ptr<wf::view_interface_t>& _terminal, + wlr_xdg_toplevel *_toplevel): + terminal(_terminal), + toplevel(_toplevel) + { + // We create a custom surface node for the ueberzugpp window and wrap it in a translation node, so that + // we can control its position. + // We add the translation node as a subsurface of the terminal. + surface = std::make_shared<scene::wlr_surface_node_t>(_toplevel->base->surface, true); + translation = std::make_shared<scene::translation_node_t>(); + + translation->set_children_list({surface}); + + auto term = _terminal.lock(); + wf::scene::add_front(term->get_surface_root_node(), translation); + + // Finally, wait for the toplevel to be destroyed + on_toplevel_destroy.set_callback([this] (auto) + { + destroy_callback(); + }); + on_toplevel_destroy.connect(&toplevel->base->events.destroy); + } + + std::function<void()> destroy_callback; + + ~ueberzugpp_surface_t() + { + wf::scene::remove_child(translation); + } + + void set_offset(int xcoord, int ycoord) + { + translation->set_offset({xcoord, ycoord}); + const auto term = terminal.lock(); + if (!term) { + return; + } + term->damage(); + } +}; + + +class ueberzugpp_mapper : public wf::plugin_interface_t +{ + public: + void init() override + { + ipc_repo->register_method("ueberzugpp/set_offset", set_offset); + wf::get_core().connect(&on_new_xdg_surface); + } + + void fini() override + { + ipc_repo->unregister_method("ueberzugpp/set_offset"); + } + + ipc::method_callback set_offset = [this] (nlohmann::json json) + { + // NOLINTBEGIN + WFJSON_EXPECT_FIELD(json, "app-id", string); + WFJSON_EXPECT_FIELD(json, "x", number_integer); + WFJSON_EXPECT_FIELD(json, "y", number_integer); + // NOLINTEND + + const std::string app_id = json["app-id"]; + const int xcoord = json["x"]; + const int ycoord = json["y"]; + + const auto search = surfaces.find(app_id); + if (search == surfaces.end()) + { + return ipc::json_error("Unknown ueberzugpp window with appid " + app_id); + } + + search->second->set_offset(xcoord, ycoord); + return ipc::json_ok(); + }; + + shared_data::ref_ptr_t<ipc::method_repository_t> ipc_repo; + + // When a new xdg_toplevel is created, we need to check whether it is an ueberzugpp window by looking at + // its app-id. If it is indeed an ueberzugpp window, we take over the toplevel (by setting + // use_default_implementation=false) and create our own ueberzugpp_surface. In addition, we directly map + // the surface to the currently focused view, if it exists. + wf::signal::connection_t<new_xdg_surface_signal> on_new_xdg_surface = [this] (new_xdg_surface_signal *event) + { + if (!event->use_default_implementation) { + return; + } + if (event->surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) { + return; + } + + const auto* toplevel_title = event->surface->toplevel->title; + const std::string appid = toplevel_title != nullptr ? toplevel_title : "null"; + const std::string search_for = "ueberzugpp_"; + if (appid.find(search_for) != std::string::npos) + { + auto terminal = wf::get_core().get_active_output()->get_active_view(); + if (!terminal) + { + LOGE("Which window to map ueberzugpp to????"); + return; + } + + event->use_default_implementation = false; + const auto [iter, was_inserted] = surfaces.insert_or_assign(appid, + std::make_unique<ueberzugpp_surface_t>(terminal->weak_from_this(), event->surface->toplevel)); + iter->second->destroy_callback = [this, appid] + { + surfaces.erase(appid); + }; + } + }; + + std::unordered_map<std::string, std::unique_ptr<ueberzugpp_surface_t>> surfaces; +}; +} // end namespace wf + +// NOLINTNEXTLINE +DECLARE_WAYFIRE_PLUGIN(wf::ueberzugpp_mapper); |
