summaryrefslogtreecommitdiff
path: root/src/canvas/wayland/window/waylandshm.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/canvas/wayland/window/waylandshm.cpp')
-rw-r--r--src/canvas/wayland/window/waylandshm.cpp186
1 files changed, 186 insertions, 0 deletions
diff --git a/src/canvas/wayland/window/waylandshm.cpp b/src/canvas/wayland/window/waylandshm.cpp
new file mode 100644
index 0000000..1ee5994
--- /dev/null
+++ b/src/canvas/wayland/window/waylandshm.cpp
@@ -0,0 +1,186 @@
+// 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/>.
+
+#include "waylandshm.hpp"
+#include "dimensions.hpp"
+#include "shm.hpp"
+#include "util.hpp"
+
+#include <fmt/format.h>
+
+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<struct XdgStruct *>(data);
+ const auto window = tmp->ptr.lock();
+ if (!window) {
+ return;
+ }
+ auto *shm_window = dynamic_cast<WaylandShmWindow *>(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<struct XdgStruct *>(data);
+ const auto window = tmp->ptr.lock();
+ if (!window) {
+ return;
+ }
+ auto *shm_window = dynamic_cast<WaylandShmWindow *>(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<Image> 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<WaylandShm>(image->width(), image->height(), output_scale, canvas->wl_shm);
+}
+
+void WaylandShmWindow::finish_init()
+{
+ auto xdg = std::make_unique<XdgStruct>();
+ 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);
+}