]> git.sur5r.net Git - i3/i3/commitdiff
Merge branch 'next'
authorAxel Wagner <mail@merovius.de>
Thu, 23 Dec 2010 15:34:19 +0000 (16:34 +0100)
committerAxel Wagner <mail@merovius.de>
Thu, 23 Dec 2010 15:34:19 +0000 (16:34 +0100)
17 files changed:
i3bar/.gitignore
i3bar/CHANGELOG [new file with mode: 0644]
i3bar/Makefile
i3bar/common.mk
i3bar/doc/Makefile [new file with mode: 0644]
i3bar/doc/i3bar.man [new file with mode: 0644]
i3bar/include/child.h
i3bar/include/common.h
i3bar/include/config.h [new file with mode: 0644]
i3bar/include/util.h
i3bar/include/xcb.h
i3bar/src/child.c
i3bar/src/ipc.c
i3bar/src/main.c
i3bar/src/outputs.c
i3bar/src/workspaces.c
i3bar/src/xcb.c

index 01e7ad61a49f44a075eddb44b280b5fa858a72e3..6aad070bbfd044db07c9ab01b479550095f1f982 100644 (file)
@@ -1,3 +1,4 @@
 i3bar
 *.o
 core
+doc/i3bar.1
diff --git a/i3bar/CHANGELOG b/i3bar/CHANGELOG
new file mode 100644 (file)
index 0000000..0106447
--- /dev/null
@@ -0,0 +1,9 @@
+- Bugfix: Correctly render long text
+- Bugfix: Don't segfault on SIGCHILD
+- Implement hide-on-modifier
+- Use double-buffering
+
+
+v0.5
+=====
+- Initial release
index 57af15e1b95c415463a09c91dc89f39569056fef..d4cc59b9ee9818b9ce348b4eb69c41ef942c860e 100644 (file)
@@ -6,10 +6,17 @@ FILES:=$(wildcard src/*.c)
 FILES:=$(FILES:.c=.o)
 HEADERS:=$(wildcard include/*.h)
 
-all: ${FILES}
+all: i3bar doc
+
+i3bar: ${FILES}
        echo "LINK"
        $(CC) -o i3bar ${FILES} ${LDFLAGS}
 
+doc:
+       echo ""
+       echo "SUBDIR doc"
+       $(MAKE) -C doc
+
 src/%.o: src/%.c ${HEADERS}
        echo "CC $<"
        $(CC) $(CFLAGS) -c -o $@ $<
@@ -20,6 +27,11 @@ install: all
        $(INSTALL) -m 0755 i3bar $(DESTDIR)$(PREFIX)/bin
 
 clean:
-       rm src/*.o
+       rm -f src/*.o
+       make -C doc clean
+
+distclean: clean
+       rm -f i3bar
+       make -C doc distclean
 
-.PHONY: install clean
+.PHONY: install clean distclean doc
index 655883144a66e7132cb38ee1af4ad0ca5ce3cbac..3d44b02805bf20d4accb5bb7c1cf9bb4b4c23bd0 100644 (file)
@@ -14,7 +14,7 @@ CFLAGS += -DI3BAR_VERSION=\"${GIT_VERSION}\"
 LDFLAGS += -lev
 LDFLAGS += -lyajl
 LDFLAGS += -lxcb
-LDFLAGS += -lxcb-atom
+LDFLAGS += -lX11
 LDFLAGS += -L/usr/local/lib
 
 ifeq ($(DEBUG),1)
diff --git a/i3bar/doc/Makefile b/i3bar/doc/Makefile
new file mode 100644 (file)
index 0000000..a8144cd
--- /dev/null
@@ -0,0 +1,9 @@
+all: i3bar.1
+
+i3bar.1: i3bar.man
+       echo "A2X i3bar"
+       a2x -f manpage i3bar.man
+clean:
+       rm -f i3bar.xml i3bar.1 i3bar.html
+
+distclean: clean
diff --git a/i3bar/doc/i3bar.man b/i3bar/doc/i3bar.man
new file mode 100644 (file)
index 0000000..c41371b
--- /dev/null
@@ -0,0 +1,73 @@
+i3bar(1)
+========
+Axel Wagner <mail+i3bar@merovius.de>
+v0.5, September 2010
+
+== NAME
+
+i3bar - xcb-based status- and ws-bar
+
+== SYNOPSIS
+
+*i3bar* [*-s* 'sock_path'] [*-c* 'command'] [*-m*] [*-f* 'font'] [*-V*] [*-h*]
+
+== OPTIONS
+
+*-s, --socket* 'sock_path'::
+Specifies the 'socketpath', via which *i3bar* connects to *i3*(1). If *i3bar* can not connect to *i3*, it will exit. Defaults to '~/.i3/ipc.sock'
+
+*-c, --command* 'command'::
+Execute '<command>' to get 'stdin'. You can also simply pipe into 'stdin', but starting the coomand for itself, *i3bar* is able to send 'SIGCONT' and 'SIGSTOP', when combined with *-m*
+
+*-m, --hide*::
+Hide the bar, when 'mod4' is not pressed. With this, dockmode will not be set, and the bar is out of the way most of the time so you have more room.
+If *-c* is specified, the childprocess is sent a 'SIGSTOP' on hiding and a 'SIGCONT' on unhiding of the bars
+
+*-f, --font* 'font'::
+Specifies a 'X-core-font' to use. You can choose one with *xfontsel*(1). Defaults to '-misc-fixed-medium-r-semicondensed--12-110-75-75-c-60-iso10646-1'.
+
+*-V, --verbose*::
+Be (very) verbose with the debug-output. If not set, only errors are reported to 'stderr'
+
+*-h, --help*::
+Display a short help-message and exit
+
+== DESCRIPTION
+
+*i3bar* is an xcb- and libev-based status- and ws-bar. It is best thought of as an replacement for the *i3-wsbar*(1) + *dzen2*(1)-combination. It creates a workspace-bar for every active output ("screen") and displays a piped in statusline rightaligned on every bar.
+
+It does not sample any status-information itself, so you still need a program like *i3status*(1) or *conky*(1) for that.
+
+i3bar does not support any color or other markups, so stdin should be plain utf8, one line at a time. If you use *i3status*(1), you therefore should specify 'output_format = none' in the general-section of it's configfile.
+
+Also, you should disable the internal workspace bar of *i3*(1), when using *i3bar* by specifying 'workspace_bar no' in your *i3*-configfile.
+
+== COLORS
+
+*i3bar* does not yet support formatting in the displayed statusline. However it does support setting colors for the bar, the workspace-buttons and the statusline.
+
+For now this happens with the following command-line-options:
+
+*--color-bar-fg, --color-bar-bg, --color-active-ws-fg, --color-active-ws-bg, --color-inactive-ws-fg,  --color-inactive-ws-bg, color-urgent-ws-bg, color-urgent-ws-fg*
+
+For each specified option you need to give a HEX-colorcode.
+
+Be advised that this command-line-options are only temporary and are very likely to be removed, when we finally have a config-file.
+
+== EXAMPLES
+
+To get a docked bar with some statusinformation, you use
+
+*i3status | i3bar*
+
+If you want it to hide when not needed, you should instead use
+
+*i3bar -c i3status -m*
+
+== SEE ALSO
+
++i3(1)+, +i3-wsbar(1)+, +dzen2(1)+, +i3status(1)+
+
+== AUTHORS
+
+Axel Wagner and contributors
index 0405816d2b52e6abbf237985923c530fc2fd639e..69cfc5767a6c9b2c051cc19450215f13c6390e2e 100644 (file)
@@ -26,4 +26,16 @@ void start_child(char *command);
  */
 void kill_child();
 
+/*
+ * Sends a SIGSTOP to the child-process (if existent)
+ *
+ */
+void stop_child();
+
+/*
+ * Sends a SIGCONT to the child-process (if existent)
+ *
+ */
+void cont_child();
+
 #endif
index d6b63a7ecea532a71b8c55844c1d57580a294a85..4ad337039df78695f1750b7195b8e190ce9f6e06 100644 (file)
@@ -13,7 +13,6 @@ typedef struct rect_t rect;
 typedef int bool;
 
 struct ev_loop* main_loop;
-pid_t           child_pid;
 char            *statusline;
 
 struct rect_t {
@@ -31,5 +30,6 @@ struct rect_t {
 #include "workspaces.h"
 #include "xcb.h"
 #include "ucs2_to_utf8.h"
+#include "config.h"
 
 #endif
diff --git a/i3bar/include/config.h b/i3bar/include/config.h
new file mode 100644 (file)
index 0000000..2dd0f53
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef CONFIG_H_
+#define CONFIG_H_
+
+#include "common.h"
+
+typedef struct config_t {
+    int          hide_on_modifier;
+    int          verbose;
+    xcb_colors_t *colors;
+} config_t;
+
+config_t config;
+
+#endif
index 0f5dd3e3083496fad2a705f1db74083f3373d6c2..1952b03901e7e186bbe79ce2c2d3da7e6e6b9214 100644 (file)
 
 #include "queue.h"
 
+/* Get the maximum/minimum of x and y */
+#define MAX(x,y) ((x) > (y) ? (x) : (y))
+#define MIN(x,y) ((x) < (y) ? (x) : (y))
+
 /* Securely free p */
 #define FREE(p) do { \
     if (p != NULL) { \
         walk = TAILQ_FIRST(l); \
     } \
 } while (0)
+
+/* Use cool logging-macros */
+#define DLOG(fmt, ...) do { \
+    if (config.verbose) { \
+        printf("[%s:%d] " fmt, __FILE__, __LINE__, ##__VA_ARGS__); \
+    } \
+} while(0)
+
+#define ELOG(fmt, ...) do { \
+    fprintf(stderr, "[%s:%d] ERROR: " fmt, __FILE__, __LINE__, ##__VA_ARGS__); \
+} while(0)
index af2c89336b110ca8a5a66092202bb4a95aa07aeb..5ace4f0b665d4ffeea20df8fd43dbf4f3909c2e7 100644 (file)
@@ -9,7 +9,21 @@
 #ifndef XCB_H_
 #define XCB_H_
 
-int font_height;
+#include <stdint.h>
+//#include "outputs.h"
+
+struct xcb_color_strings_t {
+    char *bar_fg;
+    char *bar_bg;
+    char *active_ws_fg;
+    char *active_ws_bg;
+    char *inactive_ws_fg;
+    char *inactive_ws_bg;
+    char *urgent_ws_bg;
+    char *urgent_ws_fg;
+};
+
+typedef struct xcb_colors_t xcb_colors_t;
 
 /*
  * Initialize xcb and use the specified fontname for text-rendering
@@ -17,6 +31,12 @@ int font_height;
  */
 void init_xcb();
 
+/*
+ * Initialize the colors
+ *
+ */
+void init_colors(const struct xcb_color_strings_t *colors);
+
 /*
  * Cleanup the xcb-stuff.
  * Called once, before the program terminates.
@@ -49,11 +69,17 @@ void reconfig_windows();
 void draw_bars();
 
 /*
- * Calculate the rendered width of a string with the configured font.
+ * Redraw the bars, i.e. simply copy the buffer to the barwindow
+ *
+ */
+void redraw_bars();
+
+/*
+ * Predicts the length of text based on cached data.
  * The string has to be encoded in ucs2 and glyph_len has to be the length
- * of the string (in width)
+ * of the string (in glyphs).
  *
  */
-int get_string_width(xcb_char2b_t *string, int glyph_len);
+uint32_t predict_text_extents(xcb_char2b_t *text, uint32_t length);
 
 #endif
index c0a56661c5f92e320409abec19617ed5602393b9..08d2bed9e6cb552f8424185f337f8f45fad647ff 100644 (file)
@@ -12,6 +12,7 @@
 #include <unistd.h>
 #include <sys/types.h>
 #include <signal.h>
+#include <sys/wait.h>
 #include <stdio.h>
 #include <fcntl.h>
 #include <string.h>
@@ -20,6 +21,9 @@
 
 #include "common.h"
 
+/* Global variables for child_*() */
+pid_t child_pid;
+
 /* stdin- and sigchild-watchers */
 ev_io    *stdin_io;
 ev_child *child_sig;
@@ -37,38 +41,8 @@ void cleanup() {
 }
 
 /*
- * Since we don't use colors and stuff, we strip the dzen-formatstrings
- *
- */
-void strip_dzen_formats(char *buffer) {
-    char *src = buffer;
-    char *dest = buffer;
-    while (*src != '\0') {
-        /* ^ starts a format-string, ) ends it */
-        if (*src == '^') {
-            /* We replace the seperators from i3status by pipe-symbols */
-            if (!strncmp(src, "^ro", strlen("^ro"))) {
-                *(dest++) = ' ';
-                *(dest++) = '|';
-                *(dest++) = ' ';
-            }
-            while (*src != ')') {
-                src++;
-            }
-            src++;
-        } else {
-            *dest = *src;
-            src++;
-            dest++;
-        }
-    }
-    /* The last character is \n, which xcb cannot display */
-    *(--dest) = '\0';
-}
-
-/*
- * Callbalk for stdin. We read a line from stdin, strip dzen-formats and store
- * the result in statusline
+ * Callbalk for stdin. We read a line from stdin and store the result
+ * in statusline
  *
  */
 void stdin_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
@@ -82,20 +56,20 @@ void stdin_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
         n = read(fd, buffer + rec, buffer_len - rec);
         if (n == -1) {
             if (errno == EAGAIN) {
+                /* remove trailing newline and finish up */
+                buffer[rec-1] = '\0';
                 break;
             }
-            printf("ERROR: read() failed!");
+            ELOG("read() failed!\n");
             exit(EXIT_FAILURE);
         }
         if (n == 0) {
             if (rec == buffer_len) {
-                char *tmp = buffer;
-                buffer = malloc(buffer_len + STDIN_CHUNK_SIZE);
-                memset(buffer, '\0', buffer_len);
-                strncpy(buffer, tmp, buffer_len);
                 buffer_len += STDIN_CHUNK_SIZE;
-                FREE(tmp);
+                buffer = realloc(buffer, buffer_len);
             } else {
+                /* remove trailing newline and finish up */
+                buffer[rec-1] = '\0';
                 break;
             }
         }
@@ -105,10 +79,9 @@ void stdin_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
         FREE(buffer);
         return;
     }
-    strip_dzen_formats(buffer);
     FREE(statusline);
     statusline = buffer;
-    printf("%s\n", buffer);
+    DLOG("%s\n", buffer);
     draw_bars();
 }
 
