Modular status panel for X11 and Wayland, inspired by https://github.com/jaagr/polybar
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
251 lines
6.6 KiB
251 lines
6.6 KiB
#include <stdlib.h> |
|
#include <string.h> |
|
#include <assert.h> |
|
|
|
#define LOG_MODULE "map" |
|
#include "../log.h" |
|
#include "../config.h" |
|
#include "../config-verify.h" |
|
#include "../particle.h" |
|
#include "../plugin.h" |
|
|
|
struct particle_map { |
|
const char *tag_value; |
|
struct particle *particle; |
|
}; |
|
|
|
struct private { |
|
char *tag; |
|
struct particle *default_particle; |
|
struct particle_map *map; |
|
size_t count; |
|
}; |
|
|
|
struct eprivate { |
|
struct exposable *exposable; |
|
}; |
|
|
|
static void |
|
exposable_destroy(struct exposable *exposable) |
|
{ |
|
struct eprivate *e = exposable->private; |
|
e->exposable->destroy(e->exposable); |
|
|
|
free(e); |
|
exposable_default_destroy(exposable); |
|
} |
|
|
|
static int |
|
begin_expose(struct exposable *exposable) |
|
{ |
|
struct eprivate *e = exposable->private; |
|
|
|
exposable->width = ( |
|
exposable->particle->left_margin + |
|
e->exposable->begin_expose(e->exposable) + |
|
exposable->particle->right_margin); |
|
|
|
return exposable->width; |
|
} |
|
|
|
static void |
|
expose(const struct exposable *exposable, pixman_image_t *pix, int x, int y, int height) |
|
{ |
|
struct eprivate *e = exposable->private; |
|
|
|
exposable_render_deco(exposable, pix, x, y, height); |
|
e->exposable->expose( |
|
e->exposable, pix, x + exposable->particle->left_margin, y, height); |
|
} |
|
|
|
static void |
|
on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, |
|
int x, int y) |
|
{ |
|
const struct particle *p = exposable->particle; |
|
const struct eprivate *e = exposable->private; |
|
|
|
if (exposable->on_click != NULL) { |
|
/* We have our own handler */ |
|
exposable_default_on_mouse(exposable, bar, event, x, y); |
|
return; |
|
} |
|
|
|
int px = p->left_margin; |
|
if (x >= px && x < px + e->exposable->width) { |
|
if (e->exposable->on_mouse != NULL) |
|
e->exposable->on_mouse(e->exposable, bar, event, x - px, y); |
|
return; |
|
} |
|
|
|
/* In the left- or right margin */ |
|
exposable_default_on_mouse(exposable, bar, event, x, y); |
|
} |
|
|
|
static struct exposable * |
|
instantiate(const struct particle *particle, const struct tag_set *tags) |
|
{ |
|
const struct private *p = particle->private; |
|
const struct tag *tag = tag_for_name(tags, p->tag); |
|
assert(tag != NULL || p->default_particle != NULL); |
|
|
|
if (tag == NULL) |
|
return p->default_particle->instantiate(p->default_particle, tags); |
|
|
|
|
|
const char *tag_value = tag->as_string(tag); |
|
struct particle *pp = NULL; |
|
|
|
for (size_t i = 0; i < p->count; i++) { |
|
const struct particle_map *e = &p->map[i]; |
|
|
|
if (strcmp(e->tag_value, tag_value) != 0) |
|
continue; |
|
|
|
pp = e->particle; |
|
break; |
|
} |
|
|
|
if (pp == NULL) { |
|
assert(p->default_particle != NULL); |
|
pp = p->default_particle; |
|
} |
|
|
|
struct eprivate *e = calloc(1, sizeof(*e)); |
|
e->exposable = pp->instantiate(pp, tags); |
|
|
|
char *on_click = tags_expand_template(particle->on_click_template, tags); |
|
struct exposable *exposable = exposable_common_new(particle, on_click); |
|
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; |
|
} |
|
|
|
static void |
|
particle_destroy(struct particle *particle) |
|
{ |
|
struct private *p = particle->private; |
|
|
|
if (p->default_particle != NULL) |
|
p->default_particle->destroy(p->default_particle); |
|
|
|
for (size_t i = 0; i < p->count; i++) { |
|
struct particle *pp = p->map[i].particle; |
|
pp->destroy(pp); |
|
free((char *)p->map[i].tag_value); |
|
} |
|
|
|
free(p->map); |
|
free(p->tag); |
|
free(p); |
|
particle_default_destroy(particle); |
|
} |
|
|
|
static struct particle * |
|
map_new(struct particle *common, const char *tag, |
|
const struct particle_map particle_map[], size_t count, |
|
struct particle *default_particle) |
|
{ |
|
struct private *priv = calloc(1, sizeof(*priv)); |
|
priv->tag = strdup(tag); |
|
priv->default_particle = default_particle; |
|
priv->count = count; |
|
priv->map = malloc(count * sizeof(priv->map[0])); |
|
|
|
for (size_t i = 0; i < count; i++) { |
|
priv->map[i].tag_value = strdup(particle_map[i].tag_value); |
|
priv->map[i].particle = particle_map[i].particle; |
|
} |
|
|
|
common->private = priv; |
|
common->destroy = &particle_destroy; |
|
common->instantiate = &instantiate; |
|
return common; |
|
} |
|
|
|
static bool |
|
verify_map_values(keychain_t *chain, const struct yml_node *node) |
|
{ |
|
if (!yml_is_dict(node)) { |
|
LOG_ERR( |
|
"%s: must be a dictionary of workspace-name: particle mappings", |
|
conf_err_prefix(chain, node)); |
|
return false; |
|
} |
|
|
|
for (struct yml_dict_iter it = yml_dict_iter(node); |
|
it.key != NULL; |
|
yml_dict_next(&it)) |
|
{ |
|
const char *key = yml_value_as_string(it.key); |
|
if (key == NULL) { |
|
LOG_ERR("%s: key must be a string", conf_err_prefix(chain, it.key)); |
|
return false; |
|
} |
|
|
|
if (!conf_verify_particle(chain_push(chain, key), it.value)) |
|
return false; |
|
|
|
chain_pop(chain); |
|
} |
|
|
|
return true; |
|
} |
|
|
|
static struct particle * |
|
from_conf(const struct yml_node *node, struct particle *common) |
|
{ |
|
const struct yml_node *tag = yml_get_value(node, "tag"); |
|
const struct yml_node *values = yml_get_value(node, "values"); |
|
const struct yml_node *def = yml_get_value(node, "default"); |
|
|
|
struct particle_map particle_map[yml_dict_length(values)]; |
|
|
|
struct conf_inherit inherited = { |
|
.font = common->font, |
|
.foreground = common->foreground |
|
}; |
|
|
|
size_t idx = 0; |
|
for (struct yml_dict_iter it = yml_dict_iter(values); |
|
it.key != NULL; |
|
yml_dict_next(&it), idx++) |
|
{ |
|
particle_map[idx].tag_value = yml_value_as_string(it.key); |
|
particle_map[idx].particle = conf_to_particle(it.value, inherited); |
|
} |
|
|
|
struct particle *default_particle = def != NULL |
|
? conf_to_particle(def, inherited) : NULL; |
|
|
|
return map_new( |
|
common, yml_value_as_string(tag), particle_map, yml_dict_length(values), |
|
default_particle); |
|
} |
|
|
|
static bool |
|
verify_conf(keychain_t *chain, const struct yml_node *node) |
|
{ |
|
static const struct attr_info attrs[] = { |
|
{"tag", true, &conf_verify_string}, |
|
{"values", true, &verify_map_values}, |
|
{"default", false, &conf_verify_particle}, |
|
PARTICLE_COMMON_ATTRS, |
|
}; |
|
|
|
return conf_verify_dict(chain, node, attrs); |
|
} |
|
|
|
const struct particle_iface particle_map_iface = { |
|
.verify_conf = &verify_conf, |
|
.from_conf = &from_conf, |
|
}; |
|
|
|
#if defined(CORE_PLUGINS_AS_SHARED_LIBRARIES) |
|
extern const struct particle_iface iface __attribute__((weak, alias("particle_map_iface"))); |
|
#endif
|
|
|