quakeforge/libs/ruamoko/rua_gui.c
Bill Currie 9317ce7358 [ruamoko] Handle missing fonts correctly
Carrying on as if the missing font had been loaded leads to way too many
issues for it to be a good thing (not that that really needs to be
said). Fixes the segfaults in my test scene.
2023-03-28 13:23:14 +09:00

494 lines
12 KiB
C

/*
r_gui.c
Ruamoko GUI support functions
Copyright (C) 2022 Bill Currie <bill@taniwha.org>
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:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#include <stdlib.h>
#include "QF/draw.h"
#include "QF/progs.h"
#include "QF/quakefs.h"
#include "QF/render.h"
#include "QF/ui/font.h"
#include "QF/ui/passage.h"
#include "QF/ui/text.h"
#include "QF/ui/view.h"
#include "QF/plugin/vid_render.h"
#include "rua_internal.h"
typedef struct rua_passage_s {
struct rua_passage_s *next;
struct rua_passage_s **prev;
passage_t *passage;
} rua_passage_t;
typedef struct rua_font_s {
struct rua_font_s *next;
struct rua_font_s **prev;
font_t *font;
} rua_font_t;
typedef struct {
progs_t *pr;
PR_RESMAP (rua_passage_t) passage_map;
rua_passage_t *passages;
PR_RESMAP (rua_font_t) font_map;
rua_font_t *fonts;
ecs_registry_t *reg;
uint32_t text_base;
uint32_t view_base;
uint32_t passage_base;
} gui_resources_t;
static rua_passage_t *
passage_new (gui_resources_t *res)
{
return PR_RESNEW (res->passage_map);
}
static void
passage_free (gui_resources_t *res, rua_passage_t *passage)
{
if (passage->next) {
passage->next->prev = passage->prev;
}
*passage->prev = passage->next;
PR_RESFREE (res->passage_map, passage);
}
static void
passage_reset (gui_resources_t *res)
{
PR_RESRESET (res->passage_map);
}
static inline rua_passage_t *
passage_get (gui_resources_t *res, int index)
{
return PR_RESGET(res->passage_map, index);
}
static inline int __attribute__((pure))
passage_index (gui_resources_t *res, rua_passage_t *passage)
{
return PR_RESINDEX(res->passage_map, passage);
}
static int
alloc_passage (gui_resources_t *res, passage_t *passage)
{
rua_passage_t *psg = passage_new (res);
psg->next = res->passages;
psg->prev = &res->passages;
if (res->passages) {
res->passages->prev = &psg->next;
}
res->passages = psg;
psg->passage = passage;
return passage_index (res, psg);
}
static rua_passage_t * __attribute__((pure))
_get_passage (gui_resources_t *res, int handle, const char *func)
{
rua_passage_t *psg = passage_get (res, handle);
if (!psg) {
PR_RunError (res->pr, "invalid passage handle passed to %s", func);
}
return psg;
}
#define get_passage(res, handle) _get_passage (res, handle, __FUNCTION__ + 3)
static rua_font_t *
font_new (gui_resources_t *res)
{
return PR_RESNEW (res->font_map);
}
static void
font_free (gui_resources_t *res, rua_font_t *font)
{
PR_RESFREE (res->font_map, font);
}
static void
font_reset (gui_resources_t *res)
{
PR_RESRESET (res->font_map);
}
static inline rua_font_t *
font_get (gui_resources_t *res, int index)
{
return PR_RESGET(res->font_map, index);
}
static inline int __attribute__((pure))
font_index (gui_resources_t *res, rua_font_t *font)
{
return PR_RESINDEX(res->font_map, font);
}
static int
alloc_font (gui_resources_t *res, font_t *font)
{
rua_font_t *fnt = font_new (res);
fnt->next = res->fonts;
fnt->prev = &res->fonts;
if (res->fonts) {
res->fonts->prev = &fnt->next;
}
res->fonts = fnt;
fnt->font = font;
return font_index (res, fnt);
}
static rua_font_t * __attribute__((pure))
_get_font (gui_resources_t *res, int handle, const char *func)
{
rua_font_t *psg = font_get (res, handle);
if (!psg) {
PR_RunError (res->pr, "invalid font handle passed to %s", func);
}
return psg;
}
#define get_font(res, handle) _get_font (res, handle, __FUNCTION__ + 3)
static void
bi_gui_clear (progs_t *pr, void *_res)
{
gui_resources_t *res = _res;
for (rua_passage_t *psg = res->passages; psg; psg = psg->next) {
Passage_Delete (psg->passage);
}
res->passages = 0;
passage_reset (res);
rua_font_t *font;
for (font = res->fonts; font; font = font->next) {
//FIXME can't unload fonts
}
res->fonts = 0;
font_reset (res);
}
static void
bi_gui_destroy (progs_t *pr, void *_res)
{
gui_resources_t *res = _res;
ECS_DelRegistry (res->reg);
PR_RESDELMAP (res->passage_map);
PR_RESDELMAP (res->font_map);
free (res);
}
#define bi(x) static void bi_##x (progs_t *pr, void *_res)
bi (Font_Load)
{
gui_resources_t *res = _res;
const char *font_path = P_GSTRING (pr, 0);
int font_size = P_INT (pr, 1);
R_INT (pr) = 0;
QFile *font_file = QFS_FOpenFile (font_path);
font_t *font = Font_Load (font_file, font_size);
if (font) {
R_INT (pr) = alloc_font (res, font);
}
}
bi (Font_Free)
{
gui_resources_t *res = _res;
rua_font_t *font = get_font (res, P_INT (pr, 0));
Font_Free (font->font);
font_free (res, font);
}
bi (Passage_New)
{
gui_resources_t *res = _res;
ecs_system_t passage_sys = { .reg = res->reg, .base = res->passage_base };
passage_t *passage = Passage_New (passage_sys);
R_INT (pr) = alloc_passage (res, passage);
}
bi (Passage_ParseText)
{
gui_resources_t *res = _res;
int handle = P_INT (pr, 0);
rua_passage_t *psg = get_passage (res, handle);
const char *text = P_GSTRING (pr, 1);
Passage_ParseText (psg->passage, text);
}
bi (Passage_Delete)
{
gui_resources_t *res = _res;
int handle = P_INT (pr, 0);
rua_passage_t *psg = get_passage (res, handle);
Passage_Delete (psg->passage);
passage_free (res, psg);
}
bi (Passage_ChildCount)
{
gui_resources_t *res = _res;
rua_passage_t *psg = get_passage (res, P_INT (pr, 0));
uint32_t par = P_UINT (pr, 1);
R_UINT (pr) = psg->passage->hierarchy->childCount[par];
}
bi (Passage_GetChild)
{
gui_resources_t *res = _res;
rua_passage_t *psg = get_passage (res, P_INT (pr, 0));
hierarchy_t *h = psg->passage->hierarchy;
uint32_t par = P_UINT (pr, 1);
uint32_t index = P_UINT (pr, 2);
R_UINT (pr) = h->ent[h->childIndex[par] + index];
}
bi (Text_View)
{
gui_resources_t *res = _res;
rua_font_t *font = get_font (res, P_INT (pr, 0));
rua_passage_t *psg = get_passage (res, P_INT (pr, 1));
ecs_system_t viewsys = { .reg = res->reg, .base = res->view_base };
view_t view = Text_View (viewsys, font->font, psg->passage);
R_INT (pr) = view.id;//FIXME
}
bi (Text_SetScript)
{
gui_resources_t *res = _res;
uint32_t textent = P_UINT (pr, 0);
const char *lang = P_GSTRING (pr, 1);
int script = P_INT (pr, 1);
int dir = P_INT (pr, 1);
ecs_system_t textsys = { .reg = res->reg, .base = res->text_base };
Text_SetScript (textsys, textent, lang, script, dir);
}
static void
draw_glyphs (view_pos_t *abs, glyphset_t *glyphset, glyphref_t *gref)
{
uint32_t count = gref->count;
glyphobj_t *glyph = glyphset->glyphs + gref->start;
while (count-- > 0) {
glyphobj_t *g = glyph++;
r_funcs->Draw_Glyph (abs->x + g->x, abs->y + g->y,
g->fontid, g->glyphid, 254);
}
}
static void
draw_box (view_pos_t *abs, view_pos_t *len, uint32_t ind, int c)
{
int x = abs[ind].x;
int y = abs[ind].y;
int w = len[ind].x;
int h = len[ind].y;
r_funcs->Draw_Line (x, y, x + w, y, c);
r_funcs->Draw_Line (x, y + h, x + w, y + h, c);
r_funcs->Draw_Line (x, y, x, y + h, c);
r_funcs->Draw_Line (x + w, y, x + w, y + h, c);
}
bi (Text_Draw)
{
gui_resources_t *res = _res;
uint32_t passage_glyphs = res->text_base + text_passage_glyphs;
uint32_t glyphs = res->text_base + text_glyphs;
uint32_t vhref = res->view_base + view_href;
ecs_pool_t *pool = &res->reg->comp_pools[passage_glyphs];
uint32_t count = pool->count;
uint32_t *ent = pool->dense;
glyphset_t *glyphset = pool->data;
while (count-- > 0) {
view_t psg_view = { .id = *ent++, .reg = res->reg, .comp = vhref};
// first child is always a paragraph view, and all views after the
// first paragraph's first child are all text views
view_t para_view = View_GetChild (psg_view, 0);
view_t text_view = View_GetChild (para_view, 0);
hierref_t *href = View_GetRef (text_view);
glyphset_t *gs = glyphset++;
hierarchy_t *h = href->hierarchy;
view_pos_t *abs = h->components[view_abs];
view_pos_t *len = h->components[view_len];
for (uint32_t i = href->index; i < h->num_objects; i++) {
glyphref_t *gref = Ent_GetComponent (h->ent[i], glyphs, res->reg);
draw_glyphs (&abs[i], gs, gref);
if (0) draw_box (abs, len, i, 253);
}
if (0) {
for (uint32_t i = 1; i < href->index; i++) {
draw_box (abs, len, i, 251);
}
}
}
}
bi (View_Delete)
{
gui_resources_t *res = _res;
uint32_t viewid = P_UINT (pr, 0);
view_t view = { .id = viewid, .reg = res->reg,
.comp = res->view_base + view_href };
View_Delete (view);
}
bi (View_ChildCount)
{
gui_resources_t *res = _res;
uint32_t viewid = P_UINT (pr, 0);
view_t view = { .id = viewid, .reg = res->reg,
.comp = res->view_base + view_href };
R_UINT (pr) = View_ChildCount (view);
}
bi (View_GetChild)
{
gui_resources_t *res = _res;
uint32_t viewid = P_UINT (pr, 0);
uint32_t index = P_UINT (pr, 1);
view_t view = { .id = viewid, .reg = res->reg,
.comp = res->view_base + view_href };
R_UINT (pr) = View_GetChild (view, index).id;
}
bi (View_SetPos)
{
gui_resources_t *res = _res;
uint32_t viewid = P_UINT (pr, 0);
int x = P_INT (pr, 1);
int y = P_INT (pr, 2);
view_t view = { .id = viewid, .reg = res->reg,
.comp = res->view_base + view_href };
View_SetPos (view, x, y);
}
bi (View_SetLen)
{
gui_resources_t *res = _res;
uint32_t viewid = P_UINT (pr, 0);
int x = P_INT (pr, 1);
int y = P_INT (pr, 2);
view_t view = { .id = viewid, .reg = res->reg,
.comp = res->view_base + view_href };
View_SetLen (view, x, y);
}
bi (View_GetLen)
{
gui_resources_t *res = _res;
uint32_t viewid = P_UINT (pr, 0);
view_t view = { .id = viewid, .reg = res->reg,
.comp = res->view_base + view_href };
view_pos_t l = View_GetLen (view);
R_var (pr, ivec2) = (pr_ivec2_t) { l.x, l.y };
}
bi (View_UpdateHierarchy)
{
gui_resources_t *res = _res;
uint32_t viewid = P_UINT (pr, 0);
view_t view = { .id = viewid, .reg = res->reg,
.comp = res->view_base + view_href };
View_UpdateHierarchy (view);
}
#undef bi
#define bi(x,np,params...) {#x, bi_##x, -1, np, {params}}
#define p(type) PR_PARAM(type)
#define P(a, s) { .size = (s), .alignment = BITOP_LOG2 (a), }
static builtin_t builtins[] = {
bi(Font_Load, 2, p(string), p(int)),
bi(Font_Free, 2, p(int)),
bi(Passage_New, 0),
bi(Passage_ParseText, 2, p(ptr), p(string)),
bi(Passage_Delete, 1, p(ptr)),
bi(Passage_ChildCount, 2, p(ptr), p(uint)),
bi(Passage_GetChild, 3, p(ptr), p(uint), p(uint)),
bi(Text_View, 2, p(ptr), p(int)),
bi(Text_SetScript, 4, p(uint), p(string), p(int), p (int)),
bi(View_Delete, 1, p(uint)),
bi(View_ChildCount, 1, p(uint)),
bi(View_GetChild, 2, p(uint), p(uint)),
bi(View_SetPos, 3, p(uint), p(int), p(int)),
bi(View_SetLen, 3, p(uint), p(int), p(int)),
bi(View_GetLen, 1, p(uint)),
bi(View_UpdateHierarchy,1, p(uint)),
bi(Text_Draw, 0),
{0}
};
void
RUA_GUI_Init (progs_t *pr, int secure)
{
gui_resources_t *res = calloc (1, sizeof (gui_resources_t));
res->pr = pr;
PR_Resources_Register (pr, "Draw", res, bi_gui_clear, bi_gui_destroy);
PR_RegisterBuiltins (pr, builtins, res);
res->reg = ECS_NewRegistry ();
res->text_base = ECS_RegisterComponents (res->reg, text_components,
text_comp_count);
res->view_base = ECS_RegisterComponents (res->reg, view_components,
view_comp_count);
res->passage_base = ECS_RegisterComponents (res->reg, passage_components,
passage_comp_count);
ECS_CreateComponentPools (res->reg);
}