Finish markup parsing rewrite to avoid mallocs.
For that matter, use elements as a filter for elements we care about, and let the hook implement whatever it needs without duplicating everything. The resulting algorithm is still O(n²) where n is the number of filtered elements (3 at most right now), which isn't bad if we don't need to get too many elements, but at least it's not quadratic in the number of attributes anymore. Speedup improvements could be done using gperf btw. Signed-off-by: Pierre Habouzit <madcoder@debian.org>
This commit is contained in:
parent
37d69b8dcc
commit
b4b381e947
24
client.c
24
client.c
|
@ -759,6 +759,17 @@ client_updatesizehints(client_t *c)
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Markup parsing hook.
|
||||||
|
* For now only substitute <title />
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
client_markup_on_elem(markup_parser_data_t *p, const char *elem,
|
||||||
|
const char **names, const char **values)
|
||||||
|
{
|
||||||
|
assert (!strcmp(elem, "title"));
|
||||||
|
buffer_add_xmlescaped(&p->text, p->priv);
|
||||||
|
}
|
||||||
|
|
||||||
/** Parse a markup string which contains special markup sequence relative to a
|
/** Parse a markup string which contains special markup sequence relative to a
|
||||||
* client, i.e. its title, etc.
|
* client, i.e. its title, etc.
|
||||||
* \param c The client concerned by the markup string.
|
* \param c The client concerned by the markup string.
|
||||||
|
@ -768,17 +779,18 @@ client_updatesizehints(client_t *c)
|
||||||
char *
|
char *
|
||||||
client_markup_parse(client_t *c, const char *str, ssize_t len)
|
client_markup_parse(client_t *c, const char *str, ssize_t len)
|
||||||
{
|
{
|
||||||
const char *elements[] = { "title", NULL };
|
static char const * const elements[] = { "title", NULL };
|
||||||
const char *elements_sub[] = { c->name , NULL };
|
markup_parser_data_t p = {
|
||||||
markup_parser_data_t p;
|
.elements = elements,
|
||||||
|
.priv = c->name,
|
||||||
|
.on_element = client_markup_on_elem,
|
||||||
|
};
|
||||||
char *ret;
|
char *ret;
|
||||||
|
|
||||||
markup_parser_data_init(&p, elements, elements_sub, countof(elements));
|
markup_parser_data_init(&p);
|
||||||
|
|
||||||
if(markup_parse(&p, str, len))
|
if(markup_parse(&p, str, len))
|
||||||
{
|
|
||||||
ret = buffer_detach(&p.text);
|
ret = buffer_detach(&p.text);
|
||||||
}
|
|
||||||
else
|
else
|
||||||
ret = a_strdup(str);
|
ret = a_strdup(str);
|
||||||
|
|
||||||
|
|
|
@ -216,15 +216,62 @@ typedef struct
|
||||||
} shadow;
|
} shadow;
|
||||||
} draw_parser_data_t;
|
} draw_parser_data_t;
|
||||||
|
|
||||||
|
static void
|
||||||
|
draw_markup_on_event(markup_parser_data_t *p, const char *elem,
|
||||||
|
const char **names, const char **values)
|
||||||
|
{
|
||||||
|
draw_parser_data_t *data = p->priv;
|
||||||
|
|
||||||
|
/* hack: markup.c validates tags so we can avoid strcmps here */
|
||||||
|
switch (*elem) {
|
||||||
|
case 'b': /* bg */
|
||||||
|
for (; *names; names++, values++)
|
||||||
|
{
|
||||||
|
if(!a_strcmp(*names, "color"))
|
||||||
|
data->has_bg_color = xcolor_new(data->connection, data->phys_screen,
|
||||||
|
*values, &data->bg_color);
|
||||||
|
else if(!a_strcmp(*names, "image"))
|
||||||
|
data->bg_image = draw_image_new(*values);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 't': /* text */
|
||||||
|
for (; *names; names++, values++)
|
||||||
|
{
|
||||||
|
if(!a_strcmp(*names, "align"))
|
||||||
|
data->align = draw_align_get_from_str(*values);
|
||||||
|
else if(!a_strcmp(*names, "shadow"))
|
||||||
|
xcolor_new(data->connection, data->phys_screen, *values,
|
||||||
|
&data->shadow.color);
|
||||||
|
else if(!a_strcmp(*names, "shadow_offset"))
|
||||||
|
data->shadow.offset = atoi(*values);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'm': /* margin */
|
||||||
|
for (; *names; names++, values++)
|
||||||
|
{
|
||||||
|
if(!a_strcmp(*names, "left"))
|
||||||
|
data->margin.left = atoi(*values);
|
||||||
|
else if(!a_strcmp(*names, "right"))
|
||||||
|
data->margin.right = atoi(*values);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
draw_text_markup_expand(draw_parser_data_t *data,
|
draw_text_markup_expand(draw_parser_data_t *data,
|
||||||
const char *str, ssize_t slen)
|
const char *str, ssize_t slen)
|
||||||
{
|
{
|
||||||
const char *elements[] = { "bg", "text", "margin", NULL };
|
static char const * const elements[] = { "bg", "text", "margin", NULL };
|
||||||
markup_parser_data_t p;
|
markup_parser_data_t p = {
|
||||||
int i;
|
.elements = elements,
|
||||||
|
.priv = data,
|
||||||
|
.on_element = &draw_markup_on_event,
|
||||||
|
};
|
||||||
|
|
||||||
markup_parser_data_init(&p, elements, NULL, countof(elements));
|
markup_parser_data_init(&p);
|
||||||
|
|
||||||
if(!markup_parse(&p, str, slen))
|
if(!markup_parse(&p, str, slen))
|
||||||
{
|
{
|
||||||
|
@ -232,34 +279,6 @@ draw_text_markup_expand(draw_parser_data_t *data,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* bg */
|
|
||||||
if(p.attribute_names[0])
|
|
||||||
for(i = 0; p.attribute_names[0][i]; i++)
|
|
||||||
if(!a_strcmp(p.attribute_names[0][i], "color"))
|
|
||||||
data->has_bg_color = xcolor_new(data->connection, data->phys_screen,
|
|
||||||
p.attribute_values[0][i], &data->bg_color);
|
|
||||||
else if(!a_strcmp(p.attribute_names[0][i], "image"))
|
|
||||||
data->bg_image = draw_image_new(p.attribute_values[0][i]);
|
|
||||||
|
|
||||||
/* text */
|
|
||||||
if(p.attribute_names[1])
|
|
||||||
for(i = 0; p.attribute_names[1][i]; i++)
|
|
||||||
if(!a_strcmp(p.attribute_names[1][i], "align"))
|
|
||||||
data->align = draw_align_get_from_str(p.attribute_values[1][i]);
|
|
||||||
else if(!a_strcmp(p.attribute_names[1][i], "shadow"))
|
|
||||||
xcolor_new(data->connection, data->phys_screen,
|
|
||||||
p.attribute_values[1][i], &data->shadow.color);
|
|
||||||
else if(!a_strcmp(p.attribute_names[1][i], "shadow_offset"))
|
|
||||||
data->shadow.offset = atoi(p.attribute_values[1][i]);
|
|
||||||
|
|
||||||
/* margin */
|
|
||||||
if(p.attribute_names[2])
|
|
||||||
for(i = 0; p.attribute_names[2][i]; i++)
|
|
||||||
if(!a_strcmp(p.attribute_names[2][i], "left"))
|
|
||||||
data->margin.left = atoi(p.attribute_values[2][i]);
|
|
||||||
else if(!a_strcmp(p.attribute_names[2][i], "right"))
|
|
||||||
data->margin.right = atoi(p.attribute_values[2][i]);
|
|
||||||
|
|
||||||
/* stole text */
|
/* stole text */
|
||||||
data->text = p.text;
|
data->text = p.text;
|
||||||
buffer_init(&p.text);
|
buffer_init(&p.text);
|
||||||
|
|
|
@ -47,26 +47,12 @@ markup_parse_start_element(GMarkupParseContext *context __attribute__ ((unused))
|
||||||
GError **error __attribute__ ((unused)))
|
GError **error __attribute__ ((unused)))
|
||||||
{
|
{
|
||||||
markup_parser_data_t *p = (markup_parser_data_t *) user_data;
|
markup_parser_data_t *p = (markup_parser_data_t *) user_data;
|
||||||
int i, j;
|
int i;
|
||||||
|
|
||||||
for(i = 0; p->elements[i]; i++)
|
for(i = 0; p->elements[i]; i++)
|
||||||
if(!a_strcmp(element_name, p->elements[i]))
|
if(!a_strcmp(element_name, p->elements[i])) {
|
||||||
{
|
(*p->on_element)(p, element_name, attribute_names,
|
||||||
for(j = 0; attribute_names[j]; j++);
|
attribute_values);
|
||||||
p->attribute_names[i] = p_new(char *, ++j);
|
|
||||||
p->attribute_values[i] = p_new(char *, j);
|
|
||||||
|
|
||||||
for(j = 0; attribute_names[j]; j++)
|
|
||||||
{
|
|
||||||
p->attribute_names[i][j] = a_strdup(attribute_names[j]);
|
|
||||||
p->attribute_values[i][j] = a_strdup(attribute_values[j]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(p->elements_sub && p->elements_sub[i])
|
|
||||||
{
|
|
||||||
buffer_add_xmlescaped(&p->text, p->elements_sub[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,20 +120,10 @@ markup_parse_text(GMarkupParseContext *context __attribute__ ((unused)),
|
||||||
/** Create a markup_parser_data_t structure with elements list.
|
/** Create a markup_parser_data_t structure with elements list.
|
||||||
* \param a pointer to an allocated markup_parser_data_t which must be wiped
|
* \param a pointer to an allocated markup_parser_data_t which must be wiped
|
||||||
* with markup_parser_data_wipe()
|
* with markup_parser_data_wipe()
|
||||||
* \param elements an array of elements to look for, NULL terminated
|
|
||||||
* \param elements_sub an optional array of values to substitude to elements, NULL
|
|
||||||
* terminated, or NULL if not needed
|
|
||||||
* \param nelem number of elements in the array (without NULL)
|
|
||||||
*/
|
*/
|
||||||
void
|
void markup_parser_data_init(markup_parser_data_t *p)
|
||||||
markup_parser_data_init(markup_parser_data_t *p, const char **elements,
|
|
||||||
const char **elements_sub, ssize_t nelem)
|
|
||||||
{
|
{
|
||||||
buffer_init(&p->text);
|
buffer_init(&p->text);
|
||||||
p->elements = elements;
|
|
||||||
p->elements_sub = elements_sub;
|
|
||||||
p->attribute_names = p_new(char **, nelem);
|
|
||||||
p->attribute_values = p_new(char **, nelem);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Wipe a markup_parser_data_t initialized with markup_parser_data_init.
|
/** Wipe a markup_parser_data_t initialized with markup_parser_data_init.
|
||||||
|
@ -156,23 +132,6 @@ markup_parser_data_init(markup_parser_data_t *p, const char **elements,
|
||||||
void
|
void
|
||||||
markup_parser_data_wipe(markup_parser_data_t *p)
|
markup_parser_data_wipe(markup_parser_data_t *p)
|
||||||
{
|
{
|
||||||
int i, j;
|
|
||||||
|
|
||||||
for(i = 0; p->elements[i]; i++)
|
|
||||||
if(p->attribute_names[i])
|
|
||||||
{
|
|
||||||
for(j = 0; p->attribute_names[i][j]; j++)
|
|
||||||
{
|
|
||||||
p_delete(&p->attribute_names[i][j]);
|
|
||||||
p_delete(&p->attribute_values[i][j]);
|
|
||||||
}
|
|
||||||
p_delete(&p->attribute_names[i]);
|
|
||||||
p_delete(&p->attribute_values[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
p_delete(&p->attribute_names);
|
|
||||||
p_delete(&p->attribute_values);
|
|
||||||
|
|
||||||
buffer_wipe(&p->text);
|
buffer_wipe(&p->text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,16 +24,20 @@
|
||||||
|
|
||||||
#include "common/buffer.h"
|
#include "common/buffer.h"
|
||||||
|
|
||||||
typedef struct
|
typedef struct markup_parser_data_t markup_parser_data_t;
|
||||||
|
|
||||||
|
typedef void (markup_on_elem_f)(markup_parser_data_t *, const char *,
|
||||||
|
const char **, const char **);
|
||||||
|
|
||||||
|
struct markup_parser_data_t
|
||||||
{
|
{
|
||||||
buffer_t text;
|
buffer_t text;
|
||||||
const char **elements;
|
const char * const *elements;
|
||||||
const char **elements_sub;
|
markup_on_elem_f *on_element;
|
||||||
char ***attribute_names;
|
void *priv;
|
||||||
char ***attribute_values;
|
};
|
||||||
} markup_parser_data_t;
|
|
||||||
|
|
||||||
void markup_parser_data_init(markup_parser_data_t *, const char **, const char **, ssize_t);
|
void markup_parser_data_init(markup_parser_data_t *);
|
||||||
void markup_parser_data_wipe(markup_parser_data_t *);
|
void markup_parser_data_wipe(markup_parser_data_t *);
|
||||||
bool markup_parse(markup_parser_data_t *data, const char *, ssize_t);
|
bool markup_parse(markup_parser_data_t *data, const char *, ssize_t);
|
||||||
|
|
||||||
|
|
|
@ -54,15 +54,27 @@ typedef struct
|
||||||
|
|
||||||
} taglist_data_t;
|
} taglist_data_t;
|
||||||
|
|
||||||
|
static void
|
||||||
|
tag_markup_on_elem(markup_parser_data_t *p, const char *elem,
|
||||||
|
const char **names, const char **values)
|
||||||
|
{
|
||||||
|
assert (!strcmp(elem, "title"));
|
||||||
|
buffer_add_xmlescaped(&p->text, p->priv);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static char *
|
static char *
|
||||||
tag_markup_parse(tag_t *t, const char *str, ssize_t len)
|
tag_markup_parse(tag_t *t, const char *str, ssize_t len)
|
||||||
{
|
{
|
||||||
const char *elements[] = { "title", NULL };
|
static char const * const elements[] = { "title", NULL };
|
||||||
const char *elements_sub[] = { t->name , NULL };
|
markup_parser_data_t p = {
|
||||||
markup_parser_data_t p;
|
.elements = elements,
|
||||||
|
.on_element = &tag_markup_on_elem,
|
||||||
|
.priv = t->name,
|
||||||
|
};
|
||||||
char *ret;
|
char *ret;
|
||||||
|
|
||||||
markup_parser_data_init(&p, elements, elements_sub, countof(elements));
|
markup_parser_data_init(&p);
|
||||||
|
|
||||||
if(markup_parse(&p, str, len))
|
if(markup_parse(&p, str, len))
|
||||||
ret = buffer_detach(&p.text);
|
ret = buffer_detach(&p.text);
|
||||||
|
|
|
@ -63,6 +63,31 @@ tasklist_isvisible(client_t *c, int screen, showclient_t show)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct tasklist_hook_data {
|
||||||
|
draw_context_t *ctx;
|
||||||
|
area_t *area;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
tasklist_markup_on_elem(markup_parser_data_t *p, const char *elem,
|
||||||
|
const char **names, const char **values)
|
||||||
|
{
|
||||||
|
struct tasklist_hook_data *data = p->priv;
|
||||||
|
draw_context_t *ctx = data->ctx;
|
||||||
|
|
||||||
|
assert (!strcmp(elem, "bg"));
|
||||||
|
for (; *names; names++, values++) {
|
||||||
|
if(!a_strcmp(*names, "color"))
|
||||||
|
{
|
||||||
|
xcolor_t bg_color;
|
||||||
|
xcolor_new(ctx->connection, ctx->phys_screen, *values, &bg_color);
|
||||||
|
draw_rectangle(ctx, *data->area, 1.0, true, bg_color);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
tasklist_draw(draw_context_t *ctx, int screen,
|
tasklist_draw(draw_context_t *ctx, int screen,
|
||||||
widget_node_t *w,
|
widget_node_t *w,
|
||||||
|
@ -72,11 +97,8 @@ tasklist_draw(draw_context_t *ctx, int screen,
|
||||||
tasklist_data_t *d = w->widget->data;
|
tasklist_data_t *d = w->widget->data;
|
||||||
area_t area;
|
area_t area;
|
||||||
char *text;
|
char *text;
|
||||||
int n = 0, i = 0, box_width = 0, icon_width = 0, box_width_rest = 0, j = 0;
|
int n = 0, i = 0, box_width = 0, icon_width = 0, box_width_rest = 0;
|
||||||
netwm_icon_t *icon;
|
netwm_icon_t *icon;
|
||||||
markup_parser_data_t p;
|
|
||||||
const char *elements[] = { "bg", NULL };
|
|
||||||
xcolor_t bg_color;
|
|
||||||
draw_image_t *image;
|
draw_image_t *image;
|
||||||
|
|
||||||
if(used >= ctx->width)
|
if(used >= ctx->width)
|
||||||
|
@ -114,6 +136,14 @@ tasklist_draw(draw_context_t *ctx, int screen,
|
||||||
|
|
||||||
if(d->show_icons)
|
if(d->show_icons)
|
||||||
{
|
{
|
||||||
|
static char const * const elements[] = { "bg", NULL };
|
||||||
|
struct tasklist_hook_data data = { .ctx = ctx, .area = &area };
|
||||||
|
markup_parser_data_t p = {
|
||||||
|
.elements = elements,
|
||||||
|
.on_element = &tasklist_markup_on_elem,
|
||||||
|
.priv = &data,
|
||||||
|
};
|
||||||
|
|
||||||
/* draw a background for icons */
|
/* draw a background for icons */
|
||||||
area.x = w->area.x + box_width * i;
|
area.x = w->area.x + box_width * i;
|
||||||
area.y = w->area.y;
|
area.y = w->area.y;
|
||||||
|
@ -122,15 +152,8 @@ tasklist_draw(draw_context_t *ctx, int screen,
|
||||||
|
|
||||||
/* Actually look for the proper background color, since
|
/* Actually look for the proper background color, since
|
||||||
* otherwise the background statusbar color is used instead */
|
* otherwise the background statusbar color is used instead */
|
||||||
markup_parser_data_init(&p, elements, NULL, countof(elements));
|
markup_parser_data_init(&p);
|
||||||
if(markup_parse(&p, text, a_strlen(text)) && p.attribute_names[0])
|
markup_parse(&p, text, a_strlen(text));
|
||||||
for(j = 0; p.attribute_names[0][j]; j++)
|
|
||||||
if(!a_strcmp(p.attribute_names[0][j], "color"))
|
|
||||||
{
|
|
||||||
xcolor_new(ctx->connection, ctx->phys_screen, p.attribute_values[0][j], &bg_color);
|
|
||||||
draw_rectangle(ctx, area, 1.0, true, bg_color);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
markup_parser_data_wipe(&p);
|
markup_parser_data_wipe(&p);
|
||||||
|
|
||||||
if((image = draw_image_new(c->icon_path)))
|
if((image = draw_image_new(c->icon_path)))
|
||||||
|
|
Loading…
Reference in New Issue