mirror of
https://github.com/gnif/LookingGlass.git
synced 2025-10-13 19:08:11 +00:00
[client] add SVG loading support and use icons for status display
This brings nanosvg into the project for SVG loading and rendering. Unfortunatly we can not at this time use a submodule for this project until https://github.com/memononen/nanosvg/pull/214 is merged.
This commit is contained in:
@@ -676,7 +676,7 @@ void app_msgBoxClose(MsgBoxHandle handle)
|
||||
|
||||
void app_showRecord(bool show)
|
||||
{
|
||||
overlayRecord_show(show);
|
||||
overlayStatus_set(LG_USER_STATUS_RECORDING, show);
|
||||
}
|
||||
|
||||
KeybindHandle app_registerKeybind(int sc, KeybindFn callback, void * opaque, const char * description)
|
||||
@@ -1072,4 +1072,5 @@ void app_useSpiceDisplay(bool enable)
|
||||
renderQueue_spiceShow(false);
|
||||
purespice_disconnectChannel(PS_CHANNEL_DISPLAY);
|
||||
}
|
||||
overlayStatus_set(LG_USER_STATUS_SPICE, enable);
|
||||
}
|
||||
|
@@ -1725,7 +1725,7 @@ int main(int argc, char * argv[])
|
||||
app_registerOverlay(&LGOverlayGraphs, NULL);
|
||||
app_registerOverlay(&LGOverlayHelp , NULL);
|
||||
app_registerOverlay(&LGOverlayMsg , NULL);
|
||||
app_registerOverlay(&LGOverlayRecord, NULL);
|
||||
app_registerOverlay(&LGOverlayStatus, NULL);
|
||||
|
||||
// early renderer setup for option registration
|
||||
for(unsigned int i = 0; i < LG_RENDERER_COUNT; ++i)
|
||||
|
@@ -1,87 +0,0 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 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 "interface/overlay.h"
|
||||
#include "cimgui.h"
|
||||
#include "overlay_utils.h"
|
||||
|
||||
#include "common/stringutils.h"
|
||||
|
||||
#include "../main.h"
|
||||
|
||||
static bool recordShow = false;
|
||||
static bool recordToggle = false;
|
||||
static unsigned long long lastTick = 0;
|
||||
|
||||
static bool record_init(void ** udata, const void * params)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static void record_free(void * udata)
|
||||
{}
|
||||
|
||||
static int record_render(void * udata, bool interactive, struct Rect * windowRects,
|
||||
int maxRects)
|
||||
{
|
||||
if (!recordShow || !recordToggle)
|
||||
return 0;
|
||||
|
||||
ImVec2 * screen = overlayGetScreenSize();
|
||||
ImDrawList_AddCircleFilled(igGetBackgroundDrawList_Nil(),
|
||||
(ImVec2) { screen->x - 20.0f, 20.0f },
|
||||
5.0f, 0xFF0000FF, 0
|
||||
);
|
||||
|
||||
*windowRects = (struct Rect) {
|
||||
.x = screen->x - 26, .y = 14, .w = 12, .h = 12
|
||||
};
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool record_tick(void * udata, unsigned long long tickCount)
|
||||
{
|
||||
if (tickCount - lastTick >= 25)
|
||||
{
|
||||
recordToggle = !recordToggle;
|
||||
lastTick = tickCount;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
struct LG_OverlayOps LGOverlayRecord =
|
||||
{
|
||||
.name = "record",
|
||||
.init = record_init,
|
||||
.free = record_free,
|
||||
.render = record_render,
|
||||
.tick = record_tick,
|
||||
};
|
||||
|
||||
void overlayRecord_show(bool show)
|
||||
{
|
||||
if (show == recordShow)
|
||||
return;
|
||||
|
||||
recordShow = show;
|
||||
if (g_state.state != APP_STATE_SHUTDOWN)
|
||||
app_invalidateOverlay(true);
|
||||
}
|
134
client/src/overlay/status.c
Normal file
134
client/src/overlay/status.c
Normal file
@@ -0,0 +1,134 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 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 "interface/overlay.h"
|
||||
#include "cimgui.h"
|
||||
|
||||
#include "../overlays.h"
|
||||
#include "../main.h"
|
||||
#include "overlay_utils.h"
|
||||
|
||||
#include "resources/status/recording.svg.h"
|
||||
#include "resources/status/spice.svg.h"
|
||||
|
||||
//TODO: Make this user configurable?
|
||||
#define ICON_SIZE 32
|
||||
|
||||
static bool l_state[LG_USER_STATUS_MAX] = { 0 };
|
||||
static OverlayImage l_image[LG_USER_STATUS_MAX] = { 0 };
|
||||
static bool l_recordToggle;
|
||||
|
||||
static bool status_init(void ** udata, const void * params)
|
||||
{
|
||||
overlayLoadSVG(b_status_recording_svg, b_status_recording_svg_size,
|
||||
&l_image[LG_USER_STATUS_RECORDING], ICON_SIZE, ICON_SIZE);
|
||||
|
||||
overlayLoadSVG(b_status_spice_svg, b_status_spice_svg_size,
|
||||
&l_image[LG_USER_STATUS_SPICE], ICON_SIZE, ICON_SIZE);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void status_free(void * udata)
|
||||
{
|
||||
for(int i = 0; i < LG_USER_STATUS_MAX; ++i)
|
||||
overlayFreeImage(&l_image[i]);
|
||||
}
|
||||
|
||||
static int status_render(void * udata, bool interactive, struct Rect * windowRects,
|
||||
int maxRects)
|
||||
{
|
||||
const int marginX = 10;
|
||||
const int marginY = 10;
|
||||
const int gapX = 5;
|
||||
|
||||
ImVec2 * screen = overlayGetScreenSize();
|
||||
struct Rect rect = {
|
||||
.x = screen->x - LG_USER_STATUS_MAX * (ICON_SIZE + gapX) - marginX,
|
||||
.y = marginY,
|
||||
.w = LG_USER_STATUS_MAX * (ICON_SIZE + gapX),
|
||||
.h = ICON_SIZE
|
||||
};
|
||||
|
||||
int xPos = screen->x - marginX;
|
||||
for(int i = 0; i < LG_USER_STATUS_MAX; ++i)
|
||||
{
|
||||
OverlayImage * img = &l_image[i];
|
||||
if (!l_state[i] || !img->tex)
|
||||
continue;
|
||||
|
||||
// if the recording indicator is off, don't draw but reserve space
|
||||
if (i == LG_USER_STATUS_RECORDING && !l_recordToggle)
|
||||
goto next;
|
||||
|
||||
ImDrawList_AddImage(
|
||||
igGetBackgroundDrawList_Nil(),
|
||||
img->tex,
|
||||
(ImVec2){
|
||||
xPos,
|
||||
marginY
|
||||
},
|
||||
(ImVec2){
|
||||
xPos - ICON_SIZE,
|
||||
img->height + marginY
|
||||
},
|
||||
(ImVec2){ 0, 0 },
|
||||
(ImVec2){ 1, 1 },
|
||||
0xFFFFFFFF);
|
||||
|
||||
next:
|
||||
xPos -= ICON_SIZE + gapX;
|
||||
}
|
||||
|
||||
*windowRects = rect;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool status_tick(void * udata, unsigned long long tickCount)
|
||||
{
|
||||
static unsigned long long lastTick = 0;
|
||||
|
||||
if (tickCount - lastTick >= 25)
|
||||
{
|
||||
l_recordToggle = !l_recordToggle;
|
||||
lastTick = tickCount;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
struct LG_OverlayOps LGOverlayStatus =
|
||||
{
|
||||
.name = "status",
|
||||
.init = status_init,
|
||||
.free = status_free,
|
||||
.render = status_render,
|
||||
.tick = status_tick,
|
||||
};
|
||||
|
||||
void overlayStatus_set(LGUserStatus status, bool value)
|
||||
{
|
||||
if (l_state[status] == value)
|
||||
return;
|
||||
|
||||
l_state[status] = value;
|
||||
app_invalidateOverlay(true);
|
||||
};
|
@@ -26,6 +26,11 @@
|
||||
#include "cimgui.h"
|
||||
#include "main.h"
|
||||
|
||||
#define NANOSVG_IMPLEMENTATION
|
||||
#define NANOSVG_ALL_COLOR_KEYWORDS
|
||||
#define NANOSVGRAST_IMPLEMENTATION
|
||||
#include "nanosvgrast.h"
|
||||
|
||||
void overlayGetImGuiRect(struct Rect * rect)
|
||||
{
|
||||
ImVec2 size;
|
||||
@@ -78,3 +83,93 @@ void overlayTextMaybeURL(const char * text, bool wrapped)
|
||||
else
|
||||
igText(text);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
RENDERER(freeTexture, image->tex);
|
||||
}
|
||||
|
@@ -39,7 +39,7 @@ extern struct LG_OverlayOps LGOverlayGraphs;
|
||||
extern struct LG_OverlayOps LGOverlayHelp;
|
||||
extern struct LG_OverlayOps LGOverlayConfig;
|
||||
extern struct LG_OverlayOps LGOverlayMsg;
|
||||
extern struct LG_OverlayOps LGOverlayRecord;
|
||||
extern struct LG_OverlayOps LGOverlayStatus;
|
||||
|
||||
void overlayAlert_show(LG_MsgAlert type, const char * fmt, va_list args);
|
||||
|
||||
@@ -56,6 +56,14 @@ void overlayConfig_register(const char * title,
|
||||
void overlayConfig_registerTab(const char * title,
|
||||
void (*callback)(void * udata, int * id), void * udata);
|
||||
|
||||
void overlayRecord_show(bool show);
|
||||
typedef enum LG_UserStatus
|
||||
{
|
||||
LG_USER_STATUS_SPICE,
|
||||
LG_USER_STATUS_RECORDING,
|
||||
LG_USER_STATUS_MAX
|
||||
}
|
||||
LGUserStatus;
|
||||
|
||||
void overlayStatus_set(LGUserStatus, bool value);
|
||||
|
||||
#endif
|
||||
|
Reference in New Issue
Block a user