]> git.sur5r.net Git - i3/i3/blobdiff - i3bar/src/child.c
Use _PATH_BSHELL to ensure using a bourne shell
[i3/i3] / i3bar / src / child.c
index e4c0f35737a123de9d75f679e036eb6ab5137ed0..dce0218fd305fda1ee38be246a47cc2e3618f249 100644 (file)
@@ -21,6 +21,8 @@
 #include <yajl/yajl_common.h>
 #include <yajl/yajl_parse.h>
 #include <yajl/yajl_version.h>
+#include <yajl/yajl_gen.h>
+#include <paths.h>
 
 #include "common.h"
 
@@ -35,7 +37,13 @@ ev_child *child_sig;
 yajl_callbacks callbacks;
 yajl_handle parser;
 
+/* JSON generator for stdout */
+yajl_gen gen;
+
 typedef struct parser_ctx {
+    /* True if one of the parsed blocks was urgent */
+    bool has_urgent;
+
     /* A copy of the last JSON map key. */
     char *last_map_key;
 
@@ -50,6 +58,8 @@ parser_ctx parser_context;
 struct statusline_head statusline_head = TAILQ_HEAD_INITIALIZER(statusline_head);
 char *statusline_buffer = NULL;
 
+int child_stdin;
+
 /*
  * Stop and free() the stdin- and sigchild-watchers
  *
@@ -82,6 +92,8 @@ static int stdin_start_array(void *context) {
         first = TAILQ_FIRST(&statusline_head);
         I3STRING_FREE(first->full_text);
         FREE(first->color);
+        FREE(first->name);
+        FREE(first->instance);
         TAILQ_REMOVE(&statusline_head, first, blocks);
         free(first);
     }
@@ -95,6 +107,10 @@ static int stdin_start_array(void *context) {
 static int stdin_start_map(void *context) {
     parser_ctx *ctx = context;
     memset(&(ctx->block), '\0', sizeof(struct status_block));
+
+    /* Default width of the separator block. */
+    ctx->block.sep_block_width = 9;
+
     return 1;
 }
 
@@ -109,6 +125,17 @@ static int stdin_map_key(void *context, const unsigned char *key, unsigned int l
     return 1;
 }
 
