// 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 "util/egl.hpp" #include "image.hpp" #include #include #include #include constexpr EGLint egl_major_version = 1; constexpr EGLint egl_minor_version = 5; constexpr EGLint opengl_major_version = 4; constexpr EGLint opengl_minor_version = 6; constexpr auto config_attrs = std::to_array({EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_CONFORMANT, EGL_OPENGL_BIT, EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, EGL_COLOR_BUFFER_TYPE, EGL_RGB_BUFFER, EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, EGL_DEPTH_SIZE, 24, EGL_STENCIL_SIZE, 8, EGL_NONE}); const auto context_attrs = std::to_array( {EGL_CONTEXT_MAJOR_VERSION, opengl_major_version, EGL_CONTEXT_MINOR_VERSION, opengl_minor_version, #ifdef DEBUG EGL_CONTEXT_OPENGL_DEBUG, EGL_TRUE, #endif EGL_CONTEXT_OPENGL_ROBUST_ACCESS, EGL_TRUE, EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT, EGL_NONE}); void GLAPIENTRY debug_callback([[maybe_unused]] GLenum source, [[maybe_unused]] GLenum type, [[maybe_unused]] GLuint gl_id, GLenum severity, [[maybe_unused]] GLsizei length, const GLchar *message, [[maybe_unused]] const void *user) { if (type != GL_DEBUG_TYPE_ERROR) { return; } const auto logger = spdlog::get("opengl"); logger->error("Type: {:#X}, Severity: {:#X}, Message: {}", type, severity, message); } template EGLUtil::EGLUtil(EGLenum platform, T *native_display, const EGLAttrib *attrib) : display(eglGetPlatformDisplay(platform, native_display, attrib)) { logger = spdlog::get("opengl"); if (display == EGL_NO_DISPLAY) { const auto err = error_to_string(); logger->error("Could not obtain display, error {}", err); throw std::runtime_error(""); } EGLint egl_major = 0; EGLint egl_minor = 0; EGLBoolean eglres = eglInitialize(display, &egl_major, &egl_minor); if (eglres != EGL_TRUE) { const auto err = error_to_string(); logger->error("Could not initialize display, error {}", err); throw std::runtime_error(""); } if (egl_major != egl_major_version && egl_minor != egl_minor_version) { logger->error("EGL {}.{} is not available", egl_major_version, egl_minor_version); throw std::runtime_error(""); } eglres = eglBindAPI(EGL_OPENGL_API); if (eglres != EGL_TRUE) { const auto err = error_to_string(); logger->error("Could not bind to OpenGL API, error {}", err); throw std::runtime_error(""); } int num_config = 0; eglres = eglChooseConfig(display, config_attrs.data(), &config, 1, &num_config); if (eglres != EGL_TRUE || num_config != 1) { const auto err = error_to_string(); logger->error("Could not create config, error {}", err); throw std::runtime_error(""); } logger->info("Using EGL {}.{} and OpenGL {}.{}", egl_major_version, egl_minor_version, opengl_major_version, opengl_minor_version); } template EGLUtil::~EGLUtil() { eglTerminate(display); } template auto EGLUtil::error_to_string() const -> std::string { using pair_type = std::pair; constexpr auto error_codes = std::to_array({ {EGL_SUCCESS, "EGL_SUCCESS"}, {EGL_BAD_ACCESS, "EGL_BAD_ACCESS"}, {EGL_BAD_ALLOC, "EGL_BAD_ALLOC"}, {EGL_BAD_ATTRIBUTE, "EGL_BAD_ATTRIBUTE"}, {EGL_BAD_CONFIG, "EGL_BAD_CONFIG"}, {EGL_BAD_CONTEXT, "EGL_BAD_CONTEXT"}, {EGL_BAD_CURRENT_SURFACE, "EGL_BAD_CURRENT_SURFACE"}, {EGL_BAD_DISPLAY, "EGL_BAD_DISPLAY"}, {EGL_BAD_MATCH, "EGL_BAD_MATCH"}, {EGL_BAD_NATIVE_PIXMAP, "EGL_BAD_NATIVE_PIXMAP"}, {EGL_BAD_NATIVE_WINDOW, "EGL_BAD_NATIVE_WINDOW"}, {EGL_BAD_PARAMETER, "EGL_BAD_PARAMETER"}, {EGL_BAD_SURFACE, "EGL_BAD_SURFACE"}, }); const auto current_error = eglGetError(); const auto found = ranges::find_if(error_codes, [current_error](const pair_type &pair) { return pair.first == current_error; }); if (found == error_codes.end()) { return "EGL_UNKNOWN_ERROR"; } return std::string(found->second); } template auto EGLUtil::create_context(EGLSurface surface) const -> EGLContext { EGLContext context = eglCreateContext(display, config, EGL_NO_CONTEXT, context_attrs.data()); if (context == EGL_NO_CONTEXT) { const auto err = error_to_string(); logger->error("Could not create context, error {}", err); return context; } #ifdef DEBUG run_contained(surface, context, [] { glDebugMessageCallback(debug_callback, nullptr); glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); }); #endif return context; } template auto EGLUtil::create_surface(V *native_window) const -> EGLSurface { EGLSurface surface = eglCreatePlatformWindowSurface(display, config, native_window, nullptr); if (surface == EGL_NO_SURFACE) { const auto err = error_to_string(); logger->error("Could not create surface, error {}", err); } return surface; } template void EGLUtil::make_current(EGLSurface surface, EGLContext context) const { eglMakeCurrent(display, surface, surface, context); } template void EGLUtil::restore() const { eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); } template void EGLUtil::run_contained(EGLSurface surface, EGLContext context, const std::function &func) const { make_current(surface, context); func(); restore(); } template void EGLUtil::get_texture_from_image(const Image &image, GLuint texture) const { glBindTexture(GL_TEXTURE_2D, texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, image.width(), image.height(), 0, GL_BGRA, GL_UNSIGNED_BYTE, image.data()); } #ifdef ENABLE_X11 # include template class EGLUtil; #endif #ifdef ENABLE_WAYLAND # include # include template class EGLUtil; #endif