From 6148136e7ce470a888eda29894115df700144428 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20L=C3=B6bl?= Date: Sat, 10 Nov 2012 13:41:39 +0100 Subject: [PATCH] i3bar: Add current binding mode indicator --- i3bar/include/common.h | 1 + i3bar/include/mode.h | 31 +++++++++ i3bar/include/xcb.h | 6 ++ i3bar/src/ipc.c | 18 +++++- i3bar/src/mode.c | 141 +++++++++++++++++++++++++++++++++++++++++ i3bar/src/xcb.c | 48 ++++++++++++++ 6 files changed, 242 insertions(+), 3 deletions(-) create mode 100644 i3bar/include/mode.h create mode 100644 i3bar/src/mode.c diff --git a/i3bar/include/common.h b/i3bar/include/common.h index e2582a02..870e6dbe 100644 --- a/i3bar/include/common.h +++ b/i3bar/include/common.h @@ -50,6 +50,7 @@ TAILQ_HEAD(statusline_head, status_block) statusline_head; #include "outputs.h" #include "util.h" #include "workspaces.h" +#include "mode.h" #include "trayclients.h" #include "xcb.h" #include "config.h" diff --git a/i3bar/include/mode.h b/i3bar/include/mode.h new file mode 100644 index 00000000..a8491aa9 --- /dev/null +++ b/i3bar/include/mode.h @@ -0,0 +1,31 @@ +/* + * 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) + * + * mode.c: Handle mode-event and show current binding mode in the bar + * + */ +#ifndef MODE_H_ +#define MODE_H_ + +#include + +#include "common.h" + +/* Name of current binding mode and its render width */ +struct mode { + i3String *name; + int width; +}; + +typedef struct mode mode; + +/* + * Start parsing the received json-string + * + */ +void parse_mode_json(char *json); + +#endif diff --git a/i3bar/include/xcb.h b/i3bar/include/xcb.h index dcc4d781..75019c8d 100644 --- a/i3bar/include/xcb.h +++ b/i3bar/include/xcb.h @@ -118,4 +118,10 @@ void draw_bars(bool force_unhide); */ void redraw_bars(void); +/* + * Set the current binding mode + * + */ +void set_current_mode(struct mode *mode); + #endif diff --git a/i3bar/src/ipc.c b/i3bar/src/ipc.c index fc8c6492..2170e509 100644 --- a/i3bar/src/ipc.c +++ b/i3bar/src/ipc.c @@ -138,10 +138,22 @@ void got_output_event(char *event) { } } +/* + * Called, when a mode-event arrives (i3 changed binding mode). + * + */ +void got_mode_event(char *event) { + DLOG("Got Mode Event!\n"); + parse_mode_json(event); + draw_bars(false); +} + + /* Data-structure to easily call the reply-handlers later */ handler_t event_handlers[] = { &got_workspace_event, - &got_output_event + &got_output_event, + &got_mode_event }; /* @@ -297,8 +309,8 @@ void destroy_connection(void) { */ void subscribe_events(void) { if (config.disable_ws) { - i3_send_msg(I3_IPC_MESSAGE_TYPE_SUBSCRIBE, "[ \"output\" ]"); + i3_send_msg(I3_IPC_MESSAGE_TYPE_SUBSCRIBE, "[ \"output\", \"mode\" ]"); } else { - i3_send_msg(I3_IPC_MESSAGE_TYPE_SUBSCRIBE, "[ \"workspace\", \"output\" ]"); + i3_send_msg(I3_IPC_MESSAGE_TYPE_SUBSCRIBE, "[ \"workspace\", \"output\", \"mode\" ]"); } } diff --git a/i3bar/src/mode.c b/i3bar/src/mode.c new file mode 100644 index 00000000..7363971a --- /dev/null +++ b/i3bar/src/mode.c @@ -0,0 +1,141 @@ +/* + * 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) + * + * mode.c: Handle mode-event and show current binding mode in the bar + * + */ +#include +#include +#include +#include +#include +#include + +#include "common.h" + +/* A datatype to pass through the callbacks to save the state */ +struct mode_json_params { + char *json; + char *cur_key; + mode *mode; +}; + +/* + * Parse a string (change) + * + */ +#if YAJL_MAJOR >= 2 +static int mode_string_cb(void *params_, const unsigned char *val, size_t len) { +#else +static int mode_string_cb(void *params_, const unsigned char *val, unsigned int len) { +#endif + struct mode_json_params *params = (struct mode_json_params*) params_; + + if (!strcmp(params->cur_key, "change")) { + + /* Save the name */ + params->mode->name = i3string_from_utf8_with_length((const char *)val, len); + /* Save its rendered width */ + params->mode->width = predict_text_width(params->mode->name); + + DLOG("Got mode change: %s\n", i3string_as_utf8(params->mode->name)); + FREE(params->cur_key); + + return 1; + } + + return 0; +} + +/* + * Parse a key. + * + * Essentially we just save it in the parsing-state + * + */ +#if YAJL_MAJOR >= 2 +static int mode_map_key_cb(void *params_, const unsigned char *keyVal, size_t keyLen) { +#else +static int mode_map_key_cb(void *params_, const unsigned char *keyVal, unsigned int keyLen) { +#endif + struct mode_json_params *params = (struct mode_json_params*) params_; + FREE(params->cur_key); + + params->cur_key = smalloc(sizeof(unsigned char) * (keyLen + 1)); + strncpy(params->cur_key, (const char*) keyVal, keyLen); + params->cur_key[keyLen] = '\0'; + + return 1; +} + +/* A datastructure to pass all these callbacks to yajl */ +yajl_callbacks mode_callbacks = { + NULL, + NULL, + NULL, + NULL, + NULL, + &mode_string_cb, + NULL, + &mode_map_key_cb, + NULL, + NULL, + NULL +}; + +/* + * Start parsing the received json-string + * + */ +void parse_mode_json(char *json) { + /* FIXME: Fasciliate stream-processing, i.e. allow starting to interpret + * JSON in chunks */ + struct mode_json_params params; + + mode binding; + + params.cur_key = NULL; + params.json = json; + params.mode = &binding; + + yajl_handle handle; + yajl_status state; + +#if YAJL_MAJOR < 2 + yajl_parser_config parse_conf = { 0, 0 }; + + handle = yajl_alloc(&mode_callbacks, &parse_conf, NULL, (void*) ¶ms); +#else + handle = yajl_alloc(&mode_callbacks, NULL, (void*) ¶ms); +#endif + + state = yajl_parse(handle, (const unsigned char*) json, strlen(json)); + + /* FIXME: Propper errorhandling for JSON-parsing */ + switch (state) { + case yajl_status_ok: + break; + case yajl_status_client_canceled: +#if YAJL_MAJOR < 2 + case yajl_status_insufficient_data: +#endif + case yajl_status_error: + ELOG("Could not parse mode-event!\n"); + exit(EXIT_FAILURE); + break; + } + + /* We don't want to indicate default binding mode */ + if (strcmp("default", i3string_as_utf8(params.mode->name)) == 0) + I3STRING_FREE(params.mode->name); + + /* Set the new binding mode */ + set_current_mode(&binding); + + yajl_free(handle); + + FREE(params.cur_key); +} diff --git a/i3bar/src/xcb.c b/i3bar/src/xcb.c index 2c0d2a6a..5ae8023a 100644 --- a/i3bar/src/xcb.c +++ b/i3bar/src/xcb.c @@ -74,6 +74,9 @@ ev_check *xcb_chk; ev_io *xcb_io; ev_io *xkb_io; +/* The name of current binding mode */ +static mode binding; + /* The parsed colors */ struct xcb_colors_t { uint32_t bar_fg; @@ -1527,6 +1530,41 @@ void draw_bars(bool unhide) { set_font_colors(outputs_walk->bargc, fg_color, bg_color); draw_text(ws_walk->name, outputs_walk->buffer, outputs_walk->bargc, i + 5, 2, ws_walk->name_width); i += 10 + ws_walk->name_width + 1; + + } + + if (binding.name) { + + uint32_t fg_color = colors.urgent_ws_fg; + uint32_t bg_color = colors.urgent_ws_bg; + uint32_t mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND; + + uint32_t vals_border[] = { colors.urgent_ws_border, colors.urgent_ws_border }; + xcb_change_gc(xcb_connection, + outputs_walk->bargc, + mask, + vals_border); + xcb_rectangle_t rect_border = { i, 0, binding.width + 10, font.height + 4 }; + xcb_poly_fill_rectangle(xcb_connection, + outputs_walk->buffer, + outputs_walk->bargc, + 1, + &rect_border); + + uint32_t vals[] = { bg_color, bg_color }; + xcb_change_gc(xcb_connection, + outputs_walk->bargc, + mask, + vals); + xcb_rectangle_t rect = { i + 1, 1, binding.width + 8, font.height + 2 }; + xcb_poly_fill_rectangle(xcb_connection, + outputs_walk->buffer, + outputs_walk->bargc, + 1, + &rect); + + set_font_colors(outputs_walk->bargc, fg_color, bg_color); + draw_text(binding.name, outputs_walk->buffer, outputs_walk->bargc, i + 5, 2, binding.width); } i = 0; @@ -1566,3 +1604,13 @@ void redraw_bars(void) { xcb_flush(xcb_connection); } } + +/* + * Set the current binding mode + * + */ +void set_current_mode(struct mode *current) { + I3STRING_FREE(binding.name); + binding = *current; + return; +}