@@ -119,7 +92,7 @@ void stdin_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
  *
  */
 void child_sig_cb(struct ev_loop *loop, ev_child *watcher, int revents) {
-    printf("Child (pid: %d) unexpectedly exited with status %d\n",
+    DLOG("Child (pid: %d) unexpectedly exited with status %d\n",
            child_pid,
            watcher->rstatus);
     cleanup();
@@ -128,40 +101,69 @@ void child_sig_cb(struct ev_loop *loop, ev_child *watcher, int revents) {
 /*
  * 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
- * about arguments and such
+ * about arguments and such.
+ * We also double-fork() to avoid zombies and pass the pid of the child through a
+ * temporary pipe back to i3bar
  *
  */
 void start_child(char *command) {
     child_pid = 0;
     if (command != NULL) {
-        int fd[2];
+        int fd[2], tmp[2];
+        /* This pipe will be used to communicate between e.g. i3status and i3bar */
         pipe(fd);
-        child_pid = fork();
-        switch (child_pid) {
+        /* We also need this temporary pipe to get back the pid of i3status */
+        pipe(tmp);
+        switch (fork()) {
             case -1:
-                printf("ERROR: Couldn't fork()");
+                ELOG("Couldn't fork()\n");
                 exit(EXIT_FAILURE);
             case 0:
-                close(fd[0]);
-
-                dup2(fd[1], STDOUT_FILENO);
-
-                static const char *shell = NULL;
-
-                if ((shell = getenv("SHELL")) == NULL)
-                    shell = "/bin/sh";
-
-                execl(shell, shell, "-c", command, (char*) NULL);
-                return;
+                /* Double-fork(), so the child gets reparented to init */
+                switch(child_pid = fork()) {
+                    case -1:
+                        ELOG("Couldn't fork() twice\n");
+                        exit(EXIT_FAILURE);
+                    case 0:
+                        /* Child-process. Reroute stdout and start shell */
+                        close(fd[0]);
+
+                        dup2(fd[1], STDOUT_FILENO);
+
+                        static const char *shell = NULL;
+
+                        if ((shell = getenv("SHELL")) == NULL)
+                            shell = "/bin/sh";
+
+                        execl(shell, shell, "-c", command, (char*) NULL);
+                        return;
+                    default:
+                        /* Temporary parent. We tell i3bar about the pid of i3status and exit */
+                        write(tmp[1], &child_pid, sizeof(int));
+                        close(tmp[0]);
+                        close(tmp[1]);
+                        exit(EXIT_SUCCESS);
+                    }
             default:
+                /* Parent-process. Rerout stdin */
                 close(fd[1]);
 
                 dup2(fd[0], STDIN_FILENO);
 
+                /* We also need to get the pid of i3status from the temporary pipe */
+                size_t rec = 0;
+                while (rec < sizeof(int)) {
+                    rec += read(tmp[0], &child_pid, sizeof(int) - rec);
+                }
+                /* The temporary pipe is no longer needed */
+                close(tmp[0]);
+                close(tmp[1]);
                 break;
         }
     }
+    wait(0);
 
+    /* We set O_NONBLOCK because blocking is evil in event-driven software */
     fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK);
 
     stdin_io = malloc(sizeof(ev_io));
@@ -176,13 +178,33 @@ void start_child(char *command) {
 }
 
 /*
- * kill()s the child-prozess (if existend) and closes and
+ * kill()s the child-process (if existent) and closes and
  * free()s the stdin- and sigchild-watchers
  *
  */
 void kill_child() {
     if (child_pid != 0) {
-        kill(child_pid, SIGQUIT);
+        kill(child_pid, SIGTERM);
     }
     cleanup();
 }
+
+/*
+ * Sends a SIGSTOP to the child-process (if existent)
+ *
+ */
+void stop_child() {
+    if (child_pid != 0) {
+        kill(child_pid, SIGSTOP);
+    }
+}
+
+/*
+ * Sends a SIGCONT to the child-process (if existent)
+ *
+ */
+void cont_child() {
+    if (child_pid != 0) {
+        kill(child_pid, SIGCONT);
+    }
+}
index 59563636d0fa2d9a62a2b65837a21651dd9d4fab..0ddccc2545c06c5c667ed3f610908dcb66ed3386 100644 (file)
@@ -30,7 +30,7 @@ typedef void(*handler_t)(char*);
 int get_ipc_fd(const char *socket_path) {
     int sockfd = socket(AF_LOCAL, SOCK_STREAM, 0);
     if (sockfd == -1) {
-        printf("ERROR: Could not create Socket!\n");
+        ELOG("Could not create Socket!\n");
         exit(EXIT_FAILURE);
     }
 
@@ -39,7 +39,7 @@ int get_ipc_fd(const char *socket_path) {
     addr.sun_family = AF_LOCAL;
     strcpy(addr.sun_path, socket_path);
     if (connect(sockfd, (const struct sockaddr*) &addr, sizeof(struct sockaddr_un)) < 0) {
-        printf("ERROR: Could not connct to i3\n");
+        ELOG("Could not connct to i3!\n");
         exit(EXIT_FAILURE);
     }
     return sockfd;
@@ -51,7 +51,7 @@ int get_ipc_fd(const char *socket_path) {
  *
  */
 void got_command_reply(char *reply) {
-    /* FIXME: Error handling for command-replies */
+    /* TODO: Error handling for command-replies */
 }
 
 /*
@@ -59,7 +59,7 @@ void got_command_reply(char *reply) {
  *
  */
 void got_workspace_reply(char *reply) {
-    printf("Got Workspace-Data!\n");
+    DLOG("Got Workspace-Data!\n");
     parse_workspaces_json(reply);
     draw_bars();
 }
@@ -70,8 +70,8 @@ void got_workspace_reply(char *reply) {
  *
  */
 void got_subscribe_reply(char *reply) {
-    printf("Got Subscribe Reply: %s\n", reply);
-    /* FIXME: Error handling for subscribe-commands */
+    DLOG("Got Subscribe Reply: %s\n", reply);
+    /* TODO: Error handling for subscribe-commands */
 }
 
 /*
@@ -79,9 +79,9 @@ void got_subscribe_reply(char *reply) {
  *
  */
 void got_output_reply(char *reply) {
-    printf("Parsing Outputs-JSON...\n");
+    DLOG("Parsing Outputs-JSON...\n");
     parse_outputs_json(reply);
-    printf("Reconfiguring Windows...\n");
+    DLOG("Reconfiguring Windows...\n");
     reconfig_windows();
 }
 
@@ -98,7 +98,7 @@ handler_t reply_handlers[] = {
  *
  */
 void got_workspace_event(char *event) {
-    printf("Got Workspace Event!\n");
+    DLOG("Got Workspace Event!\n");
     i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_WORKSPACES, NULL);
 }
 
@@ -107,7 +107,7 @@ void got_workspace_event(char *event) {
  *
  */
 void got_output_event(char *event) {
-    printf("Got Output Event!\n");
+    DLOG("Got Output Event!\n");
     i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_OUTPUTS, NULL);
     i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_WORKSPACES, NULL);
 }
@@ -123,47 +123,51 @@ handler_t event_handlers[] = {
  *
  */
 void got_data(struct ev_loop *loop, ev_io *watcher, int events) {
-    printf("Got data!\n");
+    DLOG("Got data!\n");
     int fd = watcher->fd;
 
     /* First we only read the header, because we know it's length */
     uint32_t header_len = strlen(I3_IPC_MAGIC) + sizeof(uint32_t)*2;
     char *header = malloc(header_len);
     if (header == NULL) {
-        printf("ERROR: Could not allocate memory!\n");
+        ELOG("Could not allocate memory!\n");
         exit(EXIT_FAILURE);
     }
 
+    /* We first parse the fixed-length IPC-header, to know, how much data
+     * we have to expect */
     uint32_t rec = 0;
     while (rec < header_len) {
         int n = read(fd, header + rec, header_len - rec);
         if (n == -1) {
-            printf("ERROR: read() failed!\n");
+            ELOG("read() failed!\n");
             exit(EXIT_FAILURE);
         }
         if (n == 0) {
-            printf("ERROR: Nothing to read!\n");
+            ELOG("Nothing to read!\n");
             exit(EXIT_FAILURE);
         }
         rec += n;
     }
 
     if (strncmp(header, I3_IPC_MAGIC, strlen(I3_IPC_MAGIC))) {
-        printf("ERROR: Wrong magic code: %.*s\n Expected: %s\n",
-               (int) strlen(I3_IPC_MAGIC),
-               header,
-               I3_IPC_MAGIC);
+        ELOG("Wrong magic code: %.*s\n Expected: %s\n",
+             (int) strlen(I3_IPC_MAGIC),
+             header,
+             I3_IPC_MAGIC);
         exit(EXIT_FAILURE);
     }
 
-    /* Know we read the rest of the message */
     char *walk = header + strlen(I3_IPC_MAGIC);
     uint32_t size = *((uint32_t*) walk);
     walk += sizeof(uint32_t);
     uint32_t type = *((uint32_t*) walk);
+
+    /* Now that we know, what to expect, we can start read()ing the rest
+     * of the message */
     char *buffer = malloc(size + 1);
     if (buffer == NULL) {
-        printf("ERROR: Could not allocate memory!\n");
+        ELOG("Could not allocate memory!\n");
         exit(EXIT_FAILURE);
     }
     rec = 0;
@@ -171,11 +175,11 @@ void got_data(struct ev_loop *loop, ev_io *watcher, int events) {
     while (rec < size) {
         int n = read(fd, buffer + rec, size - rec);
         if (n == -1) {
-            printf("ERROR: read() failed!\n");
+            ELOG("read() failed!\n");
             exit(EXIT_FAILURE);
         }
         if (n == 0) {
-            printf("ERROR: Nothing to read!\n");
+            ELOG("Nothing to read!\n");
             exit(EXIT_FAILURE);
         }
         rec += n;
@@ -205,10 +209,14 @@ int i3_send_msg(uint32_t type, const char *payload) {
         len = strlen(payload);
     }
 
+    /* We are a wellbehaved client and send a proper header first */
     uint32_t to_write = strlen (I3_IPC_MAGIC) + sizeof(uint32_t)*2 + len;
+    /* TODO: I'm not entirely sure if this buffer really has to contain more
+     * than the pure header (why not just write() the payload from *payload?),
+     * but we leave it for now */
     char *buffer = malloc(to_write);
     if (buffer == NULL) {
-        printf("ERROR: Could not allocate memory\n");
+        ELOG("Could not allocate memory\n");
         exit(EXIT_FAILURE);
     }
 
@@ -228,7 +236,7 @@ int i3_send_msg(uint32_t type, const char *payload) {
     while (to_write > 0) {
         int n = write(i3_connection->fd, buffer + written, to_write);
         if (n == -1) {
-            printf("ERROR: write() failed!\n");
+            ELOG("write() failed!\n");
             exit(EXIT_FAILURE);
         }
 
index 45a4c0dc3d8743f21171cb0f94f046e1650a9337..ec85d32455476b67f43debaaa334e15a89a53b94 100644 (file)
 
 #include "common.h"
 
-char *i3_default_sock_path = "~/.i3/ipc.sock";
-
+/*
+ * Glob path, i.e. expand ~
+ *
+ */
 char *expand_path(char *path) {
     static glob_t globbuf;
     if (glob(path, GLOB_NOCHECK | GLOB_TILDE, NULL, &globbuf) < 0) {
-        printf("glob() failed");
+        ELOG("glob() failed\n");
         exit(EXIT_FAILURE);
     }
     char *result = strdup(globbuf.gl_pathc > 0 ? globbuf.gl_pathv[0] : path);
     if (result == NULL) {
-        printf("malloc() failed");
+        ELOG("malloc() failed\n");
         exit(EXIT_FAILURE);
     }
     globfree(&globbuf);
     return result;
 }
 
+static void read_color(char **color) {
+    int len = strlen(optarg);
+    if (len == 6 || (len == 7 && optarg[0] == '#')) {
+        int offset = len - 6;
+        int good = 1, i;
+        for (i = offset; good && i < 6 + offset; ++i) {
+            char c = optarg[i];
+            if (!(c >= 'a' && c <= 'f')
+                    && !(c >= 'A' && c <= 'F')
+                    && !(c >= '0' && c <= '9')) {
+                good = 0;
+                break;
+            }
+        }
+        if (good) {
+            *color = strdup(optarg + offset);
+            return;
+        }
+    }
+
+    fprintf(stderr, "Bad color value \"%s\"\n", optarg);
+    exit(EXIT_FAILURE);
+}
+
+static void free_colors(struct xcb_color_strings_t *colors) {
+#define FREE_COLOR(x) \
+    do { \
+        if (colors->x) \
+            free(colors->x); \
+    } while (0)
+    FREE_COLOR(bar_fg);
+    FREE_COLOR(bar_bg);
+    FREE_COLOR(active_ws_fg);
+    FREE_COLOR(active_ws_bg);
+    FREE_COLOR(inactive_ws_fg);
+    FREE_COLOR(inactive_ws_bg);
+    FREE_COLOR(urgent_ws_fg);
+    FREE_COLOR(urgent_ws_bg);
+#undef FREE_COLOR
+}
+
+void print_usage(char *elf_name) {
+    printf("Usage: %s [-s sock_path] [-c command] [-m] [-f font] [-V] [-h]\n", elf_name);
+    printf("-s <sock_path>\tConnect to i3 via <sock_path>\n");
+    printf("-c <command>\tExecute <command> to get stdin\n");
+    printf("-m\t\tHide the bars, when mod4 is not pressed.\n");
+    printf("\t\tIf -c is specified, the childprocess is sent a SIGSTOP on hiding,\n");
+    printf("\t\tand a SIGCONT on unhiding of the bars\n");
+    printf("-f <font>\tUse X-Core-Font <font> for display\n");
+    printf("-V\t\tBe (very) verbose with the debug-output\n");
+    printf("-h\t\tDisplay this help-message and exit\n");
+}
+
+/*
+ * We watch various signals, that are there to make our application stop.
+ * If we get one of those, we ev_unloop() and invoke the cleanup-routines
+ * in main() with that
+ *
+ */
+void sig_cb(struct ev_loop *loop, ev_signal *watcher, int revents) {
+    switch (watcher->signum) {
+        case SIGTERM:
+            DLOG("Got a SIGTERM, stopping\n");
+            break;
+        case SIGINT:
+            DLOG("Got a SIGINT, stopping\n");
+            break;
+        case SIGHUP:
+            DLOG("Got a SIGHUP, stopping\n");
+    }
+    ev_unloop(main_loop, EVUNLOOP_ALL);
+}
+
 int main(int argc, char **argv) {
     int opt;
     int option_index = 0;
     char *socket_path = NULL;
     char *command = NULL;
     char *fontname = NULL;
+    char *i3_default_sock_path = "~/.i3/ipc.sock";
+    struct xcb_color_strings_t colors = { NULL, };
+
+    /* Definition of the standard-config */
+    config.hide_on_modifier = 0;
 
     static struct option long_opt[] = {
-        { "socket",  required_argument, 0, 's' },
-        { "command", required_argument, 0, 'c' },
-        { "font",    required_argument, 0, 'f' },
-        { "help",    no_argument,       0, 'h' },
-        { "version", no_argument,       0, 'v' },
-        { NULL,      0,                 0, 0}
+        { "socket",               required_argument, 0, 's' },
+        { "command",              required_argument, 0, 'c' },
+        { "hide",                 no_argument,       0, 'm' },
+        { "font",                 required_argument, 0, 'f' },
+        { "help",                 no_argument,       0, 'h' },
+        { "version",              no_argument,       0, 'v' },
+        { "verbose",              no_argument,       0, 'V' },
+        { "color-bar-fg",         required_argument, 0, 'A' },
+        { "color-bar-bg",         required_argument, 0, 'B' },
+        { "color-active-ws-fg",   required_argument, 0, 'C' },
+        { "color-active-ws-bg",   required_argument, 0, 'D' },
+        { "color-inactive-ws-fg", required_argument, 0, 'E' },
+        { "color-inactive-ws-bg", required_argument, 0, 'F' },
+        { "color-urgent-ws-bg",   required_argument, 0, 'G' },
+        { "color-urgent-ws-fg",   required_argument, 0, 'H' },
+        { NULL,                   0,                 0, 0}
     };
 
-    while ((opt = getopt_long(argc, argv, "s:c:f:hv", long_opt, &option_index)) != -1) {
+    while ((opt = getopt_long(argc, argv, "s:c:mf:hvVA:B:C:D:E:F:G:H:", long_opt, &option_index)) != -1) {
         switch (opt) {
             case 's':
                 socket_path = expand_path(optarg);
@@ -59,47 +149,102 @@ int main(int argc, char **argv) {
             case 'c':
                 command = strdup(optarg);
                 break;
+            case 'm':
+                config.hide_on_modifier = 1;
+                break;
             case 'f':
                 fontname = strdup(optarg);
                 break;
             case 'v':
                 printf("i3bar version " I3BAR_VERSION " Â© 2010 Axel Wagner and contributors\n");
                 exit(EXIT_SUCCESS);
+                break;
+            case 'V':
+                config.verbose = 1;
+                break;
+            case 'A':
+                read_color(&colors.bar_fg);
+                break;
+            case 'B':
+                read_color(&colors.bar_bg);
+                break;
+            case 'C':
+                read_color(&colors.active_ws_fg);
+                break;
+            case 'D':
+                read_color(&colors.active_ws_bg);
+                break;
+            case 'E':
+                read_color(&colors.inactive_ws_fg);
+                break;
+            case 'F':
+                read_color(&colors.inactive_ws_bg);
+                break;
+            case 'G':
+                read_color(&colors.urgent_ws_bg);
+                break;
+            case 'H':
+                read_color(&colors.urgent_ws_fg);
+                break;
             default:
-                printf("Usage: %s [-s socket_path] [-c command] [-f font] [-h]\n", argv[0]);
-                printf("-s <socket_path>: Connect to i3 via <socket_path>\n");
-                printf("-c <command>: Execute <command> to get sdtin\n");
-                printf("-f <font>: Use X-Core-Font <font> for display\n");
-                printf("-h: Display this help-message and exit\n");
+                print_usage(argv[0]);
                 exit(EXIT_SUCCESS);
                 break;
         }
     }
 
     if (fontname == NULL) {
+        /* This is a very restrictive default. More sensefull would be something like
+         * "-misc-*-*-*-*--*-*-*-*-*-*-*-*". But since that produces very ugly results
+         * on my machine, let's stick with this until we have a configfile */
         fontname = "-misc-fixed-medium-r-semicondensed--12-110-75-75-c-60-iso10646-1";
     }
 
     if (socket_path == NULL) {
-        printf("No Socket Path Specified, default to %s\n", i3_default_sock_path);
+        ELOG("No Socket Path Specified, default to %s\n", i3_default_sock_path);
         socket_path = expand_path(i3_default_sock_path);
     }
 
     main_loop = ev_default_loop(0);
 
+    init_colors(&colors);
     init_xcb(fontname);
+
+    free_colors(&colors);
+
     init_outputs();
     init_connection(socket_path);
 
     FREE(socket_path);
 
+    /* We subscribe to the i3-events we need */
     subscribe_events();
 
+    /* We initiate the main-function by requesting infos about the outputs and
+     * workspaces. Everything else (creating the bars, showing the right workspace-
+     * buttons and more) is taken care of by the event-driveniness of the code */
     i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_OUTPUTS, NULL);
     i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_WORKSPACES, NULL);
 
+    /* The name of this function is actually misleading. Even if no -c is specified,
+     * this function initiates the watchers to listen on stdin and react accordingly */
     start_child(command);
 
+    /* We listen to SIGTERM/QUIT/INT and try to exit cleanly, by stopping the main-loop.
+     * We only need those watchers on the stack, so putting them on the stack saves us
+     * some calls to free() */
+    ev_signal sig_term, sig_int, sig_hup;
+
+    ev_signal_init(&sig_term, &sig_cb, SIGTERM);
+    ev_signal_init(&sig_int, &sig_cb, SIGINT);
+    ev_signal_init(&sig_hup, &sig_cb, SIGHUP);
+
+    ev_signal_start(main_loop, &sig_term);
+    ev_signal_start(main_loop, &sig_int);
+    ev_signal_start(main_loop, &sig_hup);
+
+    /* From here on everything should run smooth for itself, just start listening for
+     * events. We stop simply stop the event-loop, when we are finished */
     ev_loop(main_loop, 0);
 
     kill_child();
index de905783f68e70ce9e562f15a3658fcf96d015d3..3577d82b58aa997e8f1f0718f470e4cf152f0c19 100644 (file)
@@ -230,7 +230,7 @@ void parse_outputs_json(char *json) {
         case yajl_status_client_canceled:
         case yajl_status_insufficient_data:
         case yajl_status_error:
-            printf("ERROR: Could not parse outputs-reply!\n");
+            ELOG("Could not parse outputs-reply!\n");
             exit(EXIT_FAILURE);
             break;
     }
index 8ba79eecfc98f8d8b4e0f9edc52873b6ce977dac..9f8acc1b358f52640b4a305d9e9d5c8f76856d78 100644 (file)
@@ -115,13 +115,14 @@ static int workspaces_string_cb(void *params_, const unsigned char *val, unsigne
             xcb_char2b_t *ucs2_name = (xcb_char2b_t*) convert_utf8_to_ucs2(params->workspaces_walk->name, &ucs2_len);
             params->workspaces_walk->ucs2_name = ucs2_name;
             params->workspaces_walk->name_glyphs = ucs2_len;
-            params->workspaces_walk->name_width = get_string_width(params->workspaces_walk->ucs2_name,
-                                                                   params->workspaces_walk->name_glyphs);
-
-            printf("Got Workspace %s, name_width: %d, glyphs: %d\n",
-                   params->workspaces_walk->name,
-                   params->workspaces_walk->name_width,
-                   params->workspaces_walk->name_glyphs);
+            params->workspaces_walk->name_width =
+                predict_text_extents(params->workspaces_walk->ucs2_name,
+                params->workspaces_walk->name_glyphs);
+
+            DLOG("Got Workspace %s, name_width: %d, glyphs: %d\n",
+                 params->workspaces_walk->name,
+                 params->workspaces_walk->name_width,
+                 params->workspaces_walk->name_glyphs);
             FREE(params->cur_key);
 
             return 1;
@@ -183,7 +184,7 @@ static int workspaces_map_key_cb(void *params_, const unsigned char *keyVal, uns
 
     params->cur_key = malloc(sizeof(unsigned char) * (keyLen + 1));
     if (params->cur_key == NULL) {
-        printf("ERROR: Could not allocate memory!\n");
+        ELOG("Could not allocate memory!\n");
         exit(EXIT_FAILURE);
     }
     strncpy(params->cur_key, (const char*) keyVal, keyLen);
@@ -237,7 +238,7 @@ void parse_workspaces_json(char *json) {
         case yajl_status_client_canceled:
         case yajl_status_insufficient_data:
         case yajl_status_error:
-            printf("ERROR: Could not parse workspaces-reply!\n");
+            ELOG("Could not parse workspaces-reply!\n");
             exit(EXIT_FAILURE);
             break;
     }
index 7f6379e191958333fac53f4084e7eec637f09e5e..300ac8595932a0ea5f9840b7c80e8c8deb155daa 100644 (file)
  */
 #include <xcb/xcb.h>
 #include <xcb/xproto.h>
-#include <xcb/xcb_event.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
 #include <string.h>
 #include <i3/ipc.h>
 #include <ev.h>
 
+#include <X11/Xlib.h>
+#include <X11/XKBlib.h>
+#include <X11/extensions/XKB.h>
+
 #include "common.h"
 
 /* We save the Atoms in an easy to access array, indexed by an enum */
@@ -32,14 +37,124 @@ xcb_atom_t               atoms[NUM_ATOMS];
 
 /* Variables, that are the same for all functions at all times */
 xcb_connection_t *xcb_connection;
-xcb_screen_t     *xcb_screens;
+xcb_screen_t     *xcb_screen;
 xcb_window_t     xcb_root;
 xcb_font_t       xcb_font;
 
+/* We need to cache some data to speed up text-width-prediction */
+xcb_query_font_reply_t *font_info;
+int                    font_height;
+xcb_charinfo_t         *font_table;
+
+/* These are only relevant for XKB, which we only need for grabbing modifiers */
+Display          *xkb_dpy;
+int              xkb_event_base;
+int              mod_pressed;
+
+/* Because the statusline is the same on all outputs, we have
+ * global buffer to render it on */
+xcb_gcontext_t   statusline_ctx;
+xcb_pixmap_t     statusline_pm;
+uint32_t         statusline_width;
+
 /* Event-Watchers, to interact with the user */
 ev_prepare *xcb_prep;
 ev_check   *xcb_chk;
 ev_io      *xcb_io;
+ev_io      *xkb_io;
+
+/* The parsed colors */
+struct xcb_colors_t {
+    uint32_t bar_fg;
+    uint32_t bar_bg;
+    uint32_t active_ws_fg;
+    uint32_t active_ws_bg;
+    uint32_t inactive_ws_fg;
+    uint32_t inactive_ws_bg;
+    uint32_t urgent_ws_bg;
+    uint32_t urgent_ws_fg;
+};
+struct xcb_colors_t colors;
+
+/* We define xcb_request_failed as a macro to include the relevant line-number */
+#define xcb_request_failed(cookie, err_msg) _xcb_request_failed(cookie, err_msg, __LINE__)
+int _xcb_request_failed(xcb_void_cookie_t cookie, char *err_msg, int line) {
+    xcb_generic_error_t *err;
+    if ((err = xcb_request_check(xcb_connection, cookie)) != NULL) {
+        ELOG("%s. X Error Code: %d\n", err_msg, err->error_code);
+        return err->error_code;
+    }
+    return 0;
+}
+
+/*
+ * Predicts the length of text based on cached data.
+ * The string has to be encoded in ucs2 and glyph_len has to be the length
+ * of the string (in glyphs).
+ *
+ */
+uint32_t predict_text_extents(xcb_char2b_t *text, uint32_t length) {
+    /* If we don't have per-character data, return the maximum width */
+    if (font_table == NULL) {
+        return (font_info->max_bounds.character_width * length);
+    }
+
+    uint32_t width = 0;
+    uint32_t i;
+
+    for (i = 0; i < length; i++) {
+        xcb_charinfo_t *info;
+        int row = text[i].byte1;
+        int col = text[i].byte2;
+
+        if (row < font_info->min_byte1 || row > font_info->max_byte1 ||
+            col < font_info->min_char_or_byte2 || col > font_info->max_char_or_byte2) {
+            continue;
+        }
+
+        /* Don't you ask me, how this one works… */
+        info = &font_table[((row - font_info->min_byte1) *
+                            (font_info->max_char_or_byte2 - font_info->min_char_or_byte2 + 1)) +
+                           (col - font_info->min_char_or_byte2)];
+
+        if (info->character_width != 0 ||
+            (info->right_side_bearing |
+             info->left_side_bearing |
+             info->ascent |
+             info->descent) != 0) {
+            width += info->character_width;
+        }
+    }
+
+    return width;
+}
+
+/*
+ * Draws text given in UCS-2-encoding to a given drawable and position
+ *
+ */
+void draw_text(xcb_drawable_t drawable, xcb_gcontext_t ctx, int16_t x, int16_t y,
+               xcb_char2b_t *text, uint32_t glyph_count) {
+    int offset = 0;
+    int16_t pos_x = x;
+    int16_t font_ascent = font_info->font_ascent;
+
+    while (glyph_count > 0) {
+        uint8_t chunk_size = MIN(255, glyph_count);
+        uint32_t chunk_width = predict_text_extents(text + offset, chunk_size);
+
+        xcb_image_text_16(xcb_connection,
+                          chunk_size,
+                          drawable,
+                          ctx,
+                          pos_x, y + font_ascent,
+                          text + offset);
+
+        offset += chunk_size;
+        pos_x += chunk_width;
+        glyph_count -= chunk_size;
+    }
+}
 
 /*
  * Converts a colorstring to a colorpixel as expected from xcb_change_gc.
@@ -56,6 +171,115 @@ uint32_t get_colorpixel(const char *s) {
     return (r << 16 | g << 8 | b);
 }
 
+/*
+ * Redraws the statusline to the buffer
+ *
+ */
+void refresh_statusline() {
+    int glyph_count;
+
+    if (statusline == NULL) {
+        return;
+    }
+
+    xcb_char2b_t *text = (xcb_char2b_t*) convert_utf8_to_ucs2(statusline, &glyph_count);
+    statusline_width = predict_text_extents(text, glyph_count);
+
+    xcb_free_pixmap(xcb_connection, statusline_pm);
+    statusline_pm = xcb_generate_id(xcb_connection);
+    xcb_void_cookie_t sl_pm_cookie = xcb_create_pixmap_checked(xcb_connection,
+                                                               xcb_screen->root_depth,
+                                                               statusline_pm,
+                                                               xcb_root,
+                                                               statusline_width,
+                                                               font_height);
+
+    draw_text(statusline_pm, statusline_ctx, 0, 0, text, glyph_count);
+
+    if (xcb_request_failed(sl_pm_cookie, "Could not allocate statusline-buffer")) {
+        exit(EXIT_FAILURE);
+    }
+}
+
+/*
+ * Hides all bars (unmaps them)
+ *
+ */
+void hide_bars() {
+    if (!config.hide_on_modifier) {
+        return;
+    }
+
+    i3_output *walk;
+    SLIST_FOREACH(walk, outputs, slist) {
+        xcb_unmap_window(xcb_connection, walk->bar);
+    }
+    stop_child();
+}
+
+/*
+ * Unhides all bars (maps them)
+ *
+ */
+void unhide_bars() {
+    if (!config.hide_on_modifier) {
+        return;
+    }
+
+    i3_output           *walk;
+    xcb_void_cookie_t   cookie;
+    uint32_t            mask;
+    uint32_t            values[5];
+
+    cont_child();
+
+    SLIST_FOREACH(walk, outputs, slist) {
+        if (walk->bar == XCB_NONE) {
+            continue;
+        }
+        mask = XCB_CONFIG_WINDOW_X |
+               XCB_CONFIG_WINDOW_Y |
+               XCB_CONFIG_WINDOW_WIDTH |
+               XCB_CONFIG_WINDOW_HEIGHT |
+               XCB_CONFIG_WINDOW_STACK_MODE;
+        values[0] = walk->rect.x;
+        values[1] = walk->rect.y + walk->rect.h - font_height - 6;
+        values[2] = walk->rect.w;
+        values[3] = font_height + 6;
+        values[4] = XCB_STACK_MODE_ABOVE;
+        DLOG("Reconfiguring Window for output %s to %d,%d\n", walk->name, values[0], values[1]);
+        cookie = xcb_configure_window_checked(xcb_connection,
+                                              walk->bar,
+                                              mask,
+                                              values);
+
+        if (xcb_request_failed(cookie, "Could not reconfigure window")) {
+            exit(EXIT_FAILURE);
+        }
+        xcb_map_window(xcb_connection, walk->bar);
+    }
+}
+
+/*
+ * Parse the colors into a format that we can use
+ *
+ */
+void init_colors(const struct xcb_color_strings_t *new_colors) {
+#define PARSE_COLOR(name, def) \
+    do { \
+        colors.name = get_colorpixel(new_colors->name ? new_colors->name : def); \
+    } while  (0)
+    PARSE_COLOR(bar_fg, "FFFFFF");
+    PARSE_COLOR(bar_bg, "000000");
+    PARSE_COLOR(active_ws_fg, "FFFFFF");
+    PARSE_COLOR(active_ws_bg, "480000");
+    PARSE_COLOR(inactive_ws_fg, "FFFFFF");
+    PARSE_COLOR(inactive_ws_bg, "240000");
+    PARSE_COLOR(urgent_ws_fg, "FFFFFF");
+    PARSE_COLOR(urgent_ws_bg, "002400");
+#undef PARSE_COLOR
+}
+
 /*
  * Handle a button-press-event (i.c. a mouse click on one of our bars).
  * We determine, wether the click occured on a ws-button or if the scroll-
@@ -75,11 +299,11 @@ void handle_button(xcb_button_press_event_t *event) {
     }
 
     if (walk == NULL) {
-        printf("Unknown Bar klicked!\n");
+        DLOG("Unknown Bar klicked!\n");
         return;
     }
 
-    /* TODO: Move this to exern get_ws_for_output() */
+    /* TODO: Move this to extern get_ws_for_output() */
     TAILQ_FOREACH(cur_ws, walk->workspaces, tailq) {
         if (cur_ws->visible) {
             break;
@@ -87,20 +311,20 @@ void handle_button(xcb_button_press_event_t *event) {
     }
 
     if (cur_ws == NULL) {
-        printf("No Workspace active?\n");
+        DLOG("No Workspace active?\n");
         return;
     }
 
     int32_t x = event->event_x;
 
-    printf("Got Button %d\n", event->detail);
+    DLOG("Got Button %d\n", event->detail);
 
     switch (event->detail) {
         case 1:
             /* Left Mousbutton. We determine, which button was clicked
              * and set cur_ws accordingly */
             TAILQ_FOREACH(cur_ws, walk->workspaces, tailq) {
-                printf("x = %d\n", x);
+                DLOG("x = %d\n", x);
                 if (x < cur_ws->name_width + 10) {
                     break;
                 }
@@ -157,7 +381,7 @@ void xcb_chk_cb(struct ev_loop *loop, ev_check *watcher, int revents) {
     switch (event->response_type & ~0x80) {
         case XCB_EXPOSE:
             /* Expose-events happen, when the window needs to be redrawn */
-            draw_bars();
+            redraw_bars();
             break;
         case XCB_BUTTON_PRESS:
             /* Button-press-events are mouse-buttons clicked on one of our bars */
@@ -176,27 +400,42 @@ void xcb_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
 }
 
 /*
- * Calculate the rendered width of a string with the configured font.
- * The string has to be encoded in ucs2 and glyph_len has to be the length
- * of the string (in width)
+ * We need to bind to the modifier per XKB. Sadly, XCB does not implement this
  *
  */
-int get_string_width(xcb_char2b_t *string, int glyph_len) {
-    xcb_query_text_extents_cookie_t cookie;
-    xcb_query_text_extents_reply_t *reply;
-    xcb_generic_error_t *error;
-    int width;
-
-    cookie = xcb_query_text_extents(xcb_connection, xcb_font, glyph_len, string);
-    reply = xcb_query_text_extents_reply(xcb_connection, cookie, &error);
-    if (reply == NULL) {
-        printf("ERROR: Could not get text extents!");
-        return 7;
+void xkb_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
+    XkbEvent ev;
+    int modstate;
+
+    DLOG("Got XKB-Event!\n");
+
+    while (XPending(xkb_dpy)) {
+        XNextEvent(xkb_dpy, (XEvent*)&ev);
+
+        if (ev.type != xkb_event_base) {
+            ELOG("No Xkb-Event!\n");
+            continue;
+        }
+
+        if (ev.any.xkb_type != XkbStateNotify) {
+            ELOG("No State Notify!\n");
+            continue;
+        }
+
+        unsigned int mods = ev.state.mods;
+        modstate = mods & Mod4Mask;
     }
 
-    width = reply->overall_width;
-    free(reply);
-    return width;
+    if (modstate != mod_pressed) {
+        if (modstate == 0) {
+            DLOG("Mod4 got released!\n");
+            hide_bars();
+        } else {
+            DLOG("Mod4 got pressed!\n");
+            unhide_bars();
+        }
+        mod_pressed = modstate;
+    }
 }
 
 /*
@@ -207,17 +446,17 @@ void init_xcb(char *fontname) {
     /* FIXME: xcb_connect leaks Memory */
     xcb_connection = xcb_connect(NULL, NULL);
     if (xcb_connection_has_error(xcb_connection)) {
-        printf("Cannot open display\n");
+        ELOG("Cannot open display\n");
         exit(EXIT_FAILURE);
     }
-    printf("Connected to xcb\n");
+    DLOG("Connected to xcb\n");
 
     /* We have to request the atoms we need */
     #define ATOM_DO(name) atom_cookies[name] = xcb_intern_atom(xcb_connection, 0, strlen(#name), #name);
     #include "xcb_atoms.def"
 
-    xcb_screens = xcb_setup_roots_iterator(xcb_get_setup(xcb_connection)).data;
-    xcb_root = xcb_screens->root;
+    xcb_screen = xcb_setup_roots_iterator(xcb_get_setup(xcb_connection)).data;
+    xcb_root = xcb_screen->root;
 
     /* We load and allocate the font */
     xcb_font = xcb_generate_id(xcb_connection);
@@ -227,12 +466,71 @@ void init_xcb(char *fontname) {
                                              strlen(fontname),
                                              fontname);
 
-    /* We also need the fontheight to configure our bars accordingly */
-    xcb_list_fonts_with_info_cookie_t cookie;
-    cookie = xcb_list_fonts_with_info(xcb_connection,
-                                      1,
-                                      strlen(fontname),
-                                      fontname);
+    /* We need to save info about the font, because we need the fonts height and
+     * information about the width of characters */
+    xcb_query_font_cookie_t query_font_cookie;
+    query_font_cookie = xcb_query_font(xcb_connection,
+                                       xcb_font);
+
+    /* To grab modifiers without blocking other applications from receiving key-events
+     * involving that modifier, we sadly have to use xkb which is not yet fully supported
+     * in xcb */
+    if (config.hide_on_modifier) {
+        int xkb_major, xkb_minor, xkb_errbase, xkb_err;
+        xkb_major = XkbMajorVersion;
+        xkb_minor = XkbMinorVersion;
+
+        xkb_dpy = XkbOpenDisplay(":0",
+                                 &xkb_event_base,
+                                 &xkb_errbase,
+                                 &xkb_major,
+                                 &xkb_minor,
+                                 &xkb_err);
+
+        if (xkb_dpy == NULL) {
+            ELOG("No XKB!\n");
+            exit(EXIT_FAILURE);
+        }
+
+        if (fcntl(ConnectionNumber(xkb_dpy), F_SETFD, FD_CLOEXEC) == -1) {
+            ELOG("Could not set FD_CLOEXEC on xkbdpy\n");
+            exit(EXIT_FAILURE);
+        }
+
+        int i1;
+        if (!XkbQueryExtension(xkb_dpy, &i1, &xkb_event_base, &xkb_errbase, &xkb_major, &xkb_minor)) {
+            ELOG("XKB not supported by X-server!\n");
+            exit(EXIT_FAILURE);
+        }
+
+        if (!XkbSelectEvents(xkb_dpy, XkbUseCoreKbd, XkbStateNotifyMask, XkbStateNotifyMask)) {
+            ELOG("Could not grab Key!\n");
+            exit(EXIT_FAILURE);
+        }
+
+        xkb_io = malloc(sizeof(ev_io));
+        ev_io_init(xkb_io, &xkb_io_cb, ConnectionNumber(xkb_dpy), EV_READ);
+        ev_io_start(main_loop, xkb_io);
+        XFlush(xkb_dpy);
+    }
+
+    /* We draw the statusline to a seperate pixmap, because it looks the same on all bars and
+     * this way, we can choose to crop it */
+    statusline_ctx = xcb_generate_id(xcb_connection);
+    uint32_t mask = XCB_GC_FOREGROUND |
+                    XCB_GC_BACKGROUND |
+                    XCB_GC_FONT;
+    uint32_t vals[3] = { colors.bar_fg, colors.bar_bg, xcb_font };
+
+    xcb_void_cookie_t sl_ctx_cookie = xcb_create_gc_checked(xcb_connection,
+                                                            statusline_ctx,
+                                                            xcb_root,
+                                                            mask,
+                                                            vals);
+
+    /* We only generate an id for the pixmap, because the width of it is dependent on the
+     * input we get */
+    statusline_pm = xcb_generate_id(xcb_connection);
 
     /* The varios Watchers to communicate with xcb */
     xcb_io = malloc(sizeof(ev_io));
@@ -250,23 +548,27 @@ void init_xcb(char *fontname) {
     /* Now we get the atoms and save them in a nice data-structure */
     get_atoms();
 
-    xcb_generic_error_t *err = xcb_request_check(xcb_connection,
-                                                 open_font_cookie);
+    /* Now we save the font-infos */
+    font_info = xcb_query_font_reply(xcb_connection,
+                                     query_font_cookie,
+                                     NULL);
+    font_height = font_info->font_ascent + font_info->font_descent;
 
-    if (err != NULL) {
-        printf("ERROR: Could not open font! XCB-Error-Code: %d\n", err->error_code);
+    if (xcb_request_failed(open_font_cookie, "Could not open font")) {
         exit(EXIT_FAILURE);
     }
 
-    /* Now we calculate the font-height */
-    xcb_list_fonts_with_info_reply_t *reply;
-    reply = xcb_list_fonts_with_info_reply(xcb_connection,
-                                           cookie,
-                                           NULL);
-    font_height = reply->font_ascent + reply->font_descent;
-    FREE(reply);
+    if (xcb_query_font_char_infos_length(font_info) == 0) {
+        font_table = NULL;
+    } else {
+        font_table = xcb_query_font_char_infos(font_info);
+    }
+
+    DLOG("Calculated Font-height: %d\n", font_height);
 
-    printf("Calculated Font-height: %d\n", font_height);
+    if (xcb_request_failed(sl_ctx_cookie, "Could not create context for statusline")) {
+        exit(EXIT_FAILURE);
+    }
 }
 
 /*
@@ -290,6 +592,7 @@ void clean_xcb() {
     FREE(xcb_chk);
     FREE(xcb_prep);
     FREE(xcb_io);
+    FREE(font_info);
 }
 
 /*
@@ -299,11 +602,15 @@ void clean_xcb() {
 void get_atoms() {
     xcb_intern_atom_reply_t *reply;
     #define ATOM_DO(name) reply = xcb_intern_atom_reply(xcb_connection, atom_cookies[name], NULL); \
+        if (reply == NULL) { \
+            ELOG("Could not get atom %s\n", #name); \
+            exit(EXIT_FAILURE); \
+        } \
         atoms[name] = reply->atom; \
         free(reply);
 
     #include "xcb_atoms.def"
-    printf("Got Atoms\n");
+    DLOG("Got Atoms\n");
 }
 
 /*
@@ -327,7 +634,7 @@ void destroy_window(i3_output *output) {
  */
 void reconfig_windows() {
     uint32_t mask;
-    uint32_t values[4];
+    uint32_t values[5];
 
     xcb_generic_error_t *err;
 
@@ -336,42 +643,45 @@ void reconfig_windows() {
         if (!walk->active) {
             /* If an output is not active, we destroy it's bar */
             /* FIXME: Maybe we rather want to unmap? */
-            printf("Destroying window for output %s\n", walk->name);
+            DLOG("Destroying window for output %s\n", walk->name);
             destroy_window(walk);
             continue;
         }
         if (walk->bar == XCB_NONE) {
-            printf("Creating Window for output %s\n", walk->name);
+            DLOG("Creating Window for output %s\n", walk->name);
 
             walk->bar = xcb_generate_id(xcb_connection);
             walk->buffer = xcb_generate_id(xcb_connection);
-            mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
+            mask = XCB_CW_BACK_PIXEL | XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK;
             /* Black background */
-            values[0] = xcb_screens->black_pixel;
+            values[0] = colors.bar_bg;
+            /* If hide_on_modifier is set, i3 is not supposed to manage our bar-windows */
+            values[1] = config.hide_on_modifier;
             /* The events we want to receive */
-            values[1] = XCB_EVENT_MASK_EXPOSURE |
+            values[2] = XCB_EVENT_MASK_EXPOSURE |
                         XCB_EVENT_MASK_BUTTON_PRESS;
-
             xcb_void_cookie_t win_cookie = xcb_create_window_checked(xcb_connection,
-                                                                     xcb_screens->root_depth,
+                                                                     xcb_screen->root_depth,
                                                                      walk->bar,
                                                                      xcb_root,
-                                                                     walk->rect.x, walk->rect.y,
+                                                                     walk->rect.x, walk->rect.y + walk->rect.h - font_height - 6,
                                                                      walk->rect.w, font_height + 6,
                                                                      1,
                                                                      XCB_WINDOW_CLASS_INPUT_OUTPUT,
-                                                                     xcb_screens->root_visual,
+                                                                     xcb_screen->root_visual,
                                                                      mask,
                                                                      values);
-            
+
+            /* The double-buffer we use to render stuff off-screen */
             xcb_void_cookie_t pm_cookie = xcb_create_pixmap_checked(xcb_connection,
-                                                                    xcb_screens->root_depth,
+                                                                    xcb_screen->root_depth,
                                                                     walk->buffer,
                                                                     walk->bar,
                                                                     walk->rect.w,
                                                                     walk->rect.h);
 
-            /* We want dock-windows (for now) */
+            /* We want dock-windows (for now). When override_redirect is set, i3 is ignoring
+             * this one */
             xcb_void_cookie_t prop_cookie = xcb_change_property(xcb_connection,
                                                                 XCB_PROP_MODE_REPLACE,
                                                                 walk->bar,
@@ -381,42 +691,28 @@ void reconfig_windows() {
                                                                 1,
                                                                 (unsigned char*) &atoms[_NET_WM_WINDOW_TYPE_DOCK]);
 
-            /* We also want a graphics-context (the "canvas" on which we draw) */
+            /* We also want a graphics-context for the bars (it defines the properties
+             * with which we draw to them) */
             walk->bargc = xcb_generate_id(xcb_connection);
             mask = XCB_GC_FONT;
             values[0] = xcb_font;
-
             xcb_void_cookie_t gc_cookie = xcb_create_gc_checked(xcb_connection,
                                                                 walk->bargc,
                                                                 walk->bar,
                                                                 mask,
                                                                 values);
 
-            /* We finally map the bar (display it on screen) */
-            xcb_void_cookie_t map_cookie = xcb_map_window_checked(xcb_connection, walk->bar);
-
-            if ((err = xcb_request_check(xcb_connection, win_cookie)) != NULL) {
-                printf("ERROR: Could not create Window. XCB-errorcode: %d\n", err->error_code);
-                exit(EXIT_FAILURE);
-            }
-
-            if ((err = xcb_request_check(xcb_connection, pm_cookie)) != NULL) {
-                printf("ERROR: Could not create Pixmap. XCB-errorcode: %d\n", err->error_code);
-                exit(EXIT_FAILURE);
-            }
-            if ((err = xcb_request_check(xcb_connection, prop_cookie)) != NULL) {
-                printf("ERROR: Could not set dock mode. XCB-errorcode: %d\n", err->error_code);
-                exit(EXIT_FAILURE);
-            }
-
-            if ((err = xcb_request_check(xcb_connection, gc_cookie)) != NULL) {
-                printf("ERROR: Could not create graphical context. XCB-errorcode: %d\n", err->error_code);
-                exit(EXIT_FAILURE);
+            /* We finally map the bar (display it on screen), unless the modifier-switch is on */
+            xcb_void_cookie_t map_cookie;
+            if (!config.hide_on_modifier) {
+                map_cookie = xcb_map_window_checked(xcb_connection, walk->bar);
             }
 
-            if ((err = xcb_request_check(xcb_connection, map_cookie)) != NULL) {
-                printf("ERROR: Could not map window. XCB-errorcode: %d\n", err->error_code);
+            if (xcb_request_failed(win_cookie,  "Could not create window") ||
+                xcb_request_failed(pm_cookie,   "Could not create pixmap") ||
+                xcb_request_failed(prop_cookie, "Could not set dock mode") ||
+                xcb_request_failed(gc_cookie,   "Could not create graphical context") ||
+                (!config.hide_on_modifier && xcb_request_failed(map_cookie, "Could not map window"))) {
                 exit(EXIT_FAILURE);
             }
         } else {
@@ -424,38 +720,22 @@ void reconfig_windows() {
             mask = XCB_CONFIG_WINDOW_X |
                    XCB_CONFIG_WINDOW_Y |
                    XCB_CONFIG_WINDOW_WIDTH |
-                   XCB_CONFIG_WINDOW_HEIGHT;
+                   XCB_CONFIG_WINDOW_HEIGHT |
+                   XCB_CONFIG_WINDOW_STACK_MODE;
             values[0] = walk->rect.x;
             values[1] = walk->rect.y + walk->rect.h - font_height - 6;
             values[2] = walk->rect.w;
             values[3] = font_height + 6;
-            printf("Reconfiguring Window for output %s to %d,%d\n", walk->name, values[0], values[1]);
-
+            values[4] = XCB_STACK_MODE_ABOVE;
+            DLOG("Reconfiguring Window for output %s to %d,%d\n", walk->name, values[0], values[1]);
             xcb_void_cookie_t cfg_cookie = xcb_configure_window_checked(xcb_connection,
                                                                         walk->bar,
                                                                         mask,
                                                                         values);
-
-            xcb_free_pixmap(xcb_connection, walk->buffer);
-            walk->buffer = xcb_generate_id(xcb_connection);
-
-            xcb_void_cookie_t pm_cookie = xcb_create_pixmap_checked(xcb_connection,
-                                                                    xcb_screens->root_depth,
-                                                                    walk->buffer,
-                                                                    walk->bar,
-                                                                    walk->rect.w,
-                                                                    walk->rect.h);
-
-            if ((err = xcb_request_check(xcb_connection, cfg_cookie)) != NULL) {
-                printf("ERROR: Could not reconfigure window. XCB-errorcode: %d\n", err->error_code);
+            if (xcb_request_failed(cfg_cookie, "Could not reconfigure window")) {
                 exit(EXIT_FAILURE);
             }
-
-            if ((err = xcb_request_check(xcb_connection, pm_cookie)) != NULL) {
-                printf("ERROR: Could not create Pixmap. XCB-errorcode: %d\n", err->error_code);
-                exit(EXIT_FAILURE);
-            }
-         }
+        }
     }
 }
 
@@ -464,18 +744,23 @@ void reconfig_windows() {
  *
  */
 void draw_bars() {
-    printf("Drawing Bars...\n");
+    DLOG("Drawing Bars...\n");
     int i = 0;
+
+    refresh_statusline();
+
     i3_output *outputs_walk;
     SLIST_FOREACH(outputs_walk, outputs, slist) {
         if (!outputs_walk->active) {
-            printf("Output %s inactive, skipping...\n", outputs_walk->name);
+            DLOG("Output %s inactive, skipping...\n", outputs_walk->name);
             continue;
         }
         if (outputs_walk->bar == XCB_NONE) {
+            /* Oh shit, an active output without an own bar. Create it now! */
             reconfig_windows();
         }
-        uint32_t color = get_colorpixel("000000");
+        /* First things first: clear the backbuffer */
+        uint32_t color = colors.bar_bg;
         xcb_change_gc(xcb_connection,
                       outputs_walk->bargc,
                       XCB_GC_FOREGROUND,
@@ -486,75 +771,78 @@ void draw_bars() {
                                 outputs_walk->bargc,
                                 1,
                                 &rect);
+
         if (statusline != NULL) {
-            printf("Printing statusline!\n");
-            xcb_change_gc(xcb_connection,
-                          outputs_walk->bargc,
-                          XCB_GC_BACKGROUND,
-                          &color);
-            color = get_colorpixel("FFFFFF");
-            xcb_change_gc(xcb_connection,
+            DLOG("Printing statusline!\n");
+
+            /* Luckily we already prepared a seperate pixmap containing the rendered
+             * statusline, we just have to copy the relevant parts to the relevant
+             * position */
+            xcb_copy_area(xcb_connection,
+                          statusline_pm,
+                          outputs_walk->buffer,
                           outputs_walk->bargc,
-                          XCB_GC_FOREGROUND,
-                          &color);
-
-            int glyph_count;
-            xcb_char2b_t *text = (xcb_char2b_t*) convert_utf8_to_ucs2(statusline, &glyph_count);
-
-            xcb_void_cookie_t cookie;
-            cookie = xcb_image_text_16(xcb_connection,
-                                       glyph_count,
-                                       outputs_walk->buffer,
-                                       outputs_walk->bargc,
-                                       outputs_walk->rect.w - get_string_width(text, glyph_count) - 4,
-                                       font_height + 1,
-                                       (xcb_char2b_t*) text);
-
-            xcb_generic_error_t *err = xcb_request_check(xcb_connection, cookie);
-
-            if (err != NULL) {
-                printf("XCB-Error: %d\n", err->error_code);
-            }
+                          MAX(0, (int16_t)(statusline_width - outputs_walk->rect.w + 4)), 0,
+                          MAX(0, (int16_t)(outputs_walk->rect.w - statusline_width - 4)), 3,
+                          MIN(outputs_walk->rect.w - 4, statusline_width), font_height);
         }
+
         i3_ws *ws_walk;
         TAILQ_FOREACH(ws_walk, outputs_walk->workspaces, tailq) {
-            printf("Drawing Button for WS %s at x = %d\n", ws_walk->name, i);
-            uint32_t color = get_colorpixel("240000");
+            DLOG("Drawing Button for WS %s at x = %d\n", ws_walk->name, i);
+            uint32_t fg_color = colors.inactive_ws_fg;
+            uint32_t bg_color = colors.inactive_ws_bg;
             if (ws_walk->visible) {
-                color = get_colorpixel("480000");
+                fg_color = colors.active_ws_fg;
+                bg_color = colors.active_ws_bg;
             }
             if (ws_walk->urgent) {
-                printf("WS %s is urgent!\n", ws_walk->name);
-                color = get_colorpixel("002400");
+                DLOG("WS %s is urgent!\n", ws_walk->name);
+                fg_color = colors.urgent_ws_fg;
+                bg_color = colors.urgent_ws_bg;
+                /* The urgent-hint should get noticed, so we unhide the bars shortly */
+                unhide_bars();
             }
             xcb_change_gc(xcb_connection,
                           outputs_walk->bargc,
                           XCB_GC_FOREGROUND,
-                          &color);
+                          &bg_color);
             xcb_change_gc(xcb_connection,
                           outputs_walk->bargc,
                           XCB_GC_BACKGROUND,
-                          &color);
+                          &bg_color);
             xcb_rectangle_t rect = { i + 1, 1, ws_walk->name_width + 8, font_height + 4 };
             xcb_poly_fill_rectangle(xcb_connection,
                                     outputs_walk->buffer,
                                     outputs_walk->bargc,
                                     1,
                                     &rect);
-            color = get_colorpixel("FFFFFF");
             xcb_change_gc(xcb_connection,
                           outputs_walk->bargc,
                           XCB_GC_FOREGROUND,
-                          &color);
+                          &fg_color);
             xcb_image_text_16(xcb_connection,
                               ws_walk->name_glyphs,
                               outputs_walk->buffer,
                               outputs_walk->bargc,
-                              i + 5, font_height + 1,
+                              i + 5, font_info->font_ascent + 2,
                               ws_walk->ucs2_name);
             i += 10 + ws_walk->name_width;
         }
 
+        redraw_bars();
+
+        i = 0;
+    }
+}
+
+/*
+ * Redraw the bars, i.e. simply copy the buffer to the barwindow
+ *
+ */
+void redraw_bars() {
+    i3_output *outputs_walk;
+    SLIST_FOREACH(outputs_walk, outputs, slist) {
         xcb_copy_area(xcb_connection,
                       outputs_walk->buffer,
                       outputs_walk->bar,
@@ -563,7 +851,6 @@ void draw_bars() {
                       0, 0,
                       outputs_walk->rect.w,
                       outputs_walk->rect.h);
-
-        i = 0;
+        xcb_flush(xcb_connection);
     }
 }