mirror of
https://github.com/hyprwm/Hyprland
synced 2024-11-22 20:05:58 +01:00
renderer: Add a missing texture asset and a user check
When an asset is missing, instead of a black screen, render an obnoxious, yet standard, missing texture. Additionally, warn the user assets failed to load. Shoutout to Arch for having their assets broken for months. Fix your shit. I am tired of it, and it's negatively impacting users.
This commit is contained in:
parent
3852418d24
commit
d8b865366a
5 changed files with 139 additions and 71 deletions
|
@ -2738,6 +2738,12 @@ void CCompositor::performUserChecks() {
|
||||||
CColor{}, 15000, ICON_WARNING);
|
CColor{}, 15000, ICON_WARNING);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (g_pHyprOpenGL->failedAssetsNo > 0) {
|
||||||
|
g_pHyprNotificationOverlay->addNotification(std::format("Hyprland failed to load {} essential asset{}, blame your distro's packager for doing a bad job at packaging!",
|
||||||
|
g_pHyprOpenGL->failedAssetsNo, g_pHyprOpenGL->failedAssetsNo > 1 ? "s" : ""),
|
||||||
|
CColor{1.0, 0.1, 0.1, 1.0}, 15000, ICON_ERROR);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CCompositor::moveWindowToWorkspaceSafe(PHLWINDOW pWindow, PHLWORKSPACE pWorkspace) {
|
void CCompositor::moveWindowToWorkspaceSafe(PHLWINDOW pWindow, PHLWORKSPACE pWorkspace) {
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
#include "OpenGL.hpp"
|
#include "OpenGL.hpp"
|
||||||
|
|
||||||
CFramebuffer::CFramebuffer() {
|
CFramebuffer::CFramebuffer() {
|
||||||
m_cTex = makeShared<CTexture>();
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CFramebuffer::alloc(int w, int h, uint32_t drmFormat) {
|
bool CFramebuffer::alloc(int w, int h, uint32_t drmFormat) {
|
||||||
|
@ -12,6 +12,9 @@ bool CFramebuffer::alloc(int w, int h, uint32_t drmFormat) {
|
||||||
uint32_t glFormat = FormatUtils::drmFormatToGL(drmFormat);
|
uint32_t glFormat = FormatUtils::drmFormatToGL(drmFormat);
|
||||||
uint32_t glType = FormatUtils::glFormatToType(glFormat);
|
uint32_t glType = FormatUtils::glFormatToType(glFormat);
|
||||||
|
|
||||||
|
if (!m_cTex)
|
||||||
|
m_cTex = makeShared<CTexture>();
|
||||||
|
|
||||||
if (!m_iFbAllocated) {
|
if (!m_iFbAllocated) {
|
||||||
firstAlloc = true;
|
firstAlloc = true;
|
||||||
glGenFramebuffers(1, &m_iFb);
|
glGenFramebuffers(1, &m_iFb);
|
||||||
|
@ -54,7 +57,8 @@ bool CFramebuffer::alloc(int w, int h, uint32_t drmFormat) {
|
||||||
}
|
}
|
||||||
|
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, g_pHyprOpenGL->m_iCurrentOutputFb);
|
if (g_pHyprOpenGL)
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, g_pHyprOpenGL->m_iCurrentOutputFb);
|
||||||
|
|
||||||
m_vSize = Vector2D(w, h);
|
m_vSize = Vector2D(w, h);
|
||||||
|
|
||||||
|
@ -85,14 +89,17 @@ void CFramebuffer::bind() {
|
||||||
#else
|
#else
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, m_iFb);
|
glBindFramebuffer(GL_FRAMEBUFFER, m_iFb);
|
||||||
#endif
|
#endif
|
||||||
glViewport(0, 0, g_pHyprOpenGL->m_RenderData.pMonitor->vecPixelSize.x, g_pHyprOpenGL->m_RenderData.pMonitor->vecPixelSize.y);
|
if (g_pHyprOpenGL)
|
||||||
|
glViewport(0, 0, g_pHyprOpenGL->m_RenderData.pMonitor->vecPixelSize.x, g_pHyprOpenGL->m_RenderData.pMonitor->vecPixelSize.y);
|
||||||
|
else
|
||||||
|
glViewport(0, 0, m_vSize.x, m_vSize.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CFramebuffer::release() {
|
void CFramebuffer::release() {
|
||||||
if (m_iFbAllocated)
|
if (m_iFbAllocated)
|
||||||
glDeleteFramebuffers(1, &m_iFb);
|
glDeleteFramebuffers(1, &m_iFb);
|
||||||
|
|
||||||
m_cTex->destroyTexture();
|
m_cTex.reset();
|
||||||
m_iFbAllocated = false;
|
m_iFbAllocated = false;
|
||||||
m_vSize = Vector2D();
|
m_vSize = Vector2D();
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,13 @@
|
||||||
#include <gbm.h>
|
#include <gbm.h>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
|
||||||
|
const std::vector<const char*> ASSET_PATHS = {
|
||||||
|
#ifdef DATAROOTDIR
|
||||||
|
DATAROOTDIR,
|
||||||
|
#endif
|
||||||
|
"/usr/share",
|
||||||
|
};
|
||||||
|
|
||||||
inline void loadGLProc(void* pProc, const char* name) {
|
inline void loadGLProc(void* pProc, const char* name) {
|
||||||
void* proc = (void*)eglGetProcAddress(name);
|
void* proc = (void*)eglGetProcAddress(name);
|
||||||
if (proc == NULL) {
|
if (proc == NULL) {
|
||||||
|
@ -2595,11 +2602,32 @@ void CHyprOpenGLImpl::renderSplash(cairo_t* const CAIRO, cairo_surface_t* const
|
||||||
cairo_surface_flush(CAIROSURFACE);
|
cairo_surface_flush(CAIROSURFACE);
|
||||||
}
|
}
|
||||||
|
|
||||||
SP<CTexture> CHyprOpenGLImpl::loadAsset(const std::string& file) {
|
SP<CTexture> CHyprOpenGLImpl::loadAsset(const std::string& filename) {
|
||||||
const auto CAIROSURFACE = cairo_image_surface_create_from_png(file.c_str());
|
|
||||||
|
|
||||||
if (!CAIROSURFACE)
|
std::string fullPath;
|
||||||
return nullptr;
|
for (auto& e : ASSET_PATHS) {
|
||||||
|
std::string p = std::string{e} + "/hypr/" + filename;
|
||||||
|
std::error_code ec;
|
||||||
|
if (std::filesystem::exists(p, ec)) {
|
||||||
|
fullPath = p;
|
||||||
|
break;
|
||||||
|
} else
|
||||||
|
Debug::log(LOG, "loadAsset: looking at {} unsuccessful: ec {}", filename, ec.message());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fullPath.empty()) {
|
||||||
|
failedAssetsNo++;
|
||||||
|
Debug::log(ERR, "loadAsset: looking for {} failed (no provider found)", filename);
|
||||||
|
return m_pMissingAssetTexture;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto CAIROSURFACE = cairo_image_surface_create_from_png(fullPath.c_str());
|
||||||
|
|
||||||
|
if (!CAIROSURFACE) {
|
||||||
|
failedAssetsNo++;
|
||||||
|
Debug::log(ERR, "loadAsset: failed to load {} (corrupt / inaccessible / not png)", fullPath);
|
||||||
|
return m_pMissingAssetTexture;
|
||||||
|
}
|
||||||
|
|
||||||
const auto CAIROFORMAT = cairo_image_surface_get_format(CAIROSURFACE);
|
const auto CAIROFORMAT = cairo_image_surface_get_format(CAIROSURFACE);
|
||||||
auto tex = makeShared<CTexture>();
|
auto tex = makeShared<CTexture>();
|
||||||
|
@ -2710,51 +2738,68 @@ SP<CTexture> CHyprOpenGLImpl::renderText(const std::string& text, CColor col, in
|
||||||
return tex;
|
return tex;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CHyprOpenGLImpl::initAssets() {
|
void CHyprOpenGLImpl::initMissingAssetTexture() {
|
||||||
std::string assetsPath = "";
|
SP<CTexture> tex = makeShared<CTexture>();
|
||||||
#ifndef DATAROOTDIR
|
tex->allocate();
|
||||||
assetsPath = "/usr/share/hypr/";
|
|
||||||
#else
|
|
||||||
assetsPath = std::format("{}{}", DATAROOTDIR, "/hypr/");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
m_pLockDeadTexture = loadAsset(assetsPath + "lockdead.png");
|
const auto CAIROSURFACE = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 512, 512);
|
||||||
m_pLockDead2Texture = loadAsset(assetsPath + "lockdead2.png");
|
const auto CAIRO = cairo_create(CAIROSURFACE);
|
||||||
|
|
||||||
|
cairo_set_antialias(CAIRO, CAIRO_ANTIALIAS_NONE);
|
||||||
|
cairo_save(CAIRO);
|
||||||
|
cairo_set_source_rgba(CAIRO, 0, 0, 0, 1);
|
||||||
|
cairo_set_operator(CAIRO, CAIRO_OPERATOR_SOURCE);
|
||||||
|
cairo_paint(CAIRO);
|
||||||
|
cairo_set_source_rgba(CAIRO, 1, 0, 1, 1);
|
||||||
|
cairo_rectangle(CAIRO, 256, 0, 256, 256);
|
||||||
|
cairo_fill(CAIRO);
|
||||||
|
cairo_rectangle(CAIRO, 0, 256, 256, 256);
|
||||||
|
cairo_fill(CAIRO);
|
||||||
|
cairo_restore(CAIRO);
|
||||||
|
|
||||||
|
cairo_surface_flush(CAIROSURFACE);
|
||||||
|
|
||||||
|
tex->m_vSize = {512, 512};
|
||||||
|
|
||||||
|
// copy the data to an OpenGL texture we have
|
||||||
|
const GLint glFormat = GL_RGBA;
|
||||||
|
const GLint glType = GL_UNSIGNED_BYTE;
|
||||||
|
|
||||||
|
const auto DATA = cairo_image_surface_get_data(CAIROSURFACE);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, tex->m_iTexID);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
|
#ifndef GLES2
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_BLUE);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED);
|
||||||
|
#endif
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, glFormat, tex->m_vSize.x, tex->m_vSize.y, 0, glFormat, glType, DATA);
|
||||||
|
|
||||||
|
cairo_surface_destroy(CAIROSURFACE);
|
||||||
|
cairo_destroy(CAIRO);
|
||||||
|
|
||||||
|
m_pMissingAssetTexture = tex;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CHyprOpenGLImpl::initAssets() {
|
||||||
|
initMissingAssetTexture();
|
||||||
|
|
||||||
|
static auto PFORCEWALLPAPER = CConfigValue<Hyprlang::INT>("misc:force_default_wallpaper");
|
||||||
|
|
||||||
|
const auto FORCEWALLPAPER = std::clamp(*PFORCEWALLPAPER, static_cast<int64_t>(-1L), static_cast<int64_t>(2L));
|
||||||
|
|
||||||
|
m_pLockDeadTexture = loadAsset("lockdead.png");
|
||||||
|
m_pLockDead2Texture = loadAsset("lockdead2.png");
|
||||||
|
|
||||||
m_pLockTtyTextTexture = renderText(std::format("Running on tty {}",
|
m_pLockTtyTextTexture = renderText(std::format("Running on tty {}",
|
||||||
g_pCompositor->m_pAqBackend->hasSession() && g_pCompositor->m_pAqBackend->session->vt > 0 ?
|
g_pCompositor->m_pAqBackend->hasSession() && g_pCompositor->m_pAqBackend->session->vt > 0 ?
|
||||||
std::to_string(g_pCompositor->m_pAqBackend->session->vt) :
|
std::to_string(g_pCompositor->m_pAqBackend->session->vt) :
|
||||||
"unknown"),
|
"unknown"),
|
||||||
CColor{0.9F, 0.9F, 0.9F, 0.7F}, 20, true);
|
CColor{0.9F, 0.9F, 0.9F, 0.7F}, 20, true);
|
||||||
}
|
|
||||||
|
|
||||||
void CHyprOpenGLImpl::createBGTextureForMonitor(PHLMONITOR pMonitor) {
|
// create the default background texture
|
||||||
RASSERT(m_RenderData.pMonitor, "Tried to createBGTex without begin()!");
|
{
|
||||||
|
std::string texPath = std::format("{}", "wall");
|
||||||
Debug::log(LOG, "Creating a texture for BGTex");
|
|
||||||
|
|
||||||
static auto PRENDERTEX = CConfigValue<Hyprlang::INT>("misc:disable_hyprland_logo");
|
|
||||||
static auto PNOSPLASH = CConfigValue<Hyprlang::INT>("misc:disable_splash_rendering");
|
|
||||||
static auto PFORCEWALLPAPER = CConfigValue<Hyprlang::INT>("misc:force_default_wallpaper");
|
|
||||||
|
|
||||||
const auto FORCEWALLPAPER = std::clamp(*PFORCEWALLPAPER, static_cast<int64_t>(-1L), static_cast<int64_t>(2L));
|
|
||||||
|
|
||||||
if (*PRENDERTEX)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// release the last tex if exists
|
|
||||||
const auto PFB = &m_mMonitorBGFBs[pMonitor];
|
|
||||||
PFB->release();
|
|
||||||
|
|
||||||
PFB->alloc(pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y, pMonitor->output->state->state().drmFormat);
|
|
||||||
|
|
||||||
if (!m_pBackgroundTexture) {
|
|
||||||
std::string texPath = "";
|
|
||||||
#ifndef DATAROOTDIR
|
|
||||||
texPath = "/usr/share/hypr/wall";
|
|
||||||
#else
|
|
||||||
texPath = std::format("{}{}", DATAROOTDIR, "/hypr/wall");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// get the adequate tex
|
// get the adequate tex
|
||||||
if (FORCEWALLPAPER == -1) {
|
if (FORCEWALLPAPER == -1) {
|
||||||
|
@ -2767,15 +2812,29 @@ void CHyprOpenGLImpl::createBGTextureForMonitor(PHLMONITOR pMonitor) {
|
||||||
|
|
||||||
texPath += ".png";
|
texPath += ".png";
|
||||||
|
|
||||||
// check if wallpapers exist
|
|
||||||
std::error_code err;
|
|
||||||
if (!std::filesystem::exists(texPath, err)) {
|
|
||||||
Debug::log(ERR, "createBGTextureForMonitor: failed, file \"{}\" doesn't exist or access denied, ec: {}", texPath, err.message());
|
|
||||||
return; // the texture will be empty, oh well. We'll clear with a solid color anyways.
|
|
||||||
}
|
|
||||||
|
|
||||||
m_pBackgroundTexture = loadAsset(texPath);
|
m_pBackgroundTexture = loadAsset(texPath);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CHyprOpenGLImpl::createBGTextureForMonitor(PHLMONITOR pMonitor) {
|
||||||
|
RASSERT(m_RenderData.pMonitor, "Tried to createBGTex without begin()!");
|
||||||
|
|
||||||
|
Debug::log(LOG, "Creating a texture for BGTex");
|
||||||
|
|
||||||
|
static auto PRENDERTEX = CConfigValue<Hyprlang::INT>("misc:disable_hyprland_logo");
|
||||||
|
static auto PNOSPLASH = CConfigValue<Hyprlang::INT>("misc:disable_splash_rendering");
|
||||||
|
|
||||||
|
if (*PRENDERTEX)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// release the last tex if exists
|
||||||
|
const auto PFB = &m_mMonitorBGFBs[pMonitor];
|
||||||
|
PFB->release();
|
||||||
|
|
||||||
|
PFB->alloc(pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y, pMonitor->output->state->state().drmFormat);
|
||||||
|
|
||||||
|
if (!m_pBackgroundTexture) // ?!?!?!
|
||||||
|
return;
|
||||||
|
|
||||||
// create a new one with cairo
|
// create a new one with cairo
|
||||||
SP<CTexture> tex = makeShared<CTexture>();
|
SP<CTexture> tex = makeShared<CTexture>();
|
||||||
|
|
|
@ -215,11 +215,12 @@ class CHyprOpenGLImpl {
|
||||||
|
|
||||||
GLint m_iCurrentOutputFb = 0;
|
GLint m_iCurrentOutputFb = 0;
|
||||||
|
|
||||||
int m_iGBMFD = -1;
|
int m_iGBMFD = -1;
|
||||||
gbm_device* m_pGbmDevice = nullptr;
|
gbm_device* m_pGbmDevice = nullptr;
|
||||||
EGLContext m_pEglContext = nullptr;
|
EGLContext m_pEglContext = nullptr;
|
||||||
EGLDisplay m_pEglDisplay = nullptr;
|
EGLDisplay m_pEglDisplay = nullptr;
|
||||||
EGLDeviceEXT m_pEglDevice = nullptr;
|
EGLDeviceEXT m_pEglDevice = nullptr;
|
||||||
|
uint failedAssetsNo = 0;
|
||||||
|
|
||||||
bool m_bReloadScreenShader = true; // at launch it can be set
|
bool m_bReloadScreenShader = true; // at launch it can be set
|
||||||
|
|
||||||
|
@ -277,7 +278,7 @@ class CHyprOpenGLImpl {
|
||||||
CShader m_sFinalScreenShader;
|
CShader m_sFinalScreenShader;
|
||||||
CTimer m_tGlobalTimer;
|
CTimer m_tGlobalTimer;
|
||||||
|
|
||||||
SP<CTexture> m_pBackgroundTexture, m_pLockDeadTexture, m_pLockDead2Texture, m_pLockTtyTextTexture;
|
SP<CTexture> m_pMissingAssetTexture, m_pBackgroundTexture, m_pLockDeadTexture, m_pLockDead2Texture, m_pLockTtyTextTexture;
|
||||||
|
|
||||||
void logShaderError(const GLuint&, bool program = false);
|
void logShaderError(const GLuint&, bool program = false);
|
||||||
GLuint createProgram(const std::string&, const std::string&, bool dynamic = false);
|
GLuint createProgram(const std::string&, const std::string&, bool dynamic = false);
|
||||||
|
@ -290,6 +291,7 @@ class CHyprOpenGLImpl {
|
||||||
SP<CTexture> loadAsset(const std::string& file);
|
SP<CTexture> loadAsset(const std::string& file);
|
||||||
SP<CTexture> renderText(const std::string& text, CColor col, int pt, bool italic = false);
|
SP<CTexture> renderText(const std::string& text, CColor col, int pt, bool italic = false);
|
||||||
void initAssets();
|
void initAssets();
|
||||||
|
void initMissingAssetTexture();
|
||||||
|
|
||||||
//
|
//
|
||||||
std::optional<std::vector<uint64_t>> getModsForFormat(EGLint format);
|
std::optional<std::vector<uint64_t>> getModsForFormat(EGLint format);
|
||||||
|
|
|
@ -1063,22 +1063,16 @@ void CHyprRenderer::renderSessionLockMissing(PHLMONITOR pMonitor) {
|
||||||
|
|
||||||
if (ANY_PRESENT) {
|
if (ANY_PRESENT) {
|
||||||
// render image2, without instructions. Lock still "alive", unless texture dead
|
// render image2, without instructions. Lock still "alive", unless texture dead
|
||||||
if (g_pHyprOpenGL->m_pLockDead2Texture)
|
g_pHyprOpenGL->renderTexture(g_pHyprOpenGL->m_pLockDead2Texture, &monbox, ALPHA);
|
||||||
g_pHyprOpenGL->renderTexture(g_pHyprOpenGL->m_pLockDead2Texture, &monbox, ALPHA);
|
|
||||||
else
|
|
||||||
g_pHyprOpenGL->renderRect(&monbox, CColor(1.0, 0.2, 0.2, ALPHA));
|
|
||||||
} else {
|
} else {
|
||||||
// render image, with instructions. Lock is gone.
|
// render image, with instructions. Lock is gone.
|
||||||
if (g_pHyprOpenGL->m_pLockDeadTexture) {
|
g_pHyprOpenGL->renderTexture(g_pHyprOpenGL->m_pLockDeadTexture, &monbox, ALPHA);
|
||||||
g_pHyprOpenGL->renderTexture(g_pHyprOpenGL->m_pLockDeadTexture, &monbox, ALPHA);
|
|
||||||
|
|
||||||
// also render text for the tty number
|
// also render text for the tty number
|
||||||
if (g_pHyprOpenGL->m_pLockTtyTextTexture) {
|
if (g_pHyprOpenGL->m_pLockTtyTextTexture) {
|
||||||
CBox texbox = {{}, g_pHyprOpenGL->m_pLockTtyTextTexture->m_vSize};
|
CBox texbox = {{}, g_pHyprOpenGL->m_pLockTtyTextTexture->m_vSize};
|
||||||
g_pHyprOpenGL->renderTexture(g_pHyprOpenGL->m_pLockTtyTextTexture, &texbox, 1.F);
|
g_pHyprOpenGL->renderTexture(g_pHyprOpenGL->m_pLockTtyTextTexture, &texbox, 1.F);
|
||||||
}
|
}
|
||||||
} else
|
|
||||||
g_pHyprOpenGL->renderRect(&monbox, CColor(1.0, 0.2, 0.2, ALPHA));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ALPHA < 1.f) /* animate */
|
if (ALPHA < 1.f) /* animate */
|
||||||
|
|
Loading…
Reference in a new issue