Files
LookingGlass/client/src/overlay_utils.c
Amit Mendapara 8187177c7f
Some checks failed
build / client (Debug, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / module (push) Has been cancelled
build / host-linux (push) Has been cancelled
build / host-windows-cross (push) Has been cancelled
build / host-windows-native (push) Has been cancelled
build / idd (push) Has been cancelled
build / obs (clang) (push) Has been cancelled
build / obs (gcc) (push) Has been cancelled
build / docs (push) Has been cancelled
[client] app: free overlays after display server shutdown
The render thread freed the overlays as soon as it exited, while the
main thread could still be dispatching display server input callbacks
that iterate them. Free the overlays in lg_shutdown instead, once the
render thread has been joined and the display server has shut down.

The overlays now outlive the renderer, so overlayFreeImage must not
call into it; renderer deinitialization has already destroyed all
textures along with the context.
2026-08-12 02:50:01 +10:00

191 lines
4.5 KiB
C

/**
* Looking Glass
* Copyright © 2017-2026 The Looking Glass Authors
* https://looking-glass.io
*
* 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 2 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, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "overlay_utils.h"
#include <math.h>
#include <string.h>
#include "common/open.h"
#include "cimgui.h"
#include "main.h"
#define NANOSVG_IMPLEMENTATION
#define NANOSVG_ALL_COLOR_KEYWORDS
#define NANOSVGRAST_IMPLEMENTATION
#ifndef __clang__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#include "nanosvgrast.h"
#pragma GCC diagnostic pop
#else
#include "nanosvgrast.h"
#endif
void overlayGetImGuiRect(struct Rect * rect)
{
const ImVec2 pos = igGetWindowPos();
const ImVec2 size = igGetWindowSize();
const float border = igGetStyle()->WindowBorderSize;
const float margin = border > 1.0f ? border : 1.0f;
const int left = (int)floorf(pos.x - margin);
const int top = (int)floorf(pos.y - margin);
const int right = (int)ceilf(pos.x + size.x + margin);
const int bottom = (int)ceilf(pos.y + size.y + margin);
rect->x = left;
rect->y = top;
rect->w = right - left;
rect->h = bottom - top;
}
ImVec2 * overlayGetScreenSize(void)
{
return &g_state.io->DisplaySize;
}
static void overlayAddUnderline(ImU32 color)
{
ImVec2 min = igGetItemRectMin();
ImVec2 max = igGetItemRectMax();
min.y = max.y;
ImDrawList_AddLine(igGetWindowDrawList(), min, max, color, 1.0f);
}
void overlayTextURL(const char * url, const char * text)
{
igTextUnformatted(text ? text : url, NULL);
if (igIsItemHovered(ImGuiHoveredFlags_None))
{
if (igIsItemClicked(ImGuiMouseButton_Left))
lgOpenURL(url);
overlayAddUnderline(igGetColorU32_Vec4(
*igGetStyleColorVec4(ImGuiCol_ButtonHovered)));
igSetMouseCursor(ImGuiMouseCursor_Hand);
igSetTooltip("Open in browser: %s", url);
}
}
void overlayTextMaybeURL(const char * text, bool wrapped)
{
if (strncmp(text, "https://", 8) == 0)
overlayTextURL(text, NULL);
else if (wrapped)
igTextWrapped("%s", text);
else
igTextUnformatted(text, NULL);
}
bool overlayLoadSVG(const char * data, unsigned int size, OverlayImage * image,
int width, int height)
{
image->tex = NULL;
//nsvgParse alters the data, we need to make a copy and null terminate it
char * svg = malloc(size + 1);
if (!svg)
{
DEBUG_ERROR("out of ram");
goto err;
}
memcpy(svg, data, size);
svg[size] = 0;
NSVGimage * nvi = nsvgParse(svg, "px", 96.0);
if (!nvi)
{
free(svg);
DEBUG_ERROR("nvsgParseFromData failed");
goto err;
}
free(svg);
NSVGrasterizer * rast = nsvgCreateRasterizer();
if (!rast)
{
DEBUG_ERROR("nsvgCreateRasterizer failed");
goto err_image;
}
double srcAspect = nvi->width / nvi->height;
double dstAspect = (double)width / (double)height;
float scale;
if (dstAspect > srcAspect)
{
image->width = (double)height * srcAspect;
image->height = height;
scale = (float)image->width / nvi->width;
}
else
{
image->width = width;
image->height = (double)width / srcAspect;
scale = (float)image->height / nvi->height;
}
uint8_t * img = malloc(image->width * image->height * 4);
if (!img)
{
DEBUG_ERROR("out of ram");
goto err_rast;
}
nsvgRasterize(rast, nvi,
0.0f, 0.0f,
scale,
img,
image->width,
image->height,
image->width * 4);
image->tex = RENDERER(createTexture, image->width, image->height, img);
free(img);
if (!image->tex)
{
DEBUG_ERROR("renderer failed to create the texture");
goto err_rast;
}
err_rast:
nsvgDeleteRasterizer(rast);
err_image:
nsvgDelete(nvi);
err:
return image->tex != NULL;
}
void overlayFreeImage(OverlayImage * image)
{
if (!image->tex)
return;
// During shutdown the renderer is freed first, taking all textures with it
if (g_state.lgr)
RENDERER(freeTexture, image->tex);
image->tex = NULL;
}