forked from external/yambar
Add support binding on-click handlers to other buttons than LEFT
One can now bind the left/middle/right mouse buttons to on-click. In fact, you can have all three buttons bound to different handlers for the same particle. The new syntax is on-click: left: <command> middle: <command> right: <command> Leaving one out is the same thing as not mapping it at all. Furthermore, on-click: <command> is still valid, and is a shorthand for on-click: left: <commsnd>
This commit is contained in:
parent
af163d3f77
commit
c79ffbe057
15 changed files with 163 additions and 90 deletions
|
@ -272,26 +272,25 @@ static void
|
|||
wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
|
||||
uint32_t serial, uint32_t time, uint32_t button, uint32_t state)
|
||||
{
|
||||
if (state != WL_POINTER_BUTTON_STATE_PRESSED)
|
||||
return;
|
||||
|
||||
struct seat *seat = data;
|
||||
struct wayland_backend *backend = seat->backend;
|
||||
|
||||
backend->active_seat = seat;
|
||||
if (state == WL_POINTER_BUTTON_STATE_PRESSED)
|
||||
backend->active_seat = seat;
|
||||
else {
|
||||
enum mouse_button btn;
|
||||
|
||||
enum mouse_button btn;
|
||||
switch (button) {
|
||||
case BTN_LEFT: btn = MOUSE_BTN_LEFT; break;
|
||||
case BTN_MIDDLE: btn = MOUSE_BTN_MIDDLE; break;
|
||||
case BTN_RIGHT: btn = MOUSE_BTN_RIGHT; break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
switch (button) {
|
||||
case BTN_LEFT: btn = MOUSE_BTN_LEFT; break;
|
||||
case BTN_MIDDLE: btn = MOUSE_BTN_MIDDLE; break;
|
||||
case BTN_RIGHT: btn = MOUSE_BTN_RIGHT; break;
|
||||
default:
|
||||
return;
|
||||
backend->bar_on_mouse(
|
||||
backend->bar, ON_MOUSE_CLICK, btn, seat->pointer.x, seat->pointer.y);
|
||||
}
|
||||
|
||||
backend->bar_on_mouse(
|
||||
backend->bar, ON_MOUSE_CLICK, btn, seat->pointer.x, seat->pointer.y);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
|
@ -152,6 +152,24 @@ conf_verify_dict(keychain_t *chain, const struct yml_node *node,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
conf_verify_on_click(keychain_t *chain, const struct yml_node *node)
|
||||
{
|
||||
/* on-click: <command> */
|
||||
const char *s = yml_value_as_string(node);
|
||||
if (s != NULL)
|
||||
return true;
|
||||
|
||||
static const struct attr_info info[] = {
|
||||
{"left", false, &conf_verify_string},
|
||||
{"middle", false, &conf_verify_string},
|
||||
{"right", false, &conf_verify_string},
|
||||
{NULL, false, NULL},
|
||||
};
|
||||
|
||||
return conf_verify_dict(chain, node, info);
|
||||
}
|
||||
|
||||
bool
|
||||
conf_verify_color(keychain_t *chain, const struct yml_node *node)
|
||||
{
|
||||
|
|
|
@ -40,6 +40,7 @@ bool conf_verify_list(keychain_t *chain, const struct yml_node *node,
|
|||
bool conf_verify_dict(keychain_t *chain, const struct yml_node *node,
|
||||
const struct attr_info info[]); /* NULL-terminated list */
|
||||
|
||||
bool conf_verify_on_click(keychain_t *chain, const struct yml_node *node);
|
||||
bool conf_verify_color(keychain_t *chain, const struct yml_node *node);
|
||||
bool conf_verify_font(keychain_t *chain, const struct yml_node *node);
|
||||
|
||||
|
|
31
config.c
31
config.c
|
@ -139,8 +139,33 @@ conf_to_particle(const struct yml_node *node, struct conf_inherit inherited)
|
|||
int right = margin != NULL ? yml_value_as_int(margin) :
|
||||
right_margin != NULL ? yml_value_as_int(right_margin) : 0;
|
||||
|
||||
const char *on_click_template
|
||||
= on_click != NULL ? yml_value_as_string(on_click) : NULL;
|
||||
const char *on_click_templates[MOUSE_BTN_COUNT] = {NULL};
|
||||
if (on_click != NULL) {
|
||||
const char *legacy = yml_value_as_string(on_click);
|
||||
|
||||
if (legacy != NULL)
|
||||
on_click_templates[MOUSE_BTN_LEFT] = legacy;
|
||||
|
||||
if (yml_is_dict(on_click)) {
|
||||
for (struct yml_dict_iter it = yml_dict_iter(on_click);
|
||||
it.key != NULL;
|
||||
yml_dict_next(&it))
|
||||
{
|
||||
const char *key = yml_value_as_string(it.key);
|
||||
const char *template = yml_value_as_string(it.value);
|
||||
|
||||
if (strcmp(key, "left") == 0)
|
||||
on_click_templates[MOUSE_BTN_LEFT] = template;
|
||||
else if (strcmp(key, "middle") == 0)
|
||||
on_click_templates[MOUSE_BTN_MIDDLE] = template;
|
||||
else if (strcmp(key, "right") == 0)
|
||||
on_click_templates[MOUSE_BTN_RIGHT] = template;
|
||||
else
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct deco *deco = deco_node != NULL ? conf_to_deco(deco_node) : NULL;
|
||||
|
||||
/*
|
||||
|
@ -159,7 +184,7 @@ conf_to_particle(const struct yml_node *node, struct conf_inherit inherited)
|
|||
|
||||
/* Instantiate base/common particle */
|
||||
struct particle *common = particle_common_new(
|
||||
left, right, on_click_template, font, foreground, deco);
|
||||
left, right, on_click_templates, font, foreground, deco);
|
||||
|
||||
const struct particle_iface *iface = plugin_load_particle(type);
|
||||
|
||||
|
|
63
particle.c
63
particle.c
|
@ -22,31 +22,41 @@ particle_default_destroy(struct particle *particle)
|
|||
if (particle->deco != NULL)
|
||||
particle->deco->destroy(particle->deco);
|
||||
fcft_destroy(particle->font);
|
||||
free(particle->on_click_template);
|
||||
for (size_t i = 0; i < MOUSE_BTN_COUNT; i++)
|
||||
free(particle->on_click_templates[i]);
|
||||
free(particle);
|
||||
}
|
||||
|
||||
struct particle *
|
||||
particle_common_new(int left_margin, int right_margin,
|
||||
const char *on_click_template,
|
||||
const char **on_click_templates,
|
||||
struct fcft_font *font, pixman_color_t foreground,
|
||||
struct deco *deco)
|
||||
{
|
||||
struct particle *p = calloc(1, sizeof(*p));
|
||||
p->left_margin = left_margin;
|
||||
p->right_margin = right_margin;
|
||||
p->on_click_template =
|
||||
on_click_template != NULL ? strdup(on_click_template) : NULL;
|
||||
p->foreground = foreground;
|
||||
p->font = font;
|
||||
p->deco = deco;
|
||||
|
||||
if (on_click_templates != NULL) {
|
||||
for (size_t i = 0; i < MOUSE_BTN_COUNT; i++) {
|
||||
if (on_click_templates[i] != NULL) {
|
||||
p->have_on_click_template = true;
|
||||
p->on_click_templates[i] = strdup(on_click_templates[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
void
|
||||
exposable_default_destroy(struct exposable *exposable)
|
||||
{
|
||||
free(exposable->on_click);
|
||||
for (size_t i = 0; i < MOUSE_BTN_COUNT; i++)
|
||||
free(exposable->on_click[i]);
|
||||
free(exposable);
|
||||
}
|
||||
|
||||
|
@ -144,19 +154,32 @@ exposable_default_on_mouse(struct exposable *exposable, struct bar *bar,
|
|||
enum mouse_event event, enum mouse_button btn,
|
||||
int x, int y)
|
||||
{
|
||||
#if defined(LOG_ENABLE_DBG) && LOG_ENABLE_DBG
|
||||
static const char *button_name[] = {
|
||||
[MOUSE_BTN_NONE] = "none",
|
||||
[MOUSE_BTN_LEFT] = "left",
|
||||
[MOUSE_BTN_MIDDLE] = "middle",
|
||||
[MOUSE_BTN_RIGHT] = "right",
|
||||
[MOUSE_BTN_COUNT] = "count",
|
||||
};
|
||||
LOG_DBG("on_mouse: exposable=%p, event=%s, btn=%s, x=%d, y=%d (on-click=%s)",
|
||||
exposable, event == ON_MOUSE_MOTION ? "motion" : "click",
|
||||
btn == MOUSE_BTN_NONE ? "none" : btn == MOUSE_BTN_LEFT ? "left" : btn == MOUSE_BTN_MIDDLE ? "middle" : "right",
|
||||
x, y, exposable->on_click);
|
||||
button_name[btn], x, y, exposable->on_click[btn]);
|
||||
#endif
|
||||
|
||||
/* If we have a handler, change cursor to a hand */
|
||||
bar->set_cursor(bar, exposable->on_click == NULL ? "left_ptr" : "hand2");
|
||||
const char *cursor =
|
||||
(exposable->particle != NULL &&
|
||||
exposable->particle->have_on_click_template)
|
||||
? "hand2"
|
||||
: "left_ptr";
|
||||
bar->set_cursor(bar, cursor);
|
||||
|
||||
/* If this is a mouse click, and we have a handler, execute it */
|
||||
if (exposable->on_click != NULL && event == ON_MOUSE_CLICK) {
|
||||
if (exposable->on_click[btn] != NULL && event == ON_MOUSE_CLICK) {
|
||||
/* Need a writeable copy, whose scope *we* control */
|
||||
char *cmd = strdup(exposable->on_click);
|
||||
LOG_DBG("cmd = \"%s\"", exposable->on_click);
|
||||
char *cmd = strdup(exposable->on_click[btn]);
|
||||
LOG_DBG("cmd = \"%s\"", exposable->on_click[btn]);
|
||||
|
||||
char **argv;
|
||||
if (!tokenize_cmdline(cmd, &argv)) {
|
||||
|
@ -174,15 +197,15 @@ exposable_default_on_mouse(struct exposable *exposable, struct bar *bar,
|
|||
|
||||
int wstatus;
|
||||
if (waitpid(pid, &wstatus, 0) == -1)
|
||||
LOG_ERRNO("%s: failed to wait for on_click handler", exposable->on_click);
|
||||
LOG_ERRNO("%s: failed to wait for on_click handler", exposable->on_click[btn]);
|
||||
|
||||
if (WIFEXITED(wstatus)) {
|
||||
if (WEXITSTATUS(wstatus) != 0)
|
||||
LOG_ERRNO_P("%s: failed to execute", WEXITSTATUS(wstatus), exposable->on_click);
|
||||
LOG_ERRNO_P("%s: failed to execute", WEXITSTATUS(wstatus), exposable->on_click[btn]);
|
||||
} else
|
||||
LOG_ERR("%s: did not exit normally", exposable->on_click);
|
||||
LOG_ERR("%s: did not exit normally", exposable->on_click[btn]);
|
||||
|
||||
LOG_DBG("%s: launched", exposable->on_click);
|
||||
LOG_DBG("%s: launched", exposable->on_click[btn]);
|
||||
} else {
|
||||
/*
|
||||
* Use a pipe with O_CLOEXEC to communicate exec() failure
|
||||
|
@ -283,11 +306,17 @@ exposable_default_on_mouse(struct exposable *exposable, struct bar *bar,
|
|||
}
|
||||
|
||||
struct exposable *
|
||||
exposable_common_new(const struct particle *particle, const char *on_click)
|
||||
exposable_common_new(const struct particle *particle, const struct tag_set *tags)
|
||||
{
|
||||
struct exposable *exposable = calloc(1, sizeof(*exposable));
|
||||
exposable->particle = particle;
|
||||
exposable->on_click = on_click != NULL ? strdup(on_click) : NULL;
|
||||
|
||||
if (particle != NULL && particle->have_on_click_template) {
|
||||
tags_expand_templates(
|
||||
exposable->on_click,
|
||||
(const char **)particle->on_click_templates,
|
||||
MOUSE_BTN_COUNT, tags);
|
||||
}
|
||||
exposable->destroy = &exposable_default_destroy;
|
||||
exposable->on_mouse = &exposable_default_on_mouse;
|
||||
return exposable;
|
||||
|
|
47
particle.h
47
particle.h
|
@ -7,23 +7,6 @@
|
|||
#include "decoration.h"
|
||||
#include "tag.h"
|
||||
|
||||
struct bar;
|
||||
|
||||
struct particle {
|
||||
void *private;
|
||||
|
||||
int left_margin, right_margin;
|
||||
char *on_click_template;
|
||||
|
||||
pixman_color_t foreground;
|
||||
struct fcft_font *font;
|
||||
struct deco *deco;
|
||||
|
||||
void (*destroy)(struct particle *particle);
|
||||
struct exposable *(*instantiate)(const struct particle *particle,
|
||||
const struct tag_set *tags);
|
||||
};
|
||||
|
||||
enum mouse_event {
|
||||
ON_MOUSE_MOTION,
|
||||
ON_MOUSE_CLICK,
|
||||
|
@ -34,14 +17,36 @@ enum mouse_button {
|
|||
MOUSE_BTN_LEFT,
|
||||
MOUSE_BTN_MIDDLE,
|
||||
MOUSE_BTN_RIGHT,
|
||||
|
||||
MOUSE_BTN_COUNT,
|
||||
};
|
||||
|
||||
struct bar;
|
||||
|
||||
struct particle {
|
||||
void *private;
|
||||
|
||||
int left_margin, right_margin;
|
||||
|
||||
bool have_on_click_template;
|
||||
char *on_click_templates[MOUSE_BTN_COUNT];
|
||||
|
||||
pixman_color_t foreground;
|
||||
struct fcft_font *font;
|
||||
struct deco *deco;
|
||||
|
||||
void (*destroy)(struct particle *particle);
|
||||
struct exposable *(*instantiate)(const struct particle *particle,
|
||||
const struct tag_set *tags);
|
||||
};
|
||||
|
||||
|
||||
struct exposable {
|
||||
const struct particle *particle;
|
||||
void *private;
|
||||
|
||||
int width; /* Should be set by begin_expose(), at latest */
|
||||
char *on_click;
|
||||
char *on_click[MOUSE_BTN_COUNT];
|
||||
|
||||
void (*destroy)(struct exposable *exposable);
|
||||
int (*begin_expose)(struct exposable *exposable);
|
||||
|
@ -53,13 +58,13 @@ struct exposable {
|
|||
};
|
||||
|
||||
struct particle *particle_common_new(
|
||||
int left_margin, int right_margin, const char *on_click_template,
|
||||
int left_margin, int right_margin, const char *on_click_templates[],
|
||||
struct fcft_font *font, pixman_color_t foreground, struct deco *deco);
|
||||
|
||||
void particle_default_destroy(struct particle *particle);
|
||||
|
||||
struct exposable *exposable_common_new(
|
||||
const struct particle *particle, const char *on_click);
|
||||
const struct particle *particle, const struct tag_set *tags);
|
||||
void exposable_default_destroy(struct exposable *exposable);
|
||||
void exposable_render_deco(
|
||||
const struct exposable *exposable, pixman_image_t *pix, int x, int y, int height);
|
||||
|
@ -73,7 +78,7 @@ void exposable_default_on_mouse(
|
|||
{"margin", false, &conf_verify_int}, \
|
||||
{"left-margin", false, &conf_verify_int}, \
|
||||
{"right-margin", false, &conf_verify_int}, \
|
||||
{"on-click", false, &conf_verify_string}, \
|
||||
{"on-click", false, &conf_verify_on_click}, \
|
||||
{"font", false, &conf_verify_font}, \
|
||||
{"foreground", false, &conf_verify_color}, \
|
||||
{"deco", false, &conf_verify_decoration}, \
|
||||
|
|
|
@ -72,7 +72,7 @@ on_mouse(struct exposable *exposable, struct bar *bar,
|
|||
//const struct particle *p = exposable->particle;
|
||||
const struct private *e = exposable->private;
|
||||
|
||||
if (exposable->on_click != NULL) {
|
||||
if (exposable->on_click[btn] != NULL) {
|
||||
exposable_default_on_mouse(exposable, bar, event, btn, x, y);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -22,13 +22,9 @@ expose(const struct exposable *exposable, pixman_image_t *pix, int x, int y, int
|
|||
static struct exposable *
|
||||
instantiate(const struct particle *particle, const struct tag_set *tags)
|
||||
{
|
||||
char *on_click = tags_expand_template(particle->on_click_template, tags);
|
||||
|
||||
struct exposable *exposable = exposable_common_new(particle, on_click);
|
||||
struct exposable *exposable = exposable_common_new(particle, tags);
|
||||
exposable->begin_expose = &begin_expose;
|
||||
exposable->expose = &expose;
|
||||
|
||||
free(on_click);
|
||||
return exposable;
|
||||
}
|
||||
|
||||
|
|
|
@ -80,7 +80,7 @@ on_mouse(struct exposable *exposable, struct bar *bar,
|
|||
const struct particle *p = exposable->particle;
|
||||
const struct eprivate *e = exposable->private;
|
||||
|
||||
if (exposable->on_click != NULL) {
|
||||
if (exposable->on_click[btn] != NULL) {
|
||||
/* We have our own handler */
|
||||
exposable_default_on_mouse(exposable, bar, event, btn, x, y);
|
||||
return;
|
||||
|
@ -121,16 +121,12 @@ instantiate(const struct particle *particle, const struct tag_set *tags)
|
|||
assert(e->exposables[i] != NULL);
|
||||
}
|
||||
|
||||
char *on_click = tags_expand_template(particle->on_click_template, tags);
|
||||
|
||||
struct exposable *exposable = exposable_common_new(particle, on_click);
|
||||
struct exposable *exposable = exposable_common_new(particle, tags);
|
||||
exposable->private = e;
|
||||
exposable->destroy = &exposable_destroy;
|
||||
exposable->begin_expose = &begin_expose;
|
||||
exposable->expose = &expose;
|
||||
exposable->on_mouse = &on_mouse;
|
||||
|
||||
free(on_click);
|
||||
return exposable;
|
||||
}
|
||||
|
||||
|
|
|
@ -66,7 +66,7 @@ on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event,
|
|||
const struct particle *p = exposable->particle;
|
||||
const struct eprivate *e = exposable->private;
|
||||
|
||||
if (exposable->on_click != NULL) {
|
||||
if (exposable->on_click[btn] != NULL) {
|
||||
/* We have our own handler */
|
||||
exposable_default_on_mouse(exposable, bar, event, btn, x, y);
|
||||
return;
|
||||
|
@ -119,15 +119,12 @@ instantiate(const struct particle *particle, const struct tag_set *tags)
|
|||
|
||||
assert(e->exposable != NULL);
|
||||
|
||||
char *on_click = tags_expand_template(particle->on_click_template, tags);
|
||||
struct exposable *exposable = exposable_common_new(particle, on_click);
|
||||
struct exposable *exposable = exposable_common_new(particle, tags);
|
||||
exposable->private = e;
|
||||
exposable->destroy = &exposable_destroy;
|
||||
exposable->begin_expose = &begin_expose;
|
||||
exposable->expose = &expose;
|
||||
exposable->on_mouse = &on_mouse;
|
||||
|
||||
free(on_click);
|
||||
return exposable;
|
||||
}
|
||||
|
||||
|
|
|
@ -148,7 +148,9 @@ on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event,
|
|||
}
|
||||
|
||||
/* Remember the original handler, so that we can restore it */
|
||||
char *original = exposable->on_click;
|
||||
char *original[MOUSE_BTN_COUNT];
|
||||
for (size_t i = 0; i < MOUSE_BTN_COUNT; i++)
|
||||
original[i] = exposable->on_click[i];
|
||||
|
||||
if (event == ON_MOUSE_CLICK) {
|
||||
long where = clickable_width > 0
|
||||
|
@ -160,7 +162,9 @@ on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event,
|
|||
.count = 1,
|
||||
};
|
||||
|
||||
exposable->on_click = tags_expand_template(exposable->on_click, &tags);
|
||||
tags_expand_templates(
|
||||
exposable->on_click, (const char **)exposable->on_click,
|
||||
MOUSE_BTN_COUNT, &tags);
|
||||
tag_set_destroy(&tags);
|
||||
}
|
||||
|
||||
|
@ -169,8 +173,10 @@ on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event,
|
|||
|
||||
if (event == ON_MOUSE_CLICK) {
|
||||
/* Reset handler string */
|
||||
free(exposable->on_click);
|
||||
exposable->on_click = original;
|
||||
for (size_t i = 0; i < MOUSE_BTN_COUNT; i++) {
|
||||
free(exposable->on_click[i]);
|
||||
exposable->on_click[i] = original[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -213,10 +219,7 @@ instantiate(const struct particle *particle, const struct tag_set *tags)
|
|||
for (size_t i = 0; i < epriv->count; i++)
|
||||
assert(epriv->exposables[i] != NULL);
|
||||
|
||||
char *on_click = tags_expand_template(particle->on_click_template, tags);
|
||||
|
||||
struct exposable *exposable = exposable_common_new(particle, on_click);
|
||||
free(on_click);
|
||||
struct exposable *exposable = exposable_common_new(particle, tags);
|
||||
|
||||
exposable->private = epriv;
|
||||
exposable->destroy = &exposable_destroy;
|
||||
|
|
|
@ -62,7 +62,7 @@ on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event,
|
|||
const struct particle *p = exposable->particle;
|
||||
const struct eprivate *e = exposable->private;
|
||||
|
||||
if (exposable->on_click != NULL) {
|
||||
if (exposable->on_click[btn] != NULL) {
|
||||
/* We have our own handler */
|
||||
exposable_default_on_mouse(exposable, bar, event, btn, x, y);
|
||||
return;
|
||||
|
@ -146,15 +146,12 @@ instantiate(const struct particle *particle, const struct tag_set *tags)
|
|||
e->exposable = pp->instantiate(pp, tags);
|
||||
assert(e->exposable != NULL);
|
||||
|
||||
char *on_click = tags_expand_template(particle->on_click_template, tags);
|
||||
struct exposable *exposable = exposable_common_new(particle, on_click);
|
||||
struct exposable *exposable = exposable_common_new(particle, tags);
|
||||
exposable->private = e;
|
||||
exposable->destroy = &exposable_destroy;
|
||||
exposable->begin_expose = &begin_expose;
|
||||
exposable->expose = &expose;
|
||||
exposable->on_mouse = &on_mouse;
|
||||
|
||||
free(on_click);
|
||||
return exposable;
|
||||
}
|
||||
|
||||
|
|
|
@ -269,15 +269,11 @@ instantiate(const struct particle *particle, const struct tag_set *tags)
|
|||
}
|
||||
}
|
||||
|
||||
char *on_click = tags_expand_template(particle->on_click_template, tags);
|
||||
|
||||
struct exposable *exposable = exposable_common_new(particle, on_click);
|
||||
struct exposable *exposable = exposable_common_new(particle, tags);
|
||||
exposable->private = e;
|
||||
exposable->destroy = &exposable_destroy;
|
||||
exposable->begin_expose = &begin_expose;
|
||||
exposable->expose = &expose;
|
||||
|
||||
free(on_click);
|
||||
return exposable;
|
||||
}
|
||||
|
||||
|
|
8
tag.c
8
tag.c
|
@ -533,3 +533,11 @@ tags_expand_template(const char *template, const struct tag_set *tags)
|
|||
|
||||
return formatted.s;
|
||||
}
|
||||
|
||||
void
|
||||
tags_expand_templates(char *expanded[], const char *template[], size_t nmemb,
|
||||
const struct tag_set *tags)
|
||||
{
|
||||
for (size_t i = 0; i < nmemb; i++)
|
||||
expanded[i] = tags_expand_template(template[i], tags);
|
||||
}
|
||||
|
|
3
tag.h
3
tag.h
|
@ -50,3 +50,6 @@ void tag_set_destroy(struct tag_set *set);
|
|||
|
||||
/* Utility functions */
|
||||
char *tags_expand_template(const char *template, const struct tag_set *tags);
|
||||
void tags_expand_templates(
|
||||
char *expanded[], const char *template[], size_t nmemb,
|
||||
const struct tag_set *tags);
|
||||
|
|
Loading…
Add table
Reference in a new issue