yambar/particle.c
Daniel Eklöf 620017860e particle: handle ON_MOUSE_CLICK
If a particle has an on-click handler, execute it when we receive an
ON_MOUSE_CLICK event.

This is done by first tokenizing the command string. We currently
handle one level of quotes, but no escape characters.

Then, fork(). Main process waits for child to finish. Child daemonizes
and then execvp() the tokenized argument vector.
2018-12-29 16:24:22 +01:00

117 lines
3.4 KiB
C

#include "particle.h"
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <sys/wait.h>
#define LOG_MODULE "particle"
#define LOG_ENABLE_DBG 0
#include "log.h"
#include "bar.h"
void
particle_default_destroy(struct particle *particle)
{
if (particle->deco != NULL)
particle->deco->destroy(particle->deco);
free(particle->on_click_template);
free(particle);
}
struct particle *
particle_common_new(int left_margin, int right_margin,
const char *on_click_template)
{
struct particle *p = malloc(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->deco = NULL;
return p;
}
struct exposable *
exposable_common_new(const struct particle *particle, const char *on_click)
{
struct exposable *exposable = malloc(sizeof(*exposable));
exposable->particle = particle;
exposable->private = NULL;
exposable->width = 0;
exposable->on_click = on_click != NULL ? strdup(on_click) : NULL;
exposable->destroy = &exposable_default_destroy;
exposable->on_mouse = &exposable_default_on_mouse;
exposable->begin_expose = NULL;
exposable->expose = NULL;
return exposable;
}
void
exposable_default_destroy(struct exposable *exposable)
{
free(exposable->on_click);
free(exposable);
}
void
exposable_default_on_mouse(struct exposable *exposable, struct bar *bar,
enum mouse_event event, int x, int y)
{
LOG_DBG("on_mouse: exposable=%p, event=%s, x=%d, y=%d", exposable,
event == ON_MOUSE_MOTION ? "motion" : "click", x, y);
/* If we have a handler, change cursor to a hand */
bar->set_cursor(bar, exposable->on_click == NULL ? "left_ptr" : "hand2");
/* If this is a mouse click, and we have a handler, execute it */
if (exposable->on_click != NULL && event == ON_MOUSE_CLICK) {
/* Need a writeable copy, whose scope *we* control */
char *cmd = strdup(exposable->on_click);
const char *end = cmd + strlen(cmd);
char *argv[1024];
size_t tokens = 0;
/* Tokenize the command string */
for (char *ctx, *tok = strtok_r(cmd, " ", &ctx);
tok != NULL;
/*tok = strtok_r(NULL, " ", &ctx)*/)
{
argv[tokens++] = tok;
/* Is the beginning of the next token a quote? */
bool next_is_quoted = &tok[strlen(tok) + 1] < end &&
tok[strlen(tok) + 1] == '"';
tok = strtok_r(NULL, next_is_quoted ? "\"" : " ", &ctx);
}
/* NULL-terminate list (for execvp) */
argv[tokens] = NULL;
pid_t pid = fork();
if (pid == -1)
LOG_ERRNO("failed to run on_click handler (fork)");
else if (pid > 0) {
/* Parent */
free(cmd);
if (waitpid(pid, NULL, 0) == -1)
LOG_ERRNO("failed to wait for on_click handler");
} else {
LOG_DBG("ARGV:");
for (size_t i = 0; i < tokens; i++)
LOG_DBG(" #%zu: \"%s\" ", i, argv[i]);
LOG_DBG("daemonizing on-click handler");
daemon(0, 0);
LOG_DBG("executing on-click handler: %s", cmd);
execvp(argv[0], argv);
LOG_ERRNO("failed to run on_click handler (exec)");
}
}
}