+static int stdin_boolean(void *context, int val) {
+    parser_ctx *ctx = context;
+    if (strcasecmp(ctx->last_map_key, "urgent") == 0) {
+        ctx->block.urgent = val;
+    }
+    if (strcasecmp(ctx->last_map_key, "separator") == 0) {
+        ctx->block.no_separator = !val;
+    }
+    return 1;
+}
+
 #if YAJL_MAJOR >= 2
 static int stdin_string(void *context, const unsigned char *val, size_t len) {
 #else
@@ -121,6 +148,46 @@ static int stdin_string(void *context, const unsigned char *val, unsigned int le
     if (strcasecmp(ctx->last_map_key, "color") == 0) {
         sasprintf(&(ctx->block.color), "%.*s", len, val);
     }
+    if (strcasecmp(ctx->last_map_key, "align") == 0) {
+        if (len == strlen("left") && !strncmp((const char*)val, "left", strlen("left"))) {
+            ctx->block.align = ALIGN_LEFT;
+        } else if (len == strlen("right") && !strncmp((const char*)val, "right", strlen("right"))) {
+            ctx->block.align = ALIGN_RIGHT;
+        } else {
+            ctx->block.align = ALIGN_CENTER;
+        }
+    } else if (strcasecmp(ctx->last_map_key, "min_width") == 0) {
+        i3String *text = i3string_from_utf8_with_length((const char *)val, len);
+        ctx->block.min_width = (uint32_t)predict_text_width(text);
+        i3string_free(text);
+    }
+    if (strcasecmp(ctx->last_map_key, "name") == 0) {
+        char *copy = (char*)malloc(len+1);
+        strncpy(copy, (const char *)val, len);
+        copy[len] = 0;
+        ctx->block.name = copy;
+    }
+    if (strcasecmp(ctx->last_map_key, "instance") == 0) {
+        char *copy = (char*)malloc(len+1);
+        strncpy(copy, (const char *)val, len);
+        copy[len] = 0;
+        ctx->block.instance = copy;
+    }
+    return 1;
+}
+
+#if YAJL_MAJOR >= 2
+static int stdin_integer(void *context, long long val) {
+#else
+static int stdin_integer(void *context, long val) {
+#endif
+    parser_ctx *ctx = context;
+    if (strcasecmp(ctx->last_map_key, "min_width") == 0) {
+        ctx->block.min_width = (uint32_t)val;
+    }
+    if (strcasecmp(ctx->last_map_key, "separator_block_width") == 0) {
+        ctx->block.sep_block_width = (uint32_t)val;
+    }
     return 1;
 }
 
@@ -132,6 +199,8 @@ static int stdin_end_map(void *context) {
      * i3bar doesn’t crash and the user gets an annoying message. */
     if (!new_block->full_text)
         new_block->full_text = i3string_from_utf8("SPEC VIOLATION (null)");
+    if (new_block->urgent)
+        ctx->has_urgent = true;
     TAILQ_INSERT_TAIL(&statusline_head, new_block, blocks);
     return 1;
 }
@@ -172,7 +241,7 @@ static unsigned char *get_buffer(ev_io *watcher, int *ret_buffer_len) {
             /* end of file, kill the watcher */
             ELOG("stdin: received EOF\n");
             cleanup();
-            draw_bars();
+            draw_bars(false);
             *ret_buffer_len = -1;
             return NULL;
         }
@@ -203,8 +272,9 @@ static void read_flat_input(char *buffer, int length) {
     first->full_text = i3string_from_utf8(buffer);
 }
 
-static void read_json_input(unsigned char *input, int length) {
+static bool read_json_input(unsigned char *input, int length) {
     yajl_status status = yajl_parse(parser, input, length);
+    bool has_urgent = false;
 #if YAJL_MAJOR >= 2
     if (status != yajl_status_ok) {
 #else
@@ -212,7 +282,10 @@ static void read_json_input(unsigned char *input, int length) {
 #endif
         fprintf(stderr, "[i3bar] Could not parse JSON input (code %d): %.*s\n",
                 status, length, input);
+    } else if (parser_context.has_urgent) {
+        has_urgent = true;
     }
+    return has_urgent;
 }
 
 /*
@@ -225,13 +298,14 @@ void stdin_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
     unsigned char *buffer = get_buffer(watcher, &rec);
     if (buffer == NULL)
         return;
+    bool has_urgent = false;
     if (child.version > 0) {
-        read_json_input(buffer, rec);
+        has_urgent = read_json_input(buffer, rec);
     } else {
         read_flat_input((char*)buffer, rec);
     }
     free(buffer);
-    draw_bars();
+    draw_bars(has_urgent);
 }
 
 /*
@@ -283,6 +357,21 @@ void child_sig_cb(struct ev_loop *loop, ev_child *watcher, int revents) {
     cleanup();
 }
 
+void child_write_output(void) {
+    if (child.click_events) {
+        const unsigned char *output;
+#if YAJL_MAJOR < 2
+        unsigned int size;
+#else
+        size_t size;
+#endif
+        yajl_gen_get_buf(gen, &output, &size);
+        write(child_stdin, output, size);
+        write(child_stdin, "\n", 1);
+        yajl_gen_clear(gen);
+    }
+}
+
 /*
  * Start a child-process with the specified command and reroute stdin.
  * We actually start a $SHELL to execute the command so we don't have to care
@@ -293,7 +382,9 @@ void start_child(char *command) {
     /* Allocate a yajl parser which will be used to parse stdin. */
     memset(&callbacks, '\0', sizeof(yajl_callbacks));
     callbacks.yajl_map_key = stdin_map_key;
+    callbacks.yajl_boolean = stdin_boolean;
     callbacks.yajl_string = stdin_string;
+    callbacks.yajl_integer = stdin_integer;
     callbacks.yajl_start_array = stdin_start_array;
     callbacks.yajl_end_array = stdin_end_array;
     callbacks.yajl_start_map = stdin_start_map;
@@ -302,14 +393,22 @@ void start_child(char *command) {
     yajl_parser_config parse_conf = { 0, 0 };
 
     parser = yajl_alloc(&callbacks, &parse_conf, NULL, (void*)&parser_context);
+
+    gen = yajl_gen_alloc(NULL, NULL);
 #else
     parser = yajl_alloc(&callbacks, NULL, &parser_context);
+
+    gen = yajl_gen_alloc(NULL);
 #endif
 
     if (command != NULL) {
-        int fd[2];
-        if (pipe(fd) == -1)
-            err(EXIT_FAILURE, "pipe(fd)");
+        int pipe_in[2]; /* pipe we read from */
+        int pipe_out[2]; /* pipe we write to */
+
+        if (pipe(pipe_in) == -1)
+            err(EXIT_FAILURE, "pipe(pipe_in)");
+        if (pipe(pipe_out) == -1)
+            err(EXIT_FAILURE, "pipe(pipe_out)");
 
         child.pid = fork();
         switch (child.pid) {
@@ -317,23 +416,24 @@ void start_child(char *command) {
                 ELOG("Couldn't fork(): %s\n", strerror(errno));
                 exit(EXIT_FAILURE);
             case 0:
-                /* Child-process. Reroute stdout and start shell */
-                close(fd[0]);
-
-                dup2(fd[1], STDOUT_FILENO);
+                /* Child-process. Reroute streams and start shell */
 
-                static const char *shell = NULL;
+                close(pipe_in[0]);
+                close(pipe_out[1]);
 
-                if ((shell = getenv("SHELL")) == NULL)
-                    shell = "/bin/sh";
+                dup2(pipe_in[1], STDOUT_FILENO);
+                dup2(pipe_out[0], STDIN_FILENO);
 
-                execl(shell, shell, "-c", command, (char*) NULL);
+                execl(_PATH_BSHELL, _PATH_BSHELL, "-c", command, (char*) NULL);
                 return;
             default:
-                /* Parent-process. Rerout stdin */
-                close(fd[1]);
+                /* Parent-process. Reroute streams */
+
+                close(pipe_in[1]);
+                close(pipe_out[0]);
 
-                dup2(fd[0], STDIN_FILENO);
+                dup2(pipe_in[0], STDIN_FILENO);
+                child_stdin = pipe_out[1];
 
                 break;
         }
@@ -354,6 +454,52 @@ void start_child(char *command) {
     atexit(kill_child_at_exit);
 }
 
+void child_click_events_initialize(void) {
+    if (!child.click_events_init) {
+        yajl_gen_array_open(gen);
+        child_write_output();
+        child.click_events_init = true;
+    }
+}
+
+void child_click_events_key(const char *key) {
+    yajl_gen_string(gen, (const unsigned char *)key, strlen(key));
+}
+
+/*
+ * Generates a click event, if enabled.
+ *
+ */
+void send_block_clicked(int button, const char *name, const char *instance, int x, int y) {
+    if (child.click_events) {
+        child_click_events_initialize();
+
+        yajl_gen_map_open(gen);
+
+        if (name) {
+            child_click_events_key("name");
+            yajl_gen_string(gen, (const unsigned char *)name, strlen(name));
+        }
+
+        if (instance) {
+            child_click_events_key("instance");
+            yajl_gen_string(gen, (const unsigned char *)instance, strlen(instance));
+        }
+
+        child_click_events_key("button");
+        yajl_gen_integer(gen, button);
+
+        child_click_events_key("x");
+        yajl_gen_integer(gen, x);
+
+        child_click_events_key("y");
+        yajl_gen_integer(gen, y);
+
+        yajl_gen_map_close(gen);
+        child_write_output();
+    }
+}
+
 /*
  * kill()s the child-process (if any). Called when exit()ing.
  *