diff --git a/src/Compositor.cpp b/src/Compositor.cpp index 601cf114..bd843ef5 100644 --- a/src/Compositor.cpp +++ b/src/Compositor.cpp @@ -121,6 +121,9 @@ void CCompositor::startCompositor() { // Init all the managers BEFORE we start with the wayland server so that ALL of the stuff is initialized // properly and we dont get any bad mem reads. // + Debug::log(LOG, "Creating the CHyprError!"); + g_pHyprError = std::make_unique(); + Debug::log(LOG, "Creating the KeybindManager!"); g_pKeybindManager = std::make_unique(); diff --git a/src/Compositor.hpp b/src/Compositor.hpp index f56fcec5..1133d38c 100644 --- a/src/Compositor.hpp +++ b/src/Compositor.hpp @@ -19,6 +19,7 @@ #include "Window.hpp" #include "render/Renderer.hpp" #include "render/OpenGL.hpp" +#include "hyprerror/HyprError.hpp" class CCompositor { public: diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index e32709ba..4eb617b9 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -310,7 +310,7 @@ void CConfigManager::loadConfigLoadVars() { Debug::log(ERR, "Error reading line from config. Line:"); Debug::log(NONE, "%s", line.c_str()); - parseError = "Config error at line " + std::to_string(linenum) + ": Line parsing error."; + parseError += "Config error at line " + std::to_string(linenum) + ": Line parsing error."; } if (parseError != "" && parseError.find("Config error at line") != 0) { @@ -331,6 +331,12 @@ void CConfigManager::loadConfigLoadVars() { // Calculate the mod mask for main_mod configValues["general:main_mod_internal"].intValue = g_pKeybindManager->stringToModMask(configValues["general:main_mod"].strValue); + + // parseError will be displayed next frame + if (parseError != "") + g_pHyprError->queueCreate(parseError + "\nHyprland may not work correctly.", CColor(255, 50, 50, 255)); + else + g_pHyprError->destroy(); } void CConfigManager::tick() { diff --git a/src/hyprerror/HyprError.cpp b/src/hyprerror/HyprError.cpp new file mode 100644 index 00000000..524fce2d --- /dev/null +++ b/src/hyprerror/HyprError.cpp @@ -0,0 +1,109 @@ +#include "HyprError.hpp" +#include "../Compositor.hpp" + +void CHyprError::queueCreate(std::string message, const CColor& color) { + m_szQueued = message; + m_cQueued = color; +} + +void CHyprError::createQueued() { + if (m_bIsCreated) { + m_bQueuedDestroy = false; + m_tTexture.destroyTexture(); + } + + const auto PMONITOR = &g_pCompositor->m_lMonitors.front(); + + const auto CAIROSURFACE = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, PMONITOR->vecSize.x, PMONITOR->vecSize.y); + + const auto CAIRO = cairo_create(CAIROSURFACE); + + // clear the pixmap + cairo_save(CAIRO); + cairo_set_operator(CAIRO, CAIRO_OPERATOR_CLEAR); + cairo_paint(CAIRO); + cairo_restore(CAIRO); + + const auto LINECOUNT = 1 + std::count(m_szQueued.begin(), m_szQueued.end(), '\n'); + + cairo_set_source_rgba(CAIRO, m_cQueued.r / 255.f, m_cQueued.g / 255.f, m_cQueued.b / 255.f, m_cQueued.a / 255.f); + cairo_rectangle(CAIRO, 0, 0, PMONITOR->vecSize.x, 10 * LINECOUNT); + + // outline + cairo_rectangle(CAIRO, 0, 0, 1, PMONITOR->vecSize.y); // left + cairo_rectangle(CAIRO, PMONITOR->vecSize.x - 1, 0, PMONITOR->vecSize.x, PMONITOR->vecSize.y); // right + cairo_rectangle(CAIRO, 0, PMONITOR->vecSize.y - 1, PMONITOR->vecSize.x, PMONITOR->vecSize.y); // bottom + + cairo_fill(CAIRO); + + // draw the text with a common font + const CColor textColor = m_cQueued.r * m_cQueued.g * m_cQueued.b < 0.5f ? CColor(255, 255, 255, 255) : CColor(0, 0, 0, 255); + + cairo_select_font_face(CAIRO, "Noto Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); + cairo_set_font_size(CAIRO, 8); + cairo_set_source_rgba(CAIRO, textColor.r / 255.f, textColor.g / 255.f, textColor.b / 255.f, textColor.a / 255.f); + + float yoffset = 8; + while(m_szQueued != "") { + std::string current = m_szQueued.substr(0, m_szQueued.find('\n')); + if (const auto NEWLPOS = m_szQueued.find('\n'); NEWLPOS != std::string::npos) + m_szQueued = m_szQueued.substr(NEWLPOS + 1); + else + m_szQueued = ""; + cairo_move_to(CAIRO, 0, yoffset); + cairo_show_text(CAIRO, current.c_str()); + yoffset += 9; + } + + + cairo_surface_flush(CAIROSURFACE); + + // copy the data to an OpenGL texture we have + const auto DATA = cairo_image_surface_get_data(CAIROSURFACE); + m_tTexture.allocate(); + glBindTexture(GL_TEXTURE_2D, m_tTexture.m_iTexID); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_BLUE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, PMONITOR->vecSize.x, PMONITOR->vecSize.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, DATA); + + // delete cairo + cairo_destroy(CAIRO); + cairo_surface_destroy(CAIROSURFACE); + + m_bIsCreated = true; + m_szQueued = ""; + m_cQueued = CColor(); +} + +void CHyprError::draw() { + if (!m_bIsCreated) { + if (m_szQueued != "") + createQueued(); + return; + } + + if (m_bQueuedDestroy) { + m_bQueuedDestroy = false; + m_tTexture.destroyTexture(); + m_bIsCreated = false; + return; + } + + const auto PMONITOR = &g_pCompositor->m_lMonitors.front(); + + if (g_pHyprOpenGL->m_RenderData.pMonitor != PMONITOR) + return; // wrong mon + + const auto TRANSFORM = wlr_output_transform_invert(WL_OUTPUT_TRANSFORM_NORMAL); + float matrix[9]; + wlr_box windowBox = {0, 0, PMONITOR->vecSize.x, PMONITOR->vecSize.y}; + wlr_matrix_project_box(matrix, &windowBox, TRANSFORM, 0, PMONITOR->output->transform_matrix); + + g_pHyprOpenGL->renderTexture(m_tTexture, matrix, 255.f, 0); +} + +void CHyprError::destroy() { + m_bQueuedDestroy = true; +} \ No newline at end of file diff --git a/src/hyprerror/HyprError.hpp b/src/hyprerror/HyprError.hpp new file mode 100644 index 00000000..f09183de --- /dev/null +++ b/src/hyprerror/HyprError.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include "../defines.hpp" +#include "../render/Texture.hpp" + +#include + +class CHyprError { +public: + void queueCreate(std::string message, const CColor& color); + void draw(); + void destroy(); + +private: + void createQueued(); + std::string m_szQueued = ""; + CColor m_cQueued; + bool m_bQueuedDestroy = false; + bool m_bIsCreated = false; + CTexture m_tTexture; +}; + +inline std::unique_ptr g_pHyprError; // This is a full-screen error. Treat it with respect, and there can only be one at a time. \ No newline at end of file diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index fa62b5fd..197b29b1 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -79,6 +79,10 @@ void CHyprRenderer::renderWorkspaceWithFullscreenWindow(SMonitor* pMonitor, SWor } renderDragIcon(pMonitor, time); + + // if correct monitor draw hyprerror + if (pMonitor == &g_pCompositor->m_lMonitors.front()) + g_pHyprError->draw(); } void CHyprRenderer::renderWindow(CWindow* pWindow, SMonitor* pMonitor, timespec* time, bool decorate) { @@ -178,6 +182,10 @@ void CHyprRenderer::renderAllClientsForMonitor(const int& ID, timespec* time) { } renderDragIcon(PMONITOR, time); + + // if correct monitor draw hyprerror + if (PMONITOR == &g_pCompositor->m_lMonitors.front()) + g_pHyprError->draw(); } void CHyprRenderer::outputMgrApplyTest(wlr_output_configuration_v1* config, bool test) { diff --git a/src/render/Texture.cpp b/src/render/Texture.cpp index 6d47c4a8..01b80145 100644 --- a/src/render/Texture.cpp +++ b/src/render/Texture.cpp @@ -19,4 +19,17 @@ CTexture::CTexture(wlr_texture* tex) { } m_vSize = Vector2D(tex->width, tex->height); +} + +void CTexture::destroyTexture() { + if (m_iTexID) { + glDeleteTextures(1, &m_iTexID); + m_iTexID = 0; + } +} + +void CTexture::allocate() { + if (!m_iTexID) { + glGenTextures(1, &m_iTexID); + } } \ No newline at end of file diff --git a/src/render/Texture.hpp b/src/render/Texture.hpp index dbdf715a..f1211aec 100644 --- a/src/render/Texture.hpp +++ b/src/render/Texture.hpp @@ -14,6 +14,9 @@ public: CTexture(); CTexture(wlr_texture*); + void destroyTexture(); + void allocate(); + TEXTURETYPE m_iType = TEXTURE_RGBA; GLenum m_iTarget = GL_TEXTURE_2D; GLuint m_iTexID = 0;