58e68940f6
If the statusline generator (i.e. i3status) specifies click_events:true in the protocol header, i3bar will write a JSON array on it's stdin notifying it if the user clicks on a block. The exact protocol is documented in docs/i3bar-protocol.
152 lines
3.8 KiB
C
152 lines
3.8 KiB
C
/*
|
|
* vim:ts=4:sw=4:expandtab
|
|
*
|
|
* i3bar - an xcb-based status- and ws-bar for i3
|
|
* © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
|
|
*
|
|
* parse_json_header.c: Parse the JSON protocol header to determine
|
|
* protocol version and features.
|
|
*
|
|
*/
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <signal.h>
|
|
#include <stdio.h>
|
|
#include <fcntl.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <err.h>
|
|
#include <ev.h>
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <yajl/yajl_common.h>
|
|
#include <yajl/yajl_parse.h>
|
|
#include <yajl/yajl_version.h>
|
|
|
|
#include "common.h"
|
|
|
|
static enum {
|
|
KEY_VERSION,
|
|
KEY_STOP_SIGNAL,
|
|
KEY_CONT_SIGNAL,
|
|
KEY_CLICK_EVENTS,
|
|
NO_KEY
|
|
} current_key;
|
|
|
|
#if YAJL_MAJOR >= 2
|
|
static int header_integer(void *ctx, long long val) {
|
|
#else
|
|
static int header_integer(void *ctx, long val) {
|
|
#endif
|
|
i3bar_child *child = ctx;
|
|
|
|
switch (current_key) {
|
|
case KEY_VERSION:
|
|
child->version = val;
|
|
break;
|
|
case KEY_STOP_SIGNAL:
|
|
child->stop_signal = val;
|
|
break;
|
|
case KEY_CONT_SIGNAL:
|
|
child->cont_signal = val;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int header_boolean(void *ctx, int val) {
|
|
i3bar_child *child = ctx;
|
|
|
|
switch (current_key) {
|
|
case KEY_CLICK_EVENTS:
|
|
child->click_events = val;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
#define CHECK_KEY(name) (stringlen == strlen(name) && \
|
|
STARTS_WITH((const char*)stringval, stringlen, name))
|
|
|
|
#if YAJL_MAJOR >= 2
|
|
static int header_map_key(void *ctx, const unsigned char *stringval, size_t stringlen) {
|
|
#else
|
|
static int header_map_key(void *ctx, const unsigned char *stringval, unsigned int stringlen) {
|
|
#endif
|
|
if (CHECK_KEY("version")) {
|
|
current_key = KEY_VERSION;
|
|
} else if (CHECK_KEY("stop_signal")) {
|
|
current_key = KEY_STOP_SIGNAL;
|
|
} else if (CHECK_KEY("cont_signal")) {
|
|
current_key = KEY_CONT_SIGNAL;
|
|
} else if (CHECK_KEY("click_events")) {
|
|
current_key = KEY_CLICK_EVENTS;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static yajl_callbacks version_callbacks = {
|
|
NULL, /* null */
|
|
&header_boolean, /* boolean */
|
|
&header_integer,
|
|
NULL, /* double */
|
|
NULL, /* number */
|
|
NULL, /* string */
|
|
NULL, /* start_map */
|
|
&header_map_key,
|
|
NULL, /* end_map */
|
|
NULL, /* start_array */
|
|
NULL /* end_array */
|
|
};
|
|
|
|
static void child_init(i3bar_child *child) {
|
|
child->version = 0;
|
|
child->stop_signal = SIGSTOP;
|
|
child->cont_signal = SIGCONT;
|
|
}
|
|
|
|
/*
|
|
* Parse the JSON protocol header to determine protocol version and features.
|
|
* In case the buffer does not contain a valid header (invalid JSON, or no
|
|
* version field found), the 'correct' field of the returned header is set to
|
|
* false. The amount of bytes consumed by parsing the header is returned in
|
|
* *consumed (if non-NULL).
|
|
*
|
|
*/
|
|
void parse_json_header(i3bar_child *child, const unsigned char *buffer, int length, unsigned int *consumed) {
|
|
child_init(child);
|
|
|
|
current_key = NO_KEY;
|
|
|
|
#if YAJL_MAJOR >= 2
|
|
yajl_handle handle = yajl_alloc(&version_callbacks, NULL, child);
|
|
/* Allow trailing garbage. yajl 1 always behaves that way anyways, but for
|
|
* yajl 2, we need to be explicit. */
|
|
yajl_config(handle, yajl_allow_trailing_garbage, 1);
|
|
#else
|
|
yajl_parser_config parse_conf = { 0, 0 };
|
|
|
|
yajl_handle handle = yajl_alloc(&version_callbacks, &parse_conf, NULL, child);
|
|
#endif
|
|
|
|
yajl_status state = yajl_parse(handle, buffer, length);
|
|
if (state != yajl_status_ok) {
|
|
child_init(child);
|
|
if (consumed != NULL)
|
|
*consumed = 0;
|
|
} else {
|
|
if (consumed != NULL)
|
|
*consumed = yajl_get_bytes_consumed(handle);
|
|
}
|
|
|
|
yajl_free(handle);
|
|
}
|