// 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 "x11.hpp"
#include "dimensions.hpp"
#include
#include
constexpr std::string_view win_name = "ueberzugpp";
X11Window::X11Window(xcb_connection_t *connection, xcb_screen_t *screen, xcb_window_t window, xcb_window_t parent,
std::shared_ptr image)
: connection(connection),
screen(screen),
window(window),
parent(parent),
gc(xcb_generate_id(connection)),
image(std::move(image))
{
logger = spdlog::get("X11");
create();
change_title();
}
void X11Window::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, window, this->parent, xcoord, ycoord, image->width(),
image->height(), 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, screen->root_visual, value_mask,
&value_list);
xcb_create_gc(connection, gc, window, 0, nullptr);
logger->debug("Created child window {} at ({},{}) with parent {}", window, xcoord, ycoord, parent);
}
void X11Window::change_title()
{
const int bits_in_char = 8;
xcb_change_property(connection, XCB_PROP_MODE_REPLACE, window, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, bits_in_char,
win_name.size(), win_name.data());
}
void X11Window::show()
{
if (visible) {
return;
}
visible = true;
xcb_map_window(connection, window);
xcb_flush(connection);
}
void X11Window::hide()
{
if (!visible) {
return;
}
visible = false;
xcb_unmap_window(connection, window);
xcb_flush(connection);
}
void X11Window::draw()
{
if (!xcb_image) {
return;
}
xcb_image_put(connection, window, gc, xcb_image.get(), 0, 0, 0);
}
void X11Window::generate_frame()
{
xcb_image.reset(xcb_image_create_native(connection, image->width(), image->height(), XCB_IMAGE_FORMAT_Z_PIXMAP,
screen->root_depth, nullptr, 0, nullptr));
xcb_image->data = const_cast(image->data());
send_expose_event();
}
X11Window::~X11Window()
{
xcb_destroy_window(connection, window);
xcb_free_gc(connection, gc);
xcb_flush(connection);
}
void X11Window::send_expose_event()
{
const int event_size = 32;
std::array buffer;
auto *event = reinterpret_cast(buffer.data());
event->response_type = XCB_EXPOSE;
event->window = window;
xcb_send_event(connection, 0, window, XCB_EVENT_MASK_EXPOSURE, reinterpret_cast(event));
xcb_flush(connection);
}