// 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 "x11egl.hpp" #include "dimensions.hpp" #include "image.hpp" #include "util.hpp" #include X11EGLWindow::X11EGLWindow(xcb_connection_t *connection, xcb_screen_t *screen, xcb_window_t windowid, xcb_window_t parentid, const EGLUtil *egl, std::shared_ptr new_image) : connection(connection), screen(screen), windowid(windowid), parentid(parentid), image(std::move(new_image)), egl(egl) { logger = spdlog::get("x11"); create(); opengl_setup(); } X11EGLWindow::~X11EGLWindow() { egl->run_contained(egl_surface, egl_context, [this] { glDeleteTextures(1, &texture); glDeleteFramebuffers(1, &fbo); }); eglDestroySurface(egl->display, egl_surface); eglDestroyContext(egl->display, egl_context); xcb_destroy_window(connection, windowid); xcb_flush(connection); } void X11EGLWindow::create() { const uint32_t value_mask = XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL | XCB_CW_EVENT_MASK | XCB_CW_COLORMAP; struct xcb_create_window_value_list_t value_list; value_list.background_pixel = screen->black_pixel; value_list.border_pixel = screen->black_pixel; value_list.event_mask = XCB_EVENT_MASK_EXPOSURE; value_list.colormap = screen->default_colormap; const auto dimensions = image->dimensions(); const auto xcoord = static_cast(dimensions.xpixels() + dimensions.padding_horizontal); const auto ycoord = static_cast(dimensions.ypixels() + dimensions.padding_vertical); xcb_create_window_aux(connection, screen->root_depth, windowid, parentid, xcoord, ycoord, image->width(), image->height(), 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, screen->root_visual, value_mask, &value_list); } void X11EGLWindow::opengl_setup() { egl_surface = egl->create_surface(&windowid); if (egl_surface == EGL_NO_SURFACE) { throw std::runtime_error(""); } egl_context = egl->create_context(egl_surface); if (egl_context == EGL_NO_CONTEXT) { throw std::runtime_error(""); } egl->run_contained(egl_surface, egl_context, [this] { glGenFramebuffers(1, &fbo); glGenTextures(1, &texture); }); } void X11EGLWindow::draw() { const std::scoped_lock lock{egl_mutex}; egl->run_contained(egl_surface, egl_context, [this] { glBlitFramebuffer(0, 0, image->width(), image->height(), 0, 0, image->width(), image->height(), GL_COLOR_BUFFER_BIT, GL_NEAREST); eglSwapBuffers(egl->display, egl_surface); }); } void X11EGLWindow::generate_frame() { const std::scoped_lock lock{egl_mutex}; egl->run_contained(egl_surface, egl_context, [this] { egl->get_texture_from_image(*image, texture); glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); }); send_expose_event(); } void X11EGLWindow::show() { if (visible) { return; } visible = true; xcb_map_window(connection, windowid); xcb_flush(connection); } void X11EGLWindow::hide() { if (!visible) { return; } visible = false; xcb_unmap_window(connection, windowid); xcb_flush(connection); } void X11EGLWindow::send_expose_event() { const int event_size = 32; std::array buffer; auto *event = reinterpret_cast(buffer.data()); event->response_type = XCB_EXPOSE; event->window = windowid; xcb_send_event(connection, 0, windowid, XCB_EVENT_MASK_EXPOSURE, reinterpret_cast(event)); xcb_flush(connection); }