// 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 . #include "waylandshm.hpp" #include "dimensions.hpp" #include "shm.hpp" #include "util.hpp" #include constexpr int id_len = 10; constexpr struct xdg_surface_listener xdg_surface_listener = { .configure = WaylandShmWindow::xdg_surface_configure, }; constexpr struct wl_callback_listener frame_listener = {.done = WaylandShmWindow::wl_surface_frame_done}; void WaylandShmWindow::xdg_surface_configure(void *data, struct xdg_surface *xdg_surface, uint32_t serial) { xdg_surface_ack_configure(xdg_surface, serial); const auto *tmp = static_cast(data); const auto window = tmp->ptr.lock(); if (!window) { return; } auto *shm_window = dynamic_cast(window.get()); shm_window->wl_draw(shm_window->output_scale); } void WaylandShmWindow::wl_surface_frame_done(void *data, struct wl_callback *callback, [[maybe_unused]] uint32_t time) { wl_callback_destroy(callback); const auto *tmp = static_cast(data); const auto window = tmp->ptr.lock(); if (!window) { return; } auto *shm_window = dynamic_cast(window.get()); const std::scoped_lock lock{shm_window->draw_mutex}; if (!shm_window->visible) { return; } shm_window->generate_frame(); } WaylandShmWindow::WaylandShmWindow(WaylandCanvas *canvas, std::unique_ptr new_image, struct XdgStructAgg *xdg_agg, WaylandConfig *config) : config(config), xdg_base(canvas->xdg_base), surface(wl_compositor_create_surface(canvas->compositor)), xdg_surface(xdg_wm_base_get_xdg_surface(xdg_base, surface)), xdg_toplevel(xdg_surface_get_toplevel(xdg_surface)), image(std::move(new_image)), appid(fmt::format("ueberzugpp_{}", util::generate_random_string(id_len))), xdg_agg(xdg_agg) { config->initial_setup(appid); xdg_setup(); output_scale = canvas->output_info.at(config->get_focused_output_name()); shm = std::make_unique(image->width(), image->height(), output_scale, canvas->wl_shm); } void WaylandShmWindow::finish_init() { auto xdg = std::make_unique(); xdg->ptr = weak_from_this(); this_ptr = xdg.get(); xdg_agg->ptrs.push_back(std::move(xdg)); setup_listeners(); visible = true; } void WaylandShmWindow::setup_listeners() { xdg_surface_add_listener(xdg_surface, &xdg_surface_listener, this_ptr); wl_surface_commit(surface); if (image->is_animated()) { callback = wl_surface_frame(surface); wl_callback_add_listener(callback, &frame_listener, this_ptr); } } void WaylandShmWindow::xdg_setup() { xdg_toplevel_set_app_id(xdg_toplevel, appid.c_str()); xdg_toplevel_set_title(xdg_toplevel, appid.c_str()); } WaylandShmWindow::~WaylandShmWindow() { delete_xdg_structs(); delete_wayland_structs(); } void WaylandShmWindow::wl_draw(int32_t scale_factor) { std::memcpy(shm->pool_data, image->data(), image->size()); wl_surface_attach(surface, shm->buffer, 0, 0); wl_surface_set_buffer_scale(surface, scale_factor); wl_surface_commit(surface); move_window(); } void WaylandShmWindow::show() { if (visible) { return; } visible = true; xdg_surface = xdg_wm_base_get_xdg_surface(xdg_base, surface); xdg_toplevel = xdg_surface_get_toplevel(xdg_surface); xdg_setup(); setup_listeners(); } void WaylandShmWindow::hide() { if (!visible) { return; } visible = false; const std::scoped_lock lock{draw_mutex}; delete_xdg_structs(); wl_surface_attach(surface, nullptr, 0, 0); wl_surface_commit(surface); } void WaylandShmWindow::delete_xdg_structs() { if (xdg_toplevel != nullptr) { xdg_toplevel_destroy(xdg_toplevel); xdg_toplevel = nullptr; } if (xdg_surface != nullptr) { xdg_surface_destroy(xdg_surface); xdg_surface = nullptr; } } void WaylandShmWindow::delete_wayland_structs() { if (surface != nullptr) { wl_surface_destroy(surface); surface = nullptr; } } void WaylandShmWindow::move_window() { const auto dims = image->dimensions(); const auto cur_window = config->get_window_info(); const int wayland_x = dims.xpixels() + dims.padding_horizontal; const int wayland_y = dims.ypixels() + dims.padding_vertical; const int xcoord = cur_window.x + wayland_x; const int ycoord = cur_window.y + wayland_y; config->move_window(appid, xcoord, ycoord); } void WaylandShmWindow::generate_frame() { std::this_thread::sleep_for(std::chrono::milliseconds(image->frame_delay())); callback = wl_surface_frame(surface); wl_callback_add_listener(callback, &frame_listener, this_ptr); image->next_frame(); std::memcpy(shm->pool_data, image->data(), image->size()); wl_surface_attach(surface, shm->buffer, 0, 0); wl_surface_damage_buffer(surface, 0, 0, image->width(), image->height()); wl_surface_commit(surface); }