]> git.sur5r.net Git - i3/i3/commitdiff
Merge i3bar into next
authorMichael Stapelberg <michael@stapelberg.de>
Mon, 1 Aug 2011 13:55:21 +0000 (15:55 +0200)
committerMichael Stapelberg <michael@stapelberg.de>
Mon, 1 Aug 2011 13:55:27 +0000 (15:55 +0200)
26 files changed:
i3bar/.gitignore [new file with mode: 0644]
i3bar/CHANGELOG [new file with mode: 0644]
i3bar/LICENSE [new file with mode: 0644]
i3bar/Makefile [new file with mode: 0644]
i3bar/common.mk [new file with mode: 0644]
i3bar/doc/Makefile [new file with mode: 0644]
i3bar/doc/i3bar.man [new file with mode: 0644]
i3bar/include/child.h [new file with mode: 0644]
i3bar/include/common.h [new file with mode: 0644]
i3bar/include/config.h [new file with mode: 0644]
i3bar/include/ipc.h [new file with mode: 0644]
i3bar/include/outputs.h [new file with mode: 0644]
i3bar/include/queue.h [new file with mode: 0644]
i3bar/include/ucs2_to_utf8.h [new file with mode: 0644]
i3bar/include/util.h [new file with mode: 0644]
i3bar/include/workspaces.h [new file with mode: 0644]
i3bar/include/xcb.h [new file with mode: 0644]
i3bar/include/xcb_atoms.def [new file with mode: 0644]
i3bar/src/child.c [new file with mode: 0644]
i3bar/src/ipc.c [new file with mode: 0644]
i3bar/src/main.c [new file with mode: 0644]
i3bar/src/outputs.c [new file with mode: 0644]
i3bar/src/ucs2_to_utf8.c [new file with mode: 0644]
i3bar/src/workspaces.c [new file with mode: 0644]
i3bar/src/xcb.c [new file with mode: 0644]
i3bar/yajl-fallback/yajl/yajl_version.h [new file with mode: 0644]

diff --git a/i3bar/.gitignore b/i3bar/.gitignore
new file mode 100644 (file)
index 0000000..6aad070
--- /dev/null
@@ -0,0 +1,4 @@
+i3bar
+*.o
+core
+doc/i3bar.1
diff --git a/i3bar/CHANGELOG b/i3bar/CHANGELOG
new file mode 100644 (file)
index 0000000..32d880c
--- /dev/null
@@ -0,0 +1,30 @@
+v0.7
+=====
+- Make i3bar compatible with i3-4.0
+- Implement disabling the workspace buttons
+- Add Color for focused ws
+- Add support for I3_SOCKET_PATH-atom
+- Implement different dock-positions
+- Hide-on-modifier is now the default behavior
+- Change default socketpath to /tmp/i3-ipc.sock
+- Use I3SOCK environment-variable
+- Bugfix: Stop the reconn-timer before starting it again, else it's running twice
+- Bugfix: Don't SIGSTOP child in dockmode
+- Bugfix: If hide-on-modifier is set, stop the child after starting
+- Bugfix: Recover from closed socket
+- Some minor bugfixes
+
+v0.6
+=====
+- Add manpage
+- Implement hide-on-modifier
+- Custom colors can be set from the commandline
+- Use double-buffering
+- Bugfix: Correctly render long text
+- Bugfix: Don't segfault on SIGCHILD
+- Bugfix: Double-fork() to avoid zombies
+- Some minor bugfixes
+
+v0.5
+=====
+- Initial release
diff --git a/i3bar/LICENSE b/i3bar/LICENSE
new file mode 100644 (file)
index 0000000..6fe0f42
--- /dev/null
@@ -0,0 +1,27 @@
+Copyright (c) 2010-2011, Axel Wagner
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+
+    * Neither the name of Axel Wagner nor the
+      names of contributors may be used to endorse or promote products
+      derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY Axel Wagner ''AS IS'' AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL Axel Wagner BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/i3bar/Makefile b/i3bar/Makefile
new file mode 100644 (file)
index 0000000..216e68c
--- /dev/null
@@ -0,0 +1,37 @@
+TOPDIR=$(shell pwd)
+
+include $(TOPDIR)/common.mk
+
+FILES:=$(wildcard src/*.c)
+FILES:=$(FILES:.c=.o)
+HEADERS:=$(wildcard include/*.h)
+
+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) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
+
+install: all
+       echo "INSTALL"
+       $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin
+       $(INSTALL) -m 0755 i3bar $(DESTDIR)$(PREFIX)/bin
+
+clean:
+       rm -f src/*.o
+       make -C doc clean
+
+distclean: clean
+       rm -f i3bar
+       make -C doc distclean
+
+.PHONY: install clean distclean doc
diff --git a/i3bar/common.mk b/i3bar/common.mk
new file mode 100644 (file)
index 0000000..ec63300
--- /dev/null
@@ -0,0 +1,35 @@
+INSTALL=install
+ifndef DEBUG
+DEBUG=1
+endif
+PREFIX=/usr
+
+# The escaping is absurd, but we need to escape for shell, sed, make, define
+GIT_VERSION:="$(shell git describe --tags --always) ($(shell git log --pretty=format:%cd --date=short -n1), branch $(shell [ -f .git/HEAD ] && sed 's/ref: refs\/heads\/\(.*\)/\\\\\\"\1\\\\\\"/g' .git/HEAD || echo 'unknown'))"
+
+# Fallback for libyajl 1 which did not include yajl_version.h. We need
+# YAJL_MAJOR from that file to decide which code path should be used.
+CFLAGS += -idirafter yajl-fallback
+
+CFLAGS += -Wall
+CFLAGS += -pipe
+CFLAGS += -g
+
+CPPFLAGS += -DI3BAR_VERSION=\"${GIT_VERSION}\"
+CPPFLAGS += -Iinclude
+
+LDFLAGS += -lev
+LDFLAGS += -lyajl
+LDFLAGS += -lxcb
+LDFLAGS += -lX11
+LDFLAGS += -L/usr/local/lib
+
+ifeq ($(DEBUG),1)
+CFLAGS += -g3
+else
+CFLAGS += -O2
+endif
+
+.SILENT:
+
+.PHONY: install clean
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..5faa16c
--- /dev/null
@@ -0,0 +1,90 @@
+i3bar(1)
+========
+Axel Wagner <mail+i3bar@merovius.de>
+v0.7, July 2011
+
+== NAME
+
+i3bar - xcb-based status- and workspace-bar
+
+== SYNOPSIS
+
+*i3bar* [*-s* 'sock_path'] [*-c* 'command'] [*-m*|*-d*['pos']] [*-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 '/tmp/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.
+This is the default behavior of i3bar.
+
+*-d*['pos']*, --dock*[*=*'pos']::
+Put i3bar in dockmode. This will reserve some space for it, so it does not overlap other clients.
+You can specify either *bottom* (default) or *top* as 'pos'.
+
+*-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 its config file.
+
+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, --color-focus-ws-fg, --color-focus-ws-bg*
+
+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.
+
+== ENVIRONMENT
+
+=== I3SOCK
+
+If no ipc-socket is specified on the commandline, this variable is used
+to determine the path, at wich the unix domain socket is expected, on which
+to connect to i3.
+
+== EXAMPLES
+
+To get a docked bar with some statusinformation, you use
+
+*i3status | i3bar --dock*
+
+If you rather have it displayed at the top of the screen, you use
+
+*i3status | i3bar --dock=top*
+
+If you want it to hide when not needed, you should instead simply use
+
+*i3bar -c i3status*
+
+== SEE ALSO
+
++i3(1)+, +i3-wsbar(1)+, +dzen2(1)+, +i3status(1)+
+
+== AUTHORS
+
+Axel Wagner and contributors
diff --git a/i3bar/include/child.h b/i3bar/include/child.h
new file mode 100644 (file)
index 0000000..2622b3b
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * i3bar - an xcb-based status- and ws-bar for i3
+ *
+ * © 2010-2011 Axel Wagner and contributors
+ *
+ * See file LICNSE for license information
+ *
+ */
+#ifndef CHILD_H_
+#define CHILD_H_
+
+#define STDIN_CHUNK_SIZE 1024
+
+/*
+ * 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
+ *
+ */
+void start_child(char *command);
+
+/*
+ * kill()s the child-prozess (if existend) and closes and
+ * free()s the stdin- and sigchild-watchers
+ *
+ */
+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
diff --git a/i3bar/include/common.h b/i3bar/include/common.h
new file mode 100644 (file)
index 0000000..22e3ca4
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * i3bar - an xcb-based status- and ws-bar for i3
+ *
+ * © 2010-2011 Axel Wagner and contributors
+ *
+ * See file LICNSE for license information
+ *
+ */
+#ifndef COMMON_H_
+#define COMMON_H_
+
+typedef struct rect_t rect;
+typedef int bool;
+
+struct ev_loop* main_loop;
+char            *statusline;
+char            *statusline_buffer;
+
+struct rect_t {
+       int     x;
+       int     y;
+       int     w;
+       int     h;
+};
+
+#include "queue.h"
+#include "child.h"
+#include "ipc.h"
+#include "outputs.h"
+#include "util.h"
+#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..b347391
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef CONFIG_H_
+#define CONFIG_H_
+
+#include "common.h"
+
+typedef enum {
+    DOCKPOS_NONE = 0,
+    DOCKPOS_TOP,
+    DOCKPOS_BOT
+} dockpos_t;
+
+typedef struct config_t {
+    int          hide_on_modifier;
+    dockpos_t    dockpos;
+    int          verbose;
+    xcb_colors_t *colors;
+    int          disable_ws;
+} config_t;
+
+config_t config;
+
+#endif
diff --git a/i3bar/include/ipc.h b/i3bar/include/ipc.h
new file mode 100644 (file)
index 0000000..35f2d0f
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * i3bar - an xcb-based status- and ws-bar for i3
+ *
+ * © 2010-2011 Axel Wagner and contributors
+ *
+ * See file LICNSE for license information
+ *
+ */
+#ifndef IPC_H_
+#define IPC_H_
+
+#include <stdint.h>
+
+/*
+ * Initiate a connection to i3.
+ * socket-path must be a valid path to the ipc_socket of i3
+ *
+ */
+int init_connection(const char *socket_path);
+
+/*
+ * Destroy the connection to i3.
+ *
+ */
+void destroy_connection();
+
+/*
+ * Sends a Message to i3.
+ * type must be a valid I3_IPC_MESSAGE_TYPE (see i3/ipc.h for further information)
+ *
+ */
+int i3_send_msg(uint32_t type, const char* payload);
+
+/*
+ * Subscribe to all the i3-events, we need
+ *
+ */
+void subscribe_events();
+
+#endif
diff --git a/i3bar/include/outputs.h b/i3bar/include/outputs.h
new file mode 100644 (file)
index 0000000..f74048d
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * i3bar - an xcb-based status- and ws-bar for i3
+ *
+ * © 2010-2011 Axel Wagner and contributors
+ *
+ * See file LICNSE for license information
+ *
+ */
+#ifndef OUTPUTS_H_
+#define OUTPUTS_H_
+
+#include <xcb/xcb.h>
+
+#include "common.h"
+
+typedef struct i3_output i3_output;
+
+SLIST_HEAD(outputs_head, i3_output);
+struct outputs_head *outputs;
+
+/*
+ * Start parsing the received json-string
+ *
+ */
+void        parse_outputs_json(char* json);
+
+/*
+ * Initiate the output-list
+ *
+ */
+void        init_outputs();
+
+/*
+ * Returns the output with the given name
+ *
+ */
+i3_output*  get_output_by_name(char* name);
+
+struct i3_output {
+       char*           name;         /* Name of the output */
+       bool            active;       /* If the output is active */
+       int             ws;           /* The number of the currently visible ws */
+       rect            rect;         /* The rect (relative to the root-win) */
+
+       xcb_window_t    bar;          /* The id of the bar of the output */
+    xcb_pixmap_t    buffer;       /* An extra pixmap for double-buffering */
+       xcb_gcontext_t  bargc;        /* The graphical context of the bar */
+
+       struct ws_head  *workspaces;  /* The workspaces on this output */
+
+       SLIST_ENTRY(i3_output) slist; /* Pointer for the SLIST-Macro */
+};
+
+#endif
diff --git a/i3bar/include/queue.h b/i3bar/include/queue.h
new file mode 100644 (file)
index 0000000..75bb957
--- /dev/null
@@ -0,0 +1,527 @@
+/*     $OpenBSD: queue.h,v 1.1 2007/10/26 03:14:08 niallo Exp $        */
+/*     $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $       */
+
+/*
+ * Copyright (c) 1991, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)queue.h     8.5 (Berkeley) 8/20/94
+ */
+
+#ifndef        _SYS_QUEUE_H_
+#define        _SYS_QUEUE_H_
+
+/*
+ * This file defines five types of data structures: singly-linked lists,
+ * lists, simple queues, tail queues, and circular queues.
+ *
+ *
+ * A singly-linked list is headed by a single forward pointer. The elements
+ * are singly linked for minimum space and pointer manipulation overhead at
+ * the expense of O(n) removal for arbitrary elements. New elements can be
+ * added to the list after an existing element or at the head of the list.
+ * Elements being removed from the head of the list should use the explicit
+ * macro for this purpose for optimum efficiency. A singly-linked list may
+ * only be traversed in the forward direction.  Singly-linked lists are ideal
+ * for applications with large datasets and few or no removals or for
+ * implementing a LIFO queue.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before
+ * or after an existing element or at the head of the list. A list
+ * may only be traversed in the forward direction.
+ *
+ * A simple queue is headed by a pair of pointers, one the head of the
+ * list and the other to the tail of the list. The elements are singly
+ * linked to save space, so elements can only be removed from the
+ * head of the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the
+ * list. A simple queue may only be traversed in the forward direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or
+ * after an existing element, at the head of the list, or at the end of
+ * the list. A tail queue may be traversed in either direction.
+ *
+ * A circle queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the list.
+ * A circle queue may be traversed in either direction, but has a more
+ * complex end of list detection.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ */
+
+#if defined(QUEUE_MACRO_DEBUG) || (defined(_KERNEL) && defined(DIAGNOSTIC))
+#define _Q_INVALIDATE(a) (a) = ((void *)-1)
+#else
+#define _Q_INVALIDATE(a)
+#endif
+
+/*
+ * Singly-linked List definitions.
+ */
+#define SLIST_HEAD(name, type)                                         \
+struct name {                                                          \
+       struct type *slh_first; /* first element */                     \
+}
+
+#define        SLIST_HEAD_INITIALIZER(head)                                    \
+       { NULL }
+
+#define SLIST_ENTRY(type)                                              \
+struct {                                                               \
+       struct type *sle_next;  /* next element */                      \
+}
+
+/*
+ * Singly-linked List access methods.
+ */
+#define        SLIST_FIRST(head)       ((head)->slh_first)
+#define        SLIST_END(head)         NULL
+#define        SLIST_EMPTY(head)       (SLIST_FIRST(head) == SLIST_END(head))
+#define        SLIST_NEXT(elm, field)  ((elm)->field.sle_next)
+
+#define        SLIST_FOREACH(var, head, field)                                 \
+       for((var) = SLIST_FIRST(head);                                  \
+           (var) != SLIST_END(head);                                   \
+           (var) = SLIST_NEXT(var, field))
+
+#define        SLIST_FOREACH_PREVPTR(var, varp, head, field)                   \
+       for ((varp) = &SLIST_FIRST((head));                             \
+           ((var) = *(varp)) != SLIST_END(head);                       \
+           (varp) = &SLIST_NEXT((var), field))
+
+/*
+ * Singly-linked List functions.
+ */
+#define        SLIST_INIT(head) {                                              \
+       SLIST_FIRST(head) = SLIST_END(head);                            \
+}
+
+#define        SLIST_INSERT_AFTER(slistelm, elm, field) do {                   \
+       (elm)->field.sle_next = (slistelm)->field.sle_next;             \
+       (slistelm)->field.sle_next = (elm);                             \
+} while (0)
+
+#define        SLIST_INSERT_HEAD(head, elm, field) do {                        \
+       (elm)->field.sle_next = (head)->slh_first;                      \
+       (head)->slh_first = (elm);                                      \
+} while (0)
+
+#define        SLIST_REMOVE_NEXT(head, elm, field) do {                        \
+       (elm)->field.sle_next = (elm)->field.sle_next->field.sle_next;  \
+} while (0)
+
+#define        SLIST_REMOVE_HEAD(head, field) do {                             \
+       (head)->slh_first = (head)->slh_first->field.sle_next;          \
+} while (0)
+
+#define SLIST_REMOVE(head, elm, type, field) do {                      \
+       if ((head)->slh_first == (elm)) {                               \
+               SLIST_REMOVE_HEAD((head), field);                       \
+       } else {                                                        \
+               struct type *curelm = (head)->slh_first;                \
+                                                                       \
+               while (curelm->field.sle_next != (elm))                 \
+                       curelm = curelm->field.sle_next;                \
+               curelm->field.sle_next =                                \
+                   curelm->field.sle_next->field.sle_next;             \
+               _Q_INVALIDATE((elm)->field.sle_next);                   \
+       }                                                               \
+} while (0)
+
+/*
+ * List definitions.
+ */
+#define LIST_HEAD(name, type)                                          \
+struct name {                                                          \
+       struct type *lh_first;  /* first element */                     \
+}
+
+#define LIST_HEAD_INITIALIZER(head)                                    \
+       { NULL }
+
+#define LIST_ENTRY(type)                                               \
+struct {                                                               \
+       struct type *le_next;   /* next element */                      \
+       struct type **le_prev;  /* address of previous next element */  \
+}
+
+/*
+ * List access methods
+ */
+#define        LIST_FIRST(head)                ((head)->lh_first)
+#define        LIST_END(head)                  NULL
+#define        LIST_EMPTY(head)                (LIST_FIRST(head) == LIST_END(head))
+#define        LIST_NEXT(elm, field)           ((elm)->field.le_next)
+
+#define LIST_FOREACH(var, head, field)                                 \
+       for((var) = LIST_FIRST(head);                                   \
+           (var)!= LIST_END(head);                                     \
+           (var) = LIST_NEXT(var, field))
+
+/*
+ * List functions.
+ */
+#define        LIST_INIT(head) do {                                            \
+       LIST_FIRST(head) = LIST_END(head);                              \
+} while (0)
+
+#define LIST_INSERT_AFTER(listelm, elm, field) do {                    \
+       if (((elm)->field.le_next = (listelm)->field.le_next) != NULL)  \
+               (listelm)->field.le_next->field.le_prev =               \
+                   &(elm)->field.le_next;                              \
+       (listelm)->field.le_next = (elm);                               \
+       (elm)->field.le_prev = &(listelm)->field.le_next;               \
+} while (0)
+
+#define        LIST_INSERT_BEFORE(listelm, elm, field) do {                    \
+       (elm)->field.le_prev = (listelm)->field.le_prev;                \
+       (elm)->field.le_next = (listelm);                               \
+       *(listelm)->field.le_prev = (elm);                              \
+       (listelm)->field.le_prev = &(elm)->field.le_next;               \
+} while (0)
+
+#define LIST_INSERT_HEAD(head, elm, field) do {                                \
+       if (((elm)->field.le_next = (head)->lh_first) != NULL)          \
+               (head)->lh_first->field.le_prev = &(elm)->field.le_next;\
+       (head)->lh_first = (elm);                                       \
+       (elm)->field.le_prev = &(head)->lh_first;                       \
+} while (0)
+
+#define LIST_REMOVE(elm, field) do {                                   \
+       if ((elm)->field.le_next != NULL)                               \
+               (elm)->field.le_next->field.le_prev =                   \
+                   (elm)->field.le_prev;                               \
+       *(elm)->field.le_prev = (elm)->field.le_next;                   \
+       _Q_INVALIDATE((elm)->field.le_prev);                            \
+       _Q_INVALIDATE((elm)->field.le_next);                            \
+} while (0)
+
+#define LIST_REPLACE(elm, elm2, field) do {                            \
+       if (((elm2)->field.le_next = (elm)->field.le_next) != NULL)     \
+               (elm2)->field.le_next->field.le_prev =                  \
+                   &(elm2)->field.le_next;                             \
+       (elm2)->field.le_prev = (elm)->field.le_prev;                   \
+       *(elm2)->field.le_prev = (elm2);                                \
+       _Q_INVALIDATE((elm)->field.le_prev);                            \
+       _Q_INVALIDATE((elm)->field.le_next);                            \
+} while (0)
+
+/*
+ * Simple queue definitions.
+ */
+#define SIMPLEQ_HEAD(name, type)                                       \
+struct name {                                                          \
+       struct type *sqh_first; /* first element */                     \
+       struct type **sqh_last; /* addr of last next element */         \
+}
+
+#define SIMPLEQ_HEAD_INITIALIZER(head)                                 \
+       { NULL, &(head).sqh_first }
+
+#define SIMPLEQ_ENTRY(type)                                            \
+struct {                                                               \
+       struct type *sqe_next;  /* next element */                      \
+}
+
+/*
+ * Simple queue access methods.
+ */
+#define        SIMPLEQ_FIRST(head)         ((head)->sqh_first)
+#define        SIMPLEQ_END(head)           NULL
+#define        SIMPLEQ_EMPTY(head)         (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head))
+#define        SIMPLEQ_NEXT(elm, field)    ((elm)->field.sqe_next)
+
+#define SIMPLEQ_FOREACH(var, head, field)                              \
+       for((var) = SIMPLEQ_FIRST(head);                                \
+           (var) != SIMPLEQ_END(head);                                 \
+           (var) = SIMPLEQ_NEXT(var, field))
+
+/*
+ * Simple queue functions.
+ */
+#define        SIMPLEQ_INIT(head) do {                                         \
+       (head)->sqh_first = NULL;                                       \
+       (head)->sqh_last = &(head)->sqh_first;                          \
+} while (0)
+
+#define SIMPLEQ_INSERT_HEAD(head, elm, field) do {                     \
+       if (((elm)->field.sqe_next = (head)->sqh_first) == NULL)        \
+               (head)->sqh_last = &(elm)->field.sqe_next;              \
+       (head)->sqh_first = (elm);                                      \
+} while (0)
+
+#define SIMPLEQ_INSERT_TAIL(head, elm, field) do {                     \
+       (elm)->field.sqe_next = NULL;                                   \
+       *(head)->sqh_last = (elm);                                      \
+       (head)->sqh_last = &(elm)->field.sqe_next;                      \
+} while (0)
+
+#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do {           \
+       if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
+               (head)->sqh_last = &(elm)->field.sqe_next;              \
+       (listelm)->field.sqe_next = (elm);                              \
+} while (0)
+
+#define SIMPLEQ_REMOVE_HEAD(head, field) do {                  \
+       if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
+               (head)->sqh_last = &(head)->sqh_first;                  \
+} while (0)
+
+/*
+ * Tail queue definitions.
+ */
+#define TAILQ_HEAD(name, type)                                         \
+struct name {                                                          \
+       struct type *tqh_first; /* first element */                     \
+       struct type **tqh_last; /* addr of last next element */         \
+}
+
+#define TAILQ_HEAD_INITIALIZER(head)                                   \
+       { NULL, &(head).tqh_first }
+
+#define TAILQ_ENTRY(type)                                              \
+struct {                                                               \
+       struct type *tqe_next;  /* next element */                      \
+       struct type **tqe_prev; /* address of previous next element */  \
+}
+
+/*
+ * tail queue access methods
+ */
+#define        TAILQ_FIRST(head)               ((head)->tqh_first)
+#define        TAILQ_END(head)                 NULL
+#define        TAILQ_NEXT(elm, field)          ((elm)->field.tqe_next)
+#define TAILQ_LAST(head, headname)                                     \
+       (*(((struct headname *)((head)->tqh_last))->tqh_last))
+/* XXX */
+#define TAILQ_PREV(elm, headname, field)                               \
+       (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+#define        TAILQ_EMPTY(head)                                               \
+       (TAILQ_FIRST(head) == TAILQ_END(head))
+
+#define TAILQ_FOREACH(var, head, field)                                        \
+       for((var) = TAILQ_FIRST(head);                                  \
+           (var) != TAILQ_END(head);                                   \
+           (var) = TAILQ_NEXT(var, field))
+
+#define TAILQ_FOREACH_REVERSE(var, head, headname, field)              \
+       for((var) = TAILQ_LAST(head, headname);                         \
+           (var) != TAILQ_END(head);                                   \
+           (var) = TAILQ_PREV(var, headname, field))
+
+/*
+ * Tail queue functions.
+ */
+#define        TAILQ_INIT(head) do {                                           \
+       (head)->tqh_first = NULL;                                       \
+       (head)->tqh_last = &(head)->tqh_first;                          \
+} while (0)
+
+#define TAILQ_INSERT_HEAD(head, elm, field) do {                       \
+       if (((elm)->field.tqe_next = (head)->tqh_first) != NULL)        \
+               (head)->tqh_first->field.tqe_prev =                     \
+                   &(elm)->field.tqe_next;                             \
+       else                                                            \
+               (head)->tqh_last = &(elm)->field.tqe_next;              \
+       (head)->tqh_first = (elm);                                      \
+       (elm)->field.tqe_prev = &(head)->tqh_first;                     \
+} while (0)
+
+#define TAILQ_INSERT_TAIL(head, elm, field) do {                       \
+       (elm)->field.tqe_next = NULL;                                   \
+       (elm)->field.tqe_prev = (head)->tqh_last;                       \
+       *(head)->tqh_last = (elm);                                      \
+       (head)->tqh_last = &(elm)->field.tqe_next;                      \
+} while (0)
+
+#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do {             \
+       if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
+               (elm)->field.tqe_next->field.tqe_prev =                 \
+                   &(elm)->field.tqe_next;                             \
+       else                                                            \
+               (head)->tqh_last = &(elm)->field.tqe_next;              \
+       (listelm)->field.tqe_next = (elm);                              \
+       (elm)->field.tqe_prev = &(listelm)->field.tqe_next;             \
+} while (0)
+
+#define        TAILQ_INSERT_BEFORE(listelm, elm, field) do {                   \
+       (elm)->field.tqe_prev = (listelm)->field.tqe_prev;              \
+       (elm)->field.tqe_next = (listelm);                              \
+       *(listelm)->field.tqe_prev = (elm);                             \
+       (listelm)->field.tqe_prev = &(elm)->field.tqe_next;             \
+} while (0)
+
+#define TAILQ_REMOVE(head, elm, field) do {                            \
+       if (((elm)->field.tqe_next) != NULL)                            \
+               (elm)->field.tqe_next->field.tqe_prev =                 \
+                   (elm)->field.tqe_prev;                              \
+       else                                                            \
+               (head)->tqh_last = (elm)->field.tqe_prev;               \
+       *(elm)->field.tqe_prev = (elm)->field.tqe_next;                 \
+       _Q_INVALIDATE((elm)->field.tqe_prev);                           \
+       _Q_INVALIDATE((elm)->field.tqe_next);                           \
+} while (0)
+
+#define TAILQ_REPLACE(head, elm, elm2, field) do {                     \
+       if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL)   \
+               (elm2)->field.tqe_next->field.tqe_prev =                \
+                   &(elm2)->field.tqe_next;                            \
+       else                                                            \
+               (head)->tqh_last = &(elm2)->field.tqe_next;             \
+       (elm2)->field.tqe_prev = (elm)->field.tqe_prev;                 \
+       *(elm2)->field.tqe_prev = (elm2);                               \
+       _Q_INVALIDATE((elm)->field.tqe_prev);                           \
+       _Q_INVALIDATE((elm)->field.tqe_next);                           \
+} while (0)
+
+/*
+ * Circular queue definitions.
+ */
+#define CIRCLEQ_HEAD(name, type)                                       \
+struct name {                                                          \
+       struct type *cqh_first;         /* first element */             \
+       struct type *cqh_last;          /* last element */              \
+}
+
+#define CIRCLEQ_HEAD_INITIALIZER(head)                                 \
+       { CIRCLEQ_END(&head), CIRCLEQ_END(&head) }
+
+#define CIRCLEQ_ENTRY(type)                                            \
+struct {                                                               \
+       struct type *cqe_next;          /* next element */              \
+       struct type *cqe_prev;          /* previous element */          \
+}
+
+/*
+ * Circular queue access methods
+ */
+#define        CIRCLEQ_FIRST(head)             ((head)->cqh_first)
+#define        CIRCLEQ_LAST(head)              ((head)->cqh_last)
+#define        CIRCLEQ_END(head)               ((void *)(head))
+#define        CIRCLEQ_NEXT(elm, field)        ((elm)->field.cqe_next)
+#define        CIRCLEQ_PREV(elm, field)        ((elm)->field.cqe_prev)
+#define        CIRCLEQ_EMPTY(head)                                             \
+       (CIRCLEQ_FIRST(head) == CIRCLEQ_END(head))
+
+#define CIRCLEQ_FOREACH(var, head, field)                              \
+       for((var) = CIRCLEQ_FIRST(head);                                \
+           (var) != CIRCLEQ_END(head);                                 \
+           (var) = CIRCLEQ_NEXT(var, field))
+
+#define CIRCLEQ_FOREACH_REVERSE(var, head, field)                      \
+       for((var) = CIRCLEQ_LAST(head);                                 \
+           (var) != CIRCLEQ_END(head);                                 \
+           (var) = CIRCLEQ_PREV(var, field))
+
+/*
+ * Circular queue functions.
+ */
+#define        CIRCLEQ_INIT(head) do {                                         \
+       (head)->cqh_first = CIRCLEQ_END(head);                          \
+       (head)->cqh_last = CIRCLEQ_END(head);                           \
+} while (0)
+
+#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do {           \
+       (elm)->field.cqe_next = (listelm)->field.cqe_next;              \
+       (elm)->field.cqe_prev = (listelm);                              \
+       if ((listelm)->field.cqe_next == CIRCLEQ_END(head))             \
+               (head)->cqh_last = (elm);                               \
+       else                                                            \
+               (listelm)->field.cqe_next->field.cqe_prev = (elm);      \
+       (listelm)->field.cqe_next = (elm);                              \
+} while (0)
+
+#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do {          \
+       (elm)->field.cqe_next = (listelm);                              \
+       (elm)->field.cqe_prev = (listelm)->field.cqe_prev;              \
+       if ((listelm)->field.cqe_prev == CIRCLEQ_END(head))             \
+               (head)->cqh_first = (elm);                              \
+       else                                                            \
+               (listelm)->field.cqe_prev->field.cqe_next = (elm);      \
+       (listelm)->field.cqe_prev = (elm);                              \
+} while (0)
+
+#define CIRCLEQ_INSERT_HEAD(head, elm, field) do {                     \
+       (elm)->field.cqe_next = (head)->cqh_first;                      \
+       (elm)->field.cqe_prev = CIRCLEQ_END(head);                      \
+       if ((head)->cqh_last == CIRCLEQ_END(head))                      \
+               (head)->cqh_last = (elm);                               \
+       else                                                            \
+               (head)->cqh_first->field.cqe_prev = (elm);              \
+       (head)->cqh_first = (elm);                                      \
+} while (0)
+
+#define CIRCLEQ_INSERT_TAIL(head, elm, field) do {                     \
+       (elm)->field.cqe_next = CIRCLEQ_END(head);                      \
+       (elm)->field.cqe_prev = (head)->cqh_last;                       \
+       if ((head)->cqh_first == CIRCLEQ_END(head))                     \
+               (head)->cqh_first = (elm);                              \
+       else                                                            \
+               (head)->cqh_last->field.cqe_next = (elm);               \
+       (head)->cqh_last = (elm);                                       \
+} while (0)
+
+#define        CIRCLEQ_REMOVE(head, elm, field) do {                           \
+       if ((elm)->field.cqe_next == CIRCLEQ_END(head))                 \
+               (head)->cqh_last = (elm)->field.cqe_prev;               \
+       else                                                            \
+               (elm)->field.cqe_next->field.cqe_prev =                 \
+                   (elm)->field.cqe_prev;                              \
+       if ((elm)->field.cqe_prev == CIRCLEQ_END(head))                 \
+               (head)->cqh_first = (elm)->field.cqe_next;              \
+       else                                                            \
+               (elm)->field.cqe_prev->field.cqe_next =                 \
+                   (elm)->field.cqe_next;                              \
+       _Q_INVALIDATE((elm)->field.cqe_prev);                           \
+       _Q_INVALIDATE((elm)->field.cqe_next);                           \
+} while (0)
+
+#define CIRCLEQ_REPLACE(head, elm, elm2, field) do {                   \
+       if (((elm2)->field.cqe_next = (elm)->field.cqe_next) ==         \
+           CIRCLEQ_END(head))                                          \
+               (head)->cqh_last = (elm2);                              \
+       else                                                            \
+               (elm2)->field.cqe_next->field.cqe_prev = (elm2);        \
+       if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) ==         \
+           CIRCLEQ_END(head))                                          \
+               (head)->cqh_first = (elm2);                             \
+       else                                                            \
+               (elm2)->field.cqe_prev->field.cqe_next = (elm2);        \
+       _Q_INVALIDATE((elm)->field.cqe_prev);                           \
+       _Q_INVALIDATE((elm)->field.cqe_next);                           \
+} while (0)
+
+#endif /* !_SYS_QUEUE_H_ */
diff --git a/i3bar/include/ucs2_to_utf8.h b/i3bar/include/ucs2_to_utf8.h
new file mode 100644 (file)
index 0000000..2fb3862
--- /dev/null
@@ -0,0 +1 @@
+char *convert_utf8_to_ucs2(char *input, int *real_strlen);
diff --git a/i3bar/include/util.h b/i3bar/include/util.h
new file mode 100644 (file)
index 0000000..6094932
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * i3bar - an xcb-based status- and ws-bar for i3
+ *
+ * © 2010-2011 Axel Wagner and contributors
+ *
+ * See file LICNSE for license information
+ *
+ */
+#ifndef UTIL_H_
+#define UTIL_H_
+
+#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) { \
+        free(p); \
+        p = NULL; \
+    } \
+} while (0)
+
+/* Securely fee single-linked list */
+#define FREE_SLIST(l, type) do { \
+    type *walk = SLIST_FIRST(l); \
+    while (!SLIST_EMPTY(l)) { \
+        SLIST_REMOVE_HEAD(l, slist); \
+        FREE(walk); \
+        walk = SLIST_FIRST(l); \
+    } \
+} while (0)
+
+#endif
+
+/* Securely fee tail-queues */
+#define FREE_TAILQ(l, type) do { \
+    type *walk = TAILQ_FIRST(l); \
+    while (!TAILQ_EMPTY(l)) { \
+        TAILQ_REMOVE(l, TAILQ_FIRST(l), tailq); \
+        FREE(walk); \
+        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)
diff --git a/i3bar/include/workspaces.h b/i3bar/include/workspaces.h
new file mode 100644 (file)
index 0000000..2055d8b
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * i3bar - an xcb-based status- and ws-bar for i3
+ *
+ * © 2010-2011 Axel Wagner and contributors
+ *
+ * See file LICNSE for license information
+ *
+ */
+#ifndef WORKSPACES_H_
+#define WORKSPACES_H_
+
+#include <xcb/xproto.h>
+
+#include "common.h"
+
+typedef struct i3_ws i3_ws;
+
+TAILQ_HEAD(ws_head, i3_ws);
+
+/*
+ * Start parsing the received json-string
+ *
+ */
+void parse_workspaces_json();
+
+/*
+ * free() all workspace data-structures
+ *
+ */
+void free_workspaces();
+
+struct i3_ws {
+    int                num;         /* The internal number of the ws */
+    char               *name;       /* The name (in utf8) of the ws */
+    xcb_char2b_t       *ucs2_name;  /* The name (in ucs2) of the ws */
+    int                name_glyphs; /* The length (in glyphs) of the name */
+    int                name_width;  /* The rendered width of the name */
+    bool               visible;     /* If the ws is currently visible on an output */
+    bool               focused;     /* If the ws is currently focused */
+    bool               urgent;      /* If the urgent-hint of the ws is set */
+    rect               rect;        /* The rect of the ws (not used (yet)) */
+    struct i3_output   *output;     /* The current output of the ws */
+
+    TAILQ_ENTRY(i3_ws) tailq;       /* Pointer for the TAILQ-Macro */
+};
+
+#endif
diff --git a/i3bar/include/xcb.h b/i3bar/include/xcb.h
new file mode 100644 (file)
index 0000000..a792429
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * i3bar - an xcb-based status- and ws-bar for i3
+ *
+ * © 2010-2011 Axel Wagner and contributors
+ *
+ * See file LICNSE for license information
+ *
+ */
+#ifndef XCB_H_
+#define XCB_H_
+
+#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 *focus_ws_bg;
+    char *focus_ws_fg;
+    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
+ *
+ */
+char *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.
+ *
+ */
+void clean_xcb();
+
+/*
+ * Get the earlier requested atoms and save them in the prepared data-structure
+ *
+ */
+void get_atoms();
+
+/*
+ * Destroy the bar of the specified output
+ *
+ */
+void destroy_window(i3_output *output);
+
+/*
+ * Reallocate the statusline-buffer
+ *
+ */
+void realloc_sl_buffer();
+
+/*
+ * Reconfigure all bars and create new for newly activated outputs
+ *
+ */
+void reconfig_windows();
+
+/*
+ * Render the bars, with buttons and statusline
+ *
+ */
+void draw_bars();
+
+/*
+ * 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 glyphs).
+ *
+ */
+uint32_t predict_text_extents(xcb_char2b_t *text, uint32_t length);
+
+#endif
diff --git a/i3bar/include/xcb_atoms.def b/i3bar/include/xcb_atoms.def
new file mode 100644 (file)
index 0000000..5d16887
--- /dev/null
@@ -0,0 +1,5 @@
+ATOM_DO(_NET_WM_WINDOW_TYPE)
+ATOM_DO(_NET_WM_WINDOW_TYPE_DOCK)
+ATOM_DO(_NET_WM_STRUT_PARTIAL)
+ATOM_DO(I3_SOCKET_PATH)
+#undef ATOM_DO
diff --git a/i3bar/src/child.c b/i3bar/src/child.c
new file mode 100644 (file)
index 0000000..3f59d06
--- /dev/null
@@ -0,0 +1,212 @@
+/*
+ * i3bar - an xcb-based status- and ws-bar for i3
+ *
+ * © 2010-2011 Axel Wagner and contributors
+ *
+ * See file LICNSE for license information
+ *
+ * src/child.c: Getting Input for the statusline
+ *
+ */
+#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 <ev.h>
+
+#include "common.h"
+
+/* Global variables for child_*() */
+pid_t child_pid;
+
+/* stdin- and sigchild-watchers */
+ev_io    *stdin_io;
+ev_child *child_sig;
+
+/* The buffer statusline points to */
+char *statusline_buffer = NULL;
+
+/*
+ * Stop and free() the stdin- and sigchild-watchers
+ *
+ */
+void cleanup() {
+    if (stdin_io != NULL) {
+        ev_io_stop(main_loop, stdin_io);
+        FREE(stdin_io);
+        FREE(statusline_buffer);
+    }
+
+    if (child_sig != NULL) {
+        ev_child_stop(main_loop, child_sig);
+        FREE(child_sig);
+    }
+}
+
+/*
+ * 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) {
+    int fd = watcher->fd;
+    int n = 0;
+    int rec = 0;
+    int buffer_len = STDIN_CHUNK_SIZE;
+    char *buffer = malloc(buffer_len);
+    buffer[0] = '\0';
+    while(1) {
+        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;
+            }
+            ELOG("read() failed!: %s\n", strerror(errno));
+            exit(EXIT_FAILURE);
+        }
+        if (n == 0) {
+            if (rec != 0) {
+                /* remove trailing newline and finish up */
+                buffer[rec-1] = '\0';
+            }
+
+            /* end of file, kill the watcher */
+            DLOG("stdin: EOF\n");
+            cleanup();
+            break;
+        }
+        rec += n;
+
+        if (rec == buffer_len) {
+            buffer_len += STDIN_CHUNK_SIZE;
+            buffer = realloc(buffer, buffer_len);
+           }
+    }
+    if (*buffer == '\0') {
+        FREE(buffer);
+        return;
+    }
+    FREE(statusline_buffer);
+    statusline = statusline_buffer = buffer;
+    for (n = 0; buffer[n] != '\0'; ++n) {
+        if (buffer[n] == '\n')
+            statusline = &buffer[n + 1];
+    }
+    DLOG("%s\n", statusline);
+    draw_bars();
+}
+
+/*
+ * We received a sigchild, meaning, that the child-process terminated.
+ * We simply free the respective data-structures and don't care for input
+ * anymore
+ *
+ */
+void child_sig_cb(struct ev_loop *loop, ev_child *watcher, int revents) {
+    DLOG("Child (pid: %d) unexpectedly exited with status %d\n",
+           child_pid,
+           watcher->rstatus);
+    cleanup();
+}
+
+/*
+ * 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
+ *
+ */
+void start_child(char *command) {
+    child_pid = 0;
+    if (command != NULL) {
+        int fd[2];
+        pipe(fd);
+        child_pid = fork();
+        switch (child_pid) {
+            case -1:
+                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);
+
+                static const char *shell = NULL;
+
+                if ((shell = getenv("SHELL")) == NULL)
+                    shell = "/bin/sh";
+
+                execl(shell, shell, "-c", command, (char*) NULL);
+                return;
+            default:
+                /* Parent-process. Rerout stdin */
+                close(fd[1]);
+
+                dup2(fd[0], STDIN_FILENO);
+
+                /* If hide-on-modifier is set, we start of by sending the
+                 * child a SIGSTOP, because the bars aren't mapped at start */
+                if (config.hide_on_modifier) {
+                    stop_child();
+                }
+
+                break;
+        }
+    }
+
+    /* 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));
+    ev_io_init(stdin_io, &stdin_io_cb, STDIN_FILENO, EV_READ);
+    ev_io_start(main_loop, stdin_io);
+
+    /* We must cleanup, if the child unexpectedly terminates */
+    child_sig = malloc(sizeof(ev_child));
+    ev_child_init(child_sig, &child_sig_cb, child_pid, 0);
+    ev_child_start(main_loop, child_sig);
+
+}
+
+/*
+ * 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, SIGCONT);
+        kill(child_pid, SIGTERM);
+        int status;
+        waitpid(child_pid, &status, 0);
+        child_pid = 0;
+        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);
+    }
+}
diff --git a/i3bar/src/ipc.c b/i3bar/src/ipc.c
new file mode 100644 (file)
index 0000000..85cd234
--- /dev/null
@@ -0,0 +1,342 @@
+/*
+ * i3bar - an xcb-based status- and ws-bar for i3
+ *
+ * © 2010-2011 Axel Wagner and contributors
+ *
+ * See file LICNSE for license information
+ *
+ * src/ipc.c: Communicating with i3
+ *
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <i3/ipc.h>
+#include <ev.h>
+
+#include "common.h"
+
+ev_io      *i3_connection;
+ev_timer   *reconn = NULL;
+
+const char *sock_path;
+
+typedef void(*handler_t)(char*);
+
+/*
+ * Retry to connect.
+ *
+ */
+void retry_connection(struct ev_loop *loop, ev_timer *w, int events) {
+    static int retries = 8;
+    if (init_connection(sock_path) == 0) {
+        if (retries == 0) {
+            ELOG("Retried 8 times - connection failed!\n");
+            exit(EXIT_FAILURE);
+        }
+        retries--;
+        return;
+    }
+    retries = 8;
+    ev_timer_stop(loop, w);
+    subscribe_events();
+
+    /* We get the current outputs and workspaces, to
+     * reconfigure all bars with the current configuration */
+    i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_OUTPUTS, NULL);
+    if (!config.disable_ws) {
+        i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_WORKSPACES, NULL);
+    }
+}
+
+/*
+ * Schedule a reconnect
+ *
+ */
+void reconnect() {
+    if (reconn == NULL) {
+        if ((reconn = malloc(sizeof(ev_timer))) == NULL) {
+            ELOG("malloc() failed: %s\n", strerror(errno));
+            exit(EXIT_FAILURE);
+        }
+    } else {
+        ev_timer_stop(main_loop, reconn);
+    }
+    ev_timer_init(reconn, retry_connection, 0.25, 0.25);
+    ev_timer_start(main_loop, reconn);
+}
+
+/*
+ * Called, when we get a reply to a command from i3.
+ * Since i3 does not give us much feedback on commands, we do not much
+ *
+ */
+void got_command_reply(char *reply) {
+    /* TODO: Error handling for command-replies */
+}
+
+/*
+ * Called, when we get a reply with workspaces-data
+ *
+ */
+void got_workspace_reply(char *reply) {
+    DLOG("Got Workspace-Data!\n");
+    parse_workspaces_json(reply);
+    draw_bars();
+}
+
+/*
+ * Called, when we get a reply for a subscription.
+ * Since i3 does not give us much feedback on commands, we do not much
+ *
+ */
+void got_subscribe_reply(char *reply) {
+    DLOG("Got Subscribe Reply: %s\n", reply);
+    /* TODO: Error handling for subscribe-commands */
+}
+
+/*
+ * Called, when we get a reply with outputs-data
+ *
+ */
+void got_output_reply(char *reply) {
+    DLOG("Parsing Outputs-JSON...\n");
+    parse_outputs_json(reply);
+    DLOG("Reconfiguring Windows...\n");
+    realloc_sl_buffer();
+    reconfig_windows();
+}
+
+/* Data-structure to easily call the reply-handlers later */
+handler_t reply_handlers[] = {
+    &got_command_reply,
+    &got_workspace_reply,
+    &got_subscribe_reply,
+    &got_output_reply,
+};
+
+/*
+ * Called, when a workspace-event arrives (i.e. the user changed the workspace)
+ *
+ */
+void got_workspace_event(char *event) {
+    DLOG("Got Workspace Event!\n");
+    i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_WORKSPACES, NULL);
+}
+
+/*
+ * Called, when an output-event arrives (i.e. the screen-configuration changed)
+ *
+ */
+void got_output_event(char *event) {
+    DLOG("Got Output Event!\n");
+    i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_OUTPUTS, NULL);
+    if (!config.disable_ws) {
+        i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_WORKSPACES, NULL);
+    }
+}
+
+/* Data-structure to easily call the reply-handlers later */
+handler_t event_handlers[] = {
+    &got_workspace_event,
+    &got_output_event
+};
+
+/*
+ * Called, when we get a message from i3
+ *
+ */
+void got_data(struct ev_loop *loop, ev_io *watcher, int events) {
+    DLOG("Got data!\n");
+    int fd = watcher->fd;
+
+    /* First we only read the header, because we know its length */
+    uint32_t header_len = strlen(I3_IPC_MAGIC) + sizeof(uint32_t)*2;
+    char *header = malloc(header_len);
+    if (header == NULL) {
+        ELOG("Could not allocate memory: %s\n", strerror(errno));
+        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) {
+            ELOG("read() failed: %s\n", strerror(errno));
+            exit(EXIT_FAILURE);
+        }
+        if (n == 0) {
+            /* EOF received. We try to recover a few times, because most likely
+             * i3 just restarted */
+            ELOG("EOF received, try to recover...\n");
+            destroy_connection();
+            reconnect();
+            return;
+        }
+        rec += n;
+    }
+
+    if (strncmp(header, I3_IPC_MAGIC, strlen(I3_IPC_MAGIC))) {
+        ELOG("Wrong magic code: %.*s\n Expected: %s\n",
+             (int) strlen(I3_IPC_MAGIC),
+             header,
+             I3_IPC_MAGIC);
+        exit(EXIT_FAILURE);
+    }
+
+    char *walk = header + strlen(I3_IPC_MAGIC);
+    uint32_t size;
+    memcpy(&size, (uint32_t*)walk, sizeof(uint32_t));
+    walk += sizeof(uint32_t);
+    uint32_t type;
+    memcpy(&type, (uint32_t*)walk, sizeof(uint32_t));
+
+    /* 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) {
+        /* EOF received. We try to recover a few times, because most likely
+         * i3 just restarted */
+        ELOG("EOF received, try to recover...\n");
+        destroy_connection();
+        reconnect();
+        return;
+    }
+    rec = 0;
+
+    while (rec < size) {
+        int n = read(fd, buffer + rec, size - rec);
+        if (n == -1) {
+            ELOG("read() failed: %s\n", strerror(errno));
+            exit(EXIT_FAILURE);
+        }
+        if (n == 0) {
+            ELOG("Nothing to read!\n");
+            exit(EXIT_FAILURE);
+        }
+        rec += n;
+    }
+    buffer[size] = '\0';
+
+    /* And call the callback (indexed by the type) */
+    if (type & (1 << 31)) {
+        type ^= 1 << 31;
+        event_handlers[type](buffer);
+    } else {
+        reply_handlers[type](buffer);
+    }
+
+    FREE(header);
+    FREE(buffer);
+}
+
+/*
+ * Sends a Message to i3.
+ * type must be a valid I3_IPC_MESSAGE_TYPE (see i3/ipc.h for further information)
+ *
+ */
+int i3_send_msg(uint32_t type, const char *payload) {
+    uint32_t len = 0;
+    if (payload != NULL) {
+        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) {
+        ELOG("Could not allocate memory: %s\n", strerror(errno));
+        exit(EXIT_FAILURE);
+    }
+
+    char *walk = buffer;
+
+    strncpy(buffer, I3_IPC_MAGIC, strlen(I3_IPC_MAGIC));
+    walk += strlen(I3_IPC_MAGIC);
+    memcpy(walk, &len, sizeof(uint32_t));
+    walk += sizeof(uint32_t);
+    memcpy(walk, &type, sizeof(uint32_t));
+    walk += sizeof(uint32_t);
+
+    if (payload != NULL)
+        strncpy(walk, payload, len);
+
+    uint32_t written = 0;
+
+    while (to_write > 0) {
+        int n = write(i3_connection->fd, buffer + written, to_write);
+        if (n == -1) {
+            ELOG("write() failed: %s\n", strerror(errno));
+            exit(EXIT_FAILURE);
+        }
+
+        to_write -= n;
+        written += n;
+    }
+
+    FREE(buffer);
+
+    return 1;
+}
+
+/*
+ * Initiate a connection to i3.
+ * socket-path must be a valid path to the ipc_socket of i3
+ *
+ */
+int init_connection(const char *socket_path) {
+    sock_path = socket_path;
+    int sockfd = socket(AF_LOCAL, SOCK_STREAM, 0);
+    if (sockfd == -1) {
+        ELOG("Could not create Socket: %s\n", strerror(errno));
+        exit(EXIT_FAILURE);
+    }
+
+    struct sockaddr_un addr;
+    memset(&addr, 0, sizeof(struct sockaddr_un));
+    addr.sun_family = AF_LOCAL;
+    strcpy(addr.sun_path, sock_path);
+    if (connect(sockfd, (const struct sockaddr*) &addr, sizeof(struct sockaddr_un)) < 0) {
+        ELOG("Could not connect to i3! %s: %s\n", sock_path, strerror(errno));
+        reconnect();
+        return 0;
+    }
+
+    i3_connection = malloc(sizeof(ev_io));
+    if (i3_connection == NULL) {
+        ELOG("malloc() failed: %s\n", strerror(errno));
+        exit(EXIT_FAILURE);
+    }
+    ev_io_init(i3_connection, &got_data, sockfd, EV_READ);
+    ev_io_start(main_loop, i3_connection);
+    return 1;
+}
+
+/*
+ * Destroy the connection to i3.
+ */
+void destroy_connection() {
+    close(i3_connection->fd);
+    ev_io_stop(main_loop, i3_connection);
+}
+
+/*
+ * Subscribe to all the i3-events, we need
+ *
+ */
+void subscribe_events() {
+    if (config.disable_ws) {
+        i3_send_msg(I3_IPC_MESSAGE_TYPE_SUBSCRIBE, "[ \"output\" ]");
+    } else {
+        i3_send_msg(I3_IPC_MESSAGE_TYPE_SUBSCRIBE, "[ \"workspace\", \"output\" ]");
+    }
+}
diff --git a/i3bar/src/main.c b/i3bar/src/main.c
new file mode 100644 (file)
index 0000000..e91511e
--- /dev/null
@@ -0,0 +1,315 @@
+/*
+ * i3bar - an xcb-based status- and ws-bar for i3
+ *
+ * © 2010-2011 Axel Wagner and contributors
+ *
+ * See file LICNSE for license information
+ *
+ */
+#include <stdio.h>
+#include <i3/ipc.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <ev.h>
+#include <getopt.h>
+#include <glob.h>
+
+#include "common.h"
+
+/*
+ * Glob path, i.e. expand ~
+ *
+ */
+char *expand_path(char *path) {
+    static glob_t globbuf;
+    if (glob(path, GLOB_NOCHECK | GLOB_TILDE, NULL, &globbuf) < 0) {
+        ELOG("glob() failed\n");
+        exit(EXIT_FAILURE);
+    }
+    char *result = strdup(globbuf.gl_pathc > 0 ? globbuf.gl_pathv[0] : path);
+    if (result == NULL) {
+        ELOG("malloc() failed: %s\n", strerror(errno));
+        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);
+    FREE_COLOR(focus_ws_fg);
+    FREE_COLOR(focus_ws_bg);
+#undef FREE_COLOR
+}
+
+void print_usage(char *elf_name) {
+    printf("Usage: %s [-s sock_path] [-c command] [-m|-d[pos]] [-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("-d[<pos>]\tEnable dockmode. <pos> is \"top\" or \"bottom\". Default is bottom\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("-w\t\tDisable workspace-buttons\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 = getenv("I3SOCK");
+    char *command = NULL;
+    char *fontname = NULL;
+    char *i3_default_sock_path = "/tmp/i3-ipc.sock";
+    struct xcb_color_strings_t colors = { NULL, };
+
+    /* Definition of the standard-config */
+    config.hide_on_modifier = 0;
+    config.dockpos = DOCKPOS_NONE;
+    config.disable_ws = 0;
+
+    static struct option long_opt[] = {
+        { "socket",               required_argument, 0, 's' },
+        { "command",              required_argument, 0, 'c' },
+        { "hide",                 no_argument,       0, 'm' },
+        { "dock",                 optional_argument, 0, 'd' },
+        { "font",                 required_argument, 0, 'f' },
+        { "nows",                 no_argument,       0, 'w' },
+        { "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' },
+        { "color-focus-ws-bg",    required_argument, 0, 'I' },
+        { "color-focus-ws-fg",    required_argument, 0, 'J' },
+        { NULL,                   0,                 0, 0}
+    };
+
+    while ((opt = getopt_long(argc, argv, "s:c:d::mf:whvVA:B:C:D:E:F:G:H:I:J:", long_opt, &option_index)) != -1) {
+        switch (opt) {
+            case 's':
+                socket_path = expand_path(optarg);
+                break;
+            case 'c':
+                command = strdup(optarg);
+                break;
+            case 'm':
+                config.hide_on_modifier = 1;
+                break;
+            case 'd':
+                config.hide_on_modifier = 0;
+                if (optarg == NULL) {
+                    config.dockpos = DOCKPOS_BOT;
+                    break;
+                }
+                if (!strcmp(optarg, "top")) {
+                    config.dockpos = DOCKPOS_TOP;
+                } else if (!strcmp(optarg, "bottom")) {
+                    config.dockpos = DOCKPOS_BOT;
+                } else {
+                    print_usage(argv[0]);
+                    exit(EXIT_FAILURE);
+                }
+                break;
+            case 'f':
+                fontname = strdup(optarg);
+                break;
+            case 'w':
+                config.disable_ws = 1;
+                break;
+            case 'v':
+                printf("i3bar version " I3BAR_VERSION " © 2010-2011 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;
+            case 'I':
+                read_color(&colors.focus_ws_bg);
+                break;
+            case 'J':
+                read_color(&colors.focus_ws_fg);
+                break;
+            default:
+                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 (config.dockpos != DOCKPOS_NONE) {
+        if (config.hide_on_modifier) {
+            ELOG("--dock and --hide are mutually exclusive!\n");
+            exit(EXIT_FAILURE);
+        }
+    } else {
+        config.hide_on_modifier = 1;
+    }
+
+    main_loop = ev_default_loop(0);
+
+    init_colors(&colors);
+    char *atom_sock_path = init_xcb(fontname);
+
+    if (socket_path == NULL) {
+        socket_path = atom_sock_path;
+    }
+
+    if (socket_path == NULL) {
+        ELOG("No Socket Path Specified, default to %s\n", i3_default_sock_path);
+        socket_path = expand_path(i3_default_sock_path);
+    }
+
+    free_colors(&colors);
+
+    init_outputs();
+    if (init_connection(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);
+        if (!config.disable_ws) {
+            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);
+    FREE(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 = malloc(sizeof(ev_signal));
+    ev_signal *sig_int = malloc(sizeof(ev_signal));
+    ev_signal *sig_hup = malloc(sizeof(ev_signal));
+
+    if (sig_term == NULL || sig_int == NULL || sig_hup == NULL) {
+        ELOG("malloc() failed: %s\n", strerror(errno));
+        exit(EXIT_FAILURE);
+    }
+
+    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();
+
+    FREE(statusline_buffer);
+
+    clean_xcb();
+    ev_default_destroy();
+
+    free_workspaces();
+
+    return 0;
+}
diff --git a/i3bar/src/outputs.c b/i3bar/src/outputs.c
new file mode 100644 (file)
index 0000000..8cfdaa9
--- /dev/null
@@ -0,0 +1,293 @@
+/*
+ * i3bar - an xcb-based status- and ws-bar for i3
+ *
+ * © 2010-2011 Axel Wagner and contributors
+ *
+ * See file LICNSE for license information
+ *
+ * src/outputs.c: Maintaining the output-list
+ *
+ */
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <i3/ipc.h>
+#include <yajl/yajl_parse.h>
+#include <yajl/yajl_version.h>
+
+#include "common.h"
+
+/* A datatype to pass through the callbacks to save the state */
+struct outputs_json_params {
+    struct outputs_head *outputs;
+    i3_output           *outputs_walk;
+    char                *cur_key;
+    char                *json;
+    bool                init;
+};
+
+/*
+ * Parse a null-value (current_workspace)
+ *
+ */
+static int outputs_null_cb(void *params_) {
+    struct outputs_json_params *params = (struct outputs_json_params*) params_;
+
+    FREE(params->cur_key);
+
+    return 1;
+}
+
+/*
+ * Parse a booleant-value (active)
+ *
+ */
+static int outputs_boolean_cb(void *params_, bool val) {
+    struct outputs_json_params *params = (struct outputs_json_params*) params_;
+
+    if (strcmp(params->cur_key, "active")) {
+        return 0;
+    }
+
+    params->outputs_walk->active = val;
+
+    FREE(params->cur_key);
+
+    return 1;
+}
+
+/*
+ * Parse an integer (current_workspace or the rect)
+ *
+ */
+#if YAJL_MAJOR >= 2
+static int outputs_integer_cb(void *params_, long long val) {
+#else
+static int outputs_integer_cb(void *params_, long val) {
+#endif
+    struct outputs_json_params *params = (struct outputs_json_params*) params_;
+
+    if (!strcmp(params->cur_key, "current_workspace")) {
+        params->outputs_walk->ws = (int) val;
+        FREE(params->cur_key);
+        return 1;
+    }
+
+    if (!strcmp(params->cur_key, "x")) {
+        params->outputs_walk->rect.x = (int) val;
+        FREE(params->cur_key);
+        return 1;
+    }
+
+    if (!strcmp(params->cur_key, "y")) {
+        params->outputs_walk->rect.y = (int) val;
+        FREE(params->cur_key);
+        return 1;
+    }
+
+    if (!strcmp(params->cur_key, "width")) {
+        params->outputs_walk->rect.w = (int) val;
+        FREE(params->cur_key);
+        return 1;
+    }
+
+    if (!strcmp(params->cur_key, "height")) {
+        params->outputs_walk->rect.h = (int) val;
+        FREE(params->cur_key);
+        return 1;
+    }
+
+    return 0;
+}
+
+/*
+ * Parse a string (name)
+ *
+ */
+#if YAJL_MAJOR >= 2
+static int outputs_string_cb(void *params_, const unsigned char *val, size_t len) {
+#else
+static int outputs_string_cb(void *params_, const unsigned char *val, unsigned int len) {
+#endif
+    struct outputs_json_params *params = (struct outputs_json_params*) params_;
+
+    if (!strcmp(params->cur_key, "current_workspace")) {
+        char *copy = malloc(sizeof(const unsigned char) * (len + 1));
+        strncpy(copy, (const char*) val, len);
+        copy[len] = '\0';
+
+        char *end;
+        errno = 0;
+        long parsed_num = strtol(copy, &end, 10);
+        if (errno == 0 &&
+            (end && *end == '\0'))
+            params->outputs_walk->ws = parsed_num;
+        free(copy);
+        FREE(params->cur_key);
+        return 1;
+    }
+
+    if (strcmp(params->cur_key, "name")) {
+        return 0;
+    }
+
+    char *name = malloc(sizeof(const unsigned char) * (len + 1));
+    strncpy(name, (const char*) val, len);
+    name[len] = '\0';
+
+    params->outputs_walk->name = name;
+
+    FREE(params->cur_key);
+
+    return 1;
+}
+
+/*
+ * We hit the start of a json-map (rect or a new output)
+ *
+ */
+static int outputs_start_map_cb(void *params_) {
+    struct outputs_json_params *params = (struct outputs_json_params*) params_;
+    i3_output *new_output = NULL;
+
+    if (params->cur_key == NULL) {
+        new_output = malloc(sizeof(i3_output));
+        new_output->name = NULL;
+        new_output->ws = 0,
+        memset(&new_output->rect, 0, sizeof(rect));
+        new_output->bar = XCB_NONE;
+
+        new_output->workspaces = malloc(sizeof(struct ws_head));
+        TAILQ_INIT(new_output->workspaces);
+
+        params->outputs_walk = new_output;
+
+        return 1;
+    }
+
+    return 1;
+}
+
+/*
+ * We hit the end of a map (rect or a new output)
+ *
+ */
+static int outputs_end_map_cb(void *params_) {
+    struct outputs_json_params *params = (struct outputs_json_params*) params_;
+    /* FIXME: What is at the end of a rect? */
+
+    i3_output *target = get_output_by_name(params->outputs_walk->name);
+
+    if (target == NULL) {
+        SLIST_INSERT_HEAD(outputs, params->outputs_walk, slist);
+    } else {
+        target->active = params->outputs_walk->active;
+        target->ws = params->outputs_walk->ws;
+        target->rect = params->outputs_walk->rect;
+    }
+    return 1;
+}
+
+/*
+ * Parse a key.
+ *
+ * Essentially we just save it in the parsing-state
+ *
+ */
+#if YAJL_MAJOR >= 2
+static int outputs_map_key_cb(void *params_, const unsigned char *keyVal, size_t keyLen) {
+#else
+static int outputs_map_key_cb(void *params_, const unsigned char *keyVal, unsigned keyLen) {
+#endif
+    struct outputs_json_params *params = (struct outputs_json_params*) params_;
+    FREE(params->cur_key);
+
+    params->cur_key = malloc(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 outputs_callbacks = {
+    &outputs_null_cb,
+    &outputs_boolean_cb,
+    &outputs_integer_cb,
+    NULL,
+    NULL,
+    &outputs_string_cb,
+    &outputs_start_map_cb,
+    &outputs_map_key_cb,
+    &outputs_end_map_cb,
+    NULL,
+    NULL
+};
+
+/*
+ * Initiate the output-list
+ *
+ */
+void init_outputs() {
+    outputs = malloc(sizeof(struct outputs_head));
+    SLIST_INIT(outputs);
+}
+
+/*
+ * Start parsing the received json-string
+ *
+ */
+void parse_outputs_json(char *json) {
+    struct outputs_json_params params;
+
+    params.outputs_walk = NULL;
+    params.cur_key = NULL;
+    params.json = json;
+
+    yajl_handle handle;
+    yajl_status state;
+#if YAJL_MAJOR < 2
+    yajl_parser_config parse_conf = { 0, 0 };
+
+    handle = yajl_alloc(&outputs_callbacks, &parse_conf, NULL, (void*) &params);
+#else
+    handle = yajl_alloc(&outputs_callbacks, NULL, (void*) &params);
+#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 outputs-reply!\n");
+            exit(EXIT_FAILURE);
+            break;
+    }
+
+    yajl_free(handle);
+}
+
+/*
+ * Returns the output with the given name
+ *
+ */
+i3_output *get_output_by_name(char *name) {
+    i3_output *walk;
+    if (name == NULL) {
+        return NULL;
+    }
+    SLIST_FOREACH(walk, outputs, slist) {
+        if (!strcmp(walk->name, name)) {
+            break;
+        }
+    }
+
+    return walk;
+}
diff --git a/i3bar/src/ucs2_to_utf8.c b/i3bar/src/ucs2_to_utf8.c
new file mode 100644 (file)
index 0000000..8c79c3f
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * vim:ts=8:expandtab
+ *
+ * i3 - an improved dynamic tiling window manager
+ *
+ * © 2009 Michael Stapelberg and contributors
+ *
+ * See file LICENSE for license information.
+ *
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <err.h>
+#include <iconv.h>
+
+static iconv_t conversion_descriptor = 0;
+static iconv_t conversion_descriptor2 = 0;
+
+/*
+ * Returns the input string, but converted from UCS-2 to UTF-8. Memory will be
+ * allocated, thus the caller has to free the output.
+ *
+ */
+char *convert_ucs_to_utf8(char *input) {
+       size_t input_size = 2;
+       /* UTF-8 may consume up to 4 byte */
+       int buffer_size = 8;
+
+       char *buffer = calloc(buffer_size, 1);
+        if (buffer == NULL)
+                err(EXIT_FAILURE, "malloc() failed\n");
+       size_t output_size = buffer_size;
+       /* We need to use an additional pointer, because iconv() modifies it */
+       char *output = buffer;
+
+       /* We convert the input into UCS-2 big endian */
+        if (conversion_descriptor == 0) {
+                conversion_descriptor = iconv_open("UTF-8", "UCS-2BE");
+                if (conversion_descriptor == 0) {
+                        fprintf(stderr, "error opening the conversion context\n");
+                        exit(1);
+                }
+        }
+
+       /* Get the conversion descriptor back to original state */
+       iconv(conversion_descriptor, NULL, NULL, NULL, NULL);
+
+       /* Convert our text */
+       int rc = iconv(conversion_descriptor, (void*)&input, &input_size, &output, &output_size);
+        if (rc == (size_t)-1) {
+                perror("Converting to UCS-2 failed");
+                return NULL;
+       }
+
+       return buffer;
+}
+
+/*
+ * Converts the given string to UCS-2 big endian for use with
+ * xcb_image_text_16(). The amount of real glyphs is stored in real_strlen,
+ * a buffer containing the UCS-2 encoded string (16 bit per glyph) is
+ * returned. It has to be freed when done.
+ *
+ */
+char *convert_utf8_to_ucs2(char *input, int *real_strlen) {
+       size_t input_size = strlen(input) + 1;
+       /* UCS-2 consumes exactly two bytes for each glyph */
+       int buffer_size = input_size * 2;
+
+       char *buffer = malloc(buffer_size);
+        if (buffer == NULL)
+                err(EXIT_FAILURE, "malloc() failed\n");
+       size_t output_size = buffer_size;
+       /* We need to use an additional pointer, because iconv() modifies it */
+       char *output = buffer;
+
+       /* We convert the input into UCS-2 big endian */
+        if (conversion_descriptor2 == 0) {
+                conversion_descriptor2 = iconv_open("UCS-2BE", "UTF-8");
+                if (conversion_descriptor2 == 0) {
+                        fprintf(stderr, "error opening the conversion context\n");
+                        exit(1);
+                }
+        }
+
+       /* Get the conversion descriptor back to original state */
+       iconv(conversion_descriptor2, NULL, NULL, NULL, NULL);
+
+       /* Convert our text */
+       int rc = iconv(conversion_descriptor2, (void*)&input, &input_size, &output, &output_size);
+        if (rc == (size_t)-1) {
+                perror("Converting to UCS-2 failed");
+                if (real_strlen != NULL)
+                       *real_strlen = 0;
+                return NULL;
+       }
+
+        if (real_strlen != NULL)
+               *real_strlen = ((buffer_size - output_size) / 2) - 1;
+
+       return buffer;
+}
diff --git a/i3bar/src/workspaces.c b/i3bar/src/workspaces.c
new file mode 100644 (file)
index 0000000..db81267
--- /dev/null
@@ -0,0 +1,290 @@
+/*
+ * i3bar - an xcb-based status- and ws-bar for i3
+ *
+ * © 2010-2011 Axel Wagner and contributors
+ *
+ * See file LICNSE for license information
+ *
+ * src/workspaces.c: Maintaining the workspace-lists
+ *
+ */
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <yajl/yajl_parse.h>
+#include <yajl/yajl_version.h>
+
+#include "common.h"
+
+/* A datatype to pass through the callbacks to save the state */
+struct workspaces_json_params {
+    struct ws_head *workspaces;
+    i3_ws          *workspaces_walk;
+    char           *cur_key;
+    char           *json;
+};
+
+/*
+ * Parse a booleant-value (visible, focused, urgent)
+ *
+ */
+static int workspaces_boolean_cb(void *params_, bool val) {
+    struct workspaces_json_params *params = (struct workspaces_json_params*) params_;
+
+    if (!strcmp(params->cur_key, "visible")) {
+        params->workspaces_walk->visible = val;
+        FREE(params->cur_key);
+        return 1;
+    }
+
+    if (!strcmp(params->cur_key, "focused")) {
+        params->workspaces_walk->focused = val;
+        FREE(params->cur_key);
+        return 1;
+    }
+
+    if (!strcmp(params->cur_key, "urgent")) {
+        params->workspaces_walk->urgent = val;
+        FREE(params->cur_key);
+        return 1;
+    }
+
+    FREE(params->cur_key);
+
+    return 0;
+}
+
+/*
+ * Parse an integer (num or the rect)
+ *
+ */
+#if YAJL_MAJOR >= 2
+static int workspaces_integer_cb(void *params_, long long val) {
+#else
+static int workspaces_integer_cb(void *params_, long val) {
+#endif
+    struct workspaces_json_params *params = (struct workspaces_json_params*) params_;
+
+    if (!strcmp(params->cur_key, "num")) {
+        params->workspaces_walk->num = (int) val;
+        FREE(params->cur_key);
+        return 1;
+    }
+
+    if (!strcmp(params->cur_key, "x")) {
+        params->workspaces_walk->rect.x = (int) val;
+        FREE(params->cur_key);
+        return 1;
+    }
+
+    if (!strcmp(params->cur_key, "y")) {
+        params->workspaces_walk->rect.y = (int) val;
+        FREE(params->cur_key);
+        return 1;
+    }
+
+    if (!strcmp(params->cur_key, "width")) {
+        params->workspaces_walk->rect.w = (int) val;
+        FREE(params->cur_key);
+        return 1;
+    }
+
+    if (!strcmp(params->cur_key, "height")) {
+        params->workspaces_walk->rect.h = (int) val;
+        FREE(params->cur_key);
+        return 1;
+    }
+
+    FREE(params->cur_key);
+    return 0;
+}
+
+/*
+ * Parse a string (name, output)
+ *
+ */
+#if YAJL_MAJOR >= 2
+static int workspaces_string_cb(void *params_, const unsigned char *val, size_t len) {
+#else
+static int workspaces_string_cb(void *params_, const unsigned char *val, unsigned int len) {
+#endif
+        struct workspaces_json_params *params = (struct workspaces_json_params*) params_;
+
+        char *output_name;
+
+        if (!strcmp(params->cur_key, "name")) {
+            /* Save the name */
+            params->workspaces_walk->name = malloc(sizeof(const unsigned char) * (len + 1));
+            strncpy(params->workspaces_walk->name, (const char*) val, len);
+            params->workspaces_walk->name[len] = '\0';
+
+            /* Convert the name to ucs2, save it's length in glyphs and calculate it'srendered width */
+            int ucs2_len;
+            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 =
+                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;
+        }
+
+        if (!strcmp(params->cur_key, "output")) {
+            /* We add the ws to the TAILQ of the output, it belongs to */
+            output_name = malloc(sizeof(const unsigned char) * (len + 1));
+            strncpy(output_name, (const char*) val, len);
+            output_name[len] = '\0';
+            params->workspaces_walk->output = get_output_by_name(output_name);
+
+            TAILQ_INSERT_TAIL(params->workspaces_walk->output->workspaces,
+                              params->workspaces_walk,
+                              tailq);
+
+            FREE(output_name);
+            return 1;
+        }
+
+        return 0;
+}
+
+/*
+ * We hit the start of a json-map (rect or a new output)
+ *
+ */
+static int workspaces_start_map_cb(void *params_) {
+    struct workspaces_json_params *params = (struct workspaces_json_params*) params_;
+
+    i3_ws *new_workspace = NULL;
+
+    if (params->cur_key == NULL) {
+        new_workspace = malloc(sizeof(i3_ws));
+        new_workspace->num = -1;
+        new_workspace->name = NULL;
+        new_workspace->visible = 0;
+        new_workspace->focused = 0;
+        new_workspace->urgent = 0;
+        memset(&new_workspace->rect, 0, sizeof(rect));
+        new_workspace->output = NULL;
+
+        params->workspaces_walk = new_workspace;
+        return 1;
+    }
+
+    return 1;
+}
+
+/*
+ * Parse a key.
+ *
+ * Essentially we just save it in the parsing-state
+ *
+ */
+#if YAJL_MAJOR >= 2
+static int workspaces_map_key_cb(void *params_, const unsigned char *keyVal, size_t keyLen) {
+#else
+static int workspaces_map_key_cb(void *params_, const unsigned char *keyVal, unsigned int keyLen) {
+#endif
+    struct workspaces_json_params *params = (struct workspaces_json_params*) params_;
+    FREE(params->cur_key);
+
+    params->cur_key = malloc(sizeof(unsigned char) * (keyLen + 1));
+    if (params->cur_key == NULL) {
+        ELOG("Could not allocate memory: %s\n", strerror(errno));
+        exit(EXIT_FAILURE);
+    }
+    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 workspaces_callbacks = {
+    NULL,
+    &workspaces_boolean_cb,
+    &workspaces_integer_cb,
+    NULL,
+    NULL,
+    &workspaces_string_cb,
+    &workspaces_start_map_cb,
+    &workspaces_map_key_cb,
+    NULL,
+    NULL,
+    NULL
+};
+
+/*
+ * Start parsing the received json-string
+ *
+ */
+void parse_workspaces_json(char *json) {
+    /* FIXME: Fasciliate stream-processing, i.e. allow starting to interpret
+     * JSON in chunks */
+    struct workspaces_json_params params;
+
+    free_workspaces();
+
+    params.workspaces_walk = NULL;
+    params.cur_key = NULL;
+    params.json = json;
+
+    yajl_handle handle;
+    yajl_status state;
+#if YAJL_MAJOR < 2
+    yajl_parser_config parse_conf = { 0, 0 };
+
+    handle = yajl_alloc(&workspaces_callbacks, &parse_conf, NULL, (void*) &params);
+#else
+    handle = yajl_alloc(&workspaces_callbacks, NULL, (void*) &params);
+#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 workspaces-reply!\n");
+            exit(EXIT_FAILURE);
+            break;
+    }
+
+    yajl_free(handle);
+
+    FREE(params.cur_key);
+}
+
+/*
+ * free() all workspace data-structures. Does not free() the heads of the tailqueues.
+ *
+ */
+void free_workspaces() {
+    i3_output *outputs_walk;
+    if (outputs == NULL) {
+        return;
+    }
+    i3_ws     *ws_walk;
+
+    SLIST_FOREACH(outputs_walk, outputs, slist) {
+        if (outputs_walk->workspaces != NULL && !TAILQ_EMPTY(outputs_walk->workspaces)) {
+            TAILQ_FOREACH(ws_walk, outputs_walk->workspaces, tailq) {
+                FREE(ws_walk->name);
+                FREE(ws_walk->ucs2_name);
+            }
+            FREE_TAILQ(outputs_walk->workspaces, i3_ws);
+        }
+    }
+}
diff --git a/i3bar/src/xcb.c b/i3bar/src/xcb.c
new file mode 100644 (file)
index 0000000..62160f4
--- /dev/null
@@ -0,0 +1,1013 @@
+/*
+ * i3bar - an xcb-based status- and ws-bar for i3
+ *
+ * © 2010-2011 Axel Wagner and contributors
+ *
+ * See file LICNSE for license information
+ *
+ * src/xcb.c: Communicating with X
+ *
+ */
+#include <xcb/xcb.h>
+#include <xcb/xproto.h>
+#include <xcb/xcb_atom.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <i3/ipc.h>
+#include <ev.h>
+#include <errno.h>
+#include <limits.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 */
+enum {
+    #define ATOM_DO(name) name,
+    #include "xcb_atoms.def"
+    NUM_ATOMS
+};
+
+xcb_intern_atom_cookie_t atom_cookies[NUM_ATOMS];
+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_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 = 0;
+
+/* Because the statusline is the same on all outputs, we have
+ * global buffer to render it on */
+xcb_gcontext_t   statusline_ctx;
+xcb_gcontext_t   statusline_clear;
+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;
+    uint32_t focus_ws_bg;
+    uint32_t focus_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) {
+        fprintf(stderr, "[%s:%d] ERROR: %s. X Error Code: %d\n", __FILE__, line, 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.
+ * s is assumed to be in the format "rrggbb"
+ *
+ */
+uint32_t get_colorpixel(const char *s) {
+    char strings[3][3] = { { s[0], s[1], '\0'} ,
+                           { s[2], s[3], '\0'} ,
+                           { s[4], s[5], '\0'} };
+    uint8_t r = strtol(strings[0], NULL, 16);
+    uint8_t g = strtol(strings[1], NULL, 16);
+    uint8_t b = strtol(strings[2], NULL, 16);
+    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_rectangle_t rect = { 0, 0, xcb_screen->width_in_pixels, font_height };
+    xcb_poly_fill_rectangle(xcb_connection, statusline_pm, statusline_clear, 1, &rect);
+    draw_text(statusline_pm, statusline_ctx, 0, 0, text, glyph_count);
+
+    FREE(text);
+}
+
+/*
+ * Hides all bars (unmaps them)
+ *
+ */
+void hide_bars() {
+    if (!config.hide_on_modifier) {
+        return;
+    }
+
+    i3_output *walk;
+    SLIST_FOREACH(walk, outputs, slist) {
+        if (!walk->active) {
+            continue;
+        }
+        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");
+    PARSE_COLOR(focus_ws_fg, "FFFFFF");
+    PARSE_COLOR(focus_ws_bg, "480000");
+#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-
+ * wheel was used and change the workspace appropriately
+ *
+ */
+void handle_button(xcb_button_press_event_t *event) {
+    i3_ws *cur_ws;
+
+    /* Determine, which bar was clicked */
+    i3_output *walk;
+    xcb_window_t bar = event->event;
+    SLIST_FOREACH(walk, outputs, slist) {
+        if (walk->bar == bar) {
+            break;
+        }
+    }
+
+    if (walk == NULL) {
+        DLOG("Unknown Bar klicked!\n");
+        return;
+    }
+
+    /* TODO: Move this to extern get_ws_for_output() */
+    TAILQ_FOREACH(cur_ws, walk->workspaces, tailq) {
+        if (cur_ws->visible) {
+            break;
+        }
+    }
+
+    if (cur_ws == NULL) {
+        DLOG("No Workspace active?\n");
+        return;
+    }
+
+    int32_t x = event->event_x;
+
+    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) {
+                DLOG("x = %d\n", x);
+                if (x < cur_ws->name_width + 10) {
+                    break;
+                }
+                x -= cur_ws->name_width + 10;
+            }
+            if (cur_ws == NULL) {
+                return;
+            }
+            break;
+        case 4:
+            /* Mouse wheel down. We select the next ws */
+            if (cur_ws == TAILQ_FIRST(walk->workspaces)) {
+                cur_ws = TAILQ_LAST(walk->workspaces, ws_head);
+            } else {
+                cur_ws = TAILQ_PREV(cur_ws, ws_head, tailq);
+            }
+            break;
+        case 5:
+            /* Mouse wheel up. We select the previos ws */
+            if (cur_ws == TAILQ_LAST(walk->workspaces, ws_head)) {
+                cur_ws = TAILQ_FIRST(walk->workspaces);
+            } else {
+                cur_ws = TAILQ_NEXT(cur_ws, tailq);
+            }
+            break;
+    }
+
+    char buffer[strlen(cur_ws->name) + 11];
+    snprintf(buffer, 50, "workspace %s", cur_ws->name);
+    i3_send_msg(I3_IPC_MESSAGE_TYPE_COMMAND, buffer);
+}
+
+/*
+ * This function is called immediately bevor the main loop locks. We flush xcb
+ * then (and only then)
+ *
+ */
+void xcb_prep_cb(struct ev_loop *loop, ev_prepare *watcher, int revents) {
+    xcb_flush(xcb_connection);
+}
+
+/*
+ * This function is called immediately after the main loop locks, so when one
+ * of the watchers registered an event.
+ * We check wether an X-Event arrived and handle it.
+ *
+ */
+void xcb_chk_cb(struct ev_loop *loop, ev_check *watcher, int revents) {
+    xcb_generic_event_t *event;
+    while ((event = xcb_poll_for_event(xcb_connection)) == NULL) {
+        return;
+    }
+
+    switch (event->response_type & ~0x80) {
+        case XCB_EXPOSE:
+            /* Expose-events happen, when the window needs to be redrawn */
+            redraw_bars();
+            break;
+        case XCB_BUTTON_PRESS:
+            /* Button-press-events are mouse-buttons clicked on one of our bars */
+            handle_button((xcb_button_press_event_t*) event);
+            break;
+    }
+    FREE(event);
+}
+
+/*
+ * Dummy Callback. We only need this, so that the Prepare- and Check-Watchers
+ * are triggered
+ *
+ */
+void xcb_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
+}
+
+/*
+ * We need to bind to the modifier per XKB. Sadly, XCB does not implement this
+ *
+ */
+void xkb_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
+    XkbEvent ev;
+    int modstate = 0;
+
+    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;
+    }
+
+    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;
+    }
+}
+
+/*
+ * Initialize xcb and use the specified fontname for text-rendering
+ *
+ */
+char *init_xcb(char *fontname) {
+    /* FIXME: xcb_connect leaks Memory */
+    xcb_connection = xcb_connect(NULL, NULL);
+    if (xcb_connection_has_error(xcb_connection)) {
+        ELOG("Cannot open display\n");
+        exit(EXIT_FAILURE);
+    }
+    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_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);
+    xcb_void_cookie_t open_font_cookie;
+    open_font_cookie = xcb_open_font_checked(xcb_connection,
+                                             xcb_font,
+                                             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;
+
+        char *dispname = getenv("DISPLAY");
+        if (dispname == NULL) {
+            dispname = ":0";
+        }
+        xkb_dpy = XkbOpenDisplay(dispname,
+                                 &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: %s\n", strerror(errno));
+            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 */
+    uint32_t mask = XCB_GC_FOREGROUND;
+    uint32_t vals[3] = { colors.bar_bg, colors.bar_bg, xcb_font };
+
+    statusline_clear = xcb_generate_id(xcb_connection);
+    xcb_void_cookie_t clear_ctx_cookie = xcb_create_gc_checked(xcb_connection,
+                                                               statusline_clear,
+                                                               xcb_root,
+                                                               mask,
+                                                               vals);
+
+    mask |= XCB_GC_BACKGROUND | XCB_GC_FONT;
+    vals[0] = colors.bar_fg;
+    statusline_ctx = xcb_generate_id(xcb_connection);
+    xcb_void_cookie_t sl_ctx_cookie = xcb_create_gc_checked(xcb_connection,
+                                                            statusline_ctx,
+                                                            xcb_root,
+                                                            mask,
+                                                            vals);
+
+    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,
+                                                               xcb_screen->width_in_pixels,
+                                                               xcb_screen->height_in_pixels);
+
+
+    /* The varios Watchers to communicate with xcb */
+    xcb_io = malloc(sizeof(ev_io));
+    xcb_prep = malloc(sizeof(ev_prepare));
+    xcb_chk = malloc(sizeof(ev_check));
+
+    ev_io_init(xcb_io, &xcb_io_cb, xcb_get_file_descriptor(xcb_connection), EV_READ);
+    ev_prepare_init(xcb_prep, &xcb_prep_cb);
+    ev_check_init(xcb_chk, &xcb_chk_cb);
+
+    ev_io_start(main_loop, xcb_io);
+    ev_prepare_start(main_loop, xcb_prep);
+    ev_check_start(main_loop, xcb_chk);
+
+    /* Now we get the atoms and save them in a nice data-structure */
+    get_atoms();
+
+    xcb_get_property_cookie_t path_cookie;
+    path_cookie = xcb_get_property_unchecked(xcb_connection,
+                                   0,
+                                   xcb_root,
+                                   atoms[I3_SOCKET_PATH],
+                                   XCB_GET_PROPERTY_TYPE_ANY,
+                                   0, PATH_MAX);
+
+    /* We check, if i3 set it's socket-path */
+    xcb_get_property_reply_t *path_reply = xcb_get_property_reply(xcb_connection,
+                                                                  path_cookie,
+                                                                  NULL);
+    char *path = NULL;
+    if (path_reply) {
+        int len = xcb_get_property_value_length(path_reply);
+        if (len != 0) {
+            path = strndup(xcb_get_property_value(path_reply), len);
+        }
+    }
+
+    /* Now we save the font-infos */
+    font_info = xcb_query_font_reply(xcb_connection,
+                                     query_font_cookie,
+                                     NULL);
+
+    if (xcb_request_failed(open_font_cookie, "Could not open font")) {
+        exit(EXIT_FAILURE);
+    }
+
+    font_height = font_info->font_ascent + font_info->font_descent;
+
+    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);
+
+    if (xcb_request_failed(sl_pm_cookie, "Could not allocate statusline-buffer") ||
+        xcb_request_failed(clear_ctx_cookie, "Could not allocate statusline-buffer-clearcontext") ||
+        xcb_request_failed(sl_ctx_cookie, "Could not allocate statusline-buffer-context")) {
+        exit(EXIT_FAILURE);
+    }
+
+    return path;
+}
+
+/*
+ * Cleanup the xcb-stuff.
+ * Called once, before the program terminates.
+ *
+ */
+void clean_xcb() {
+    i3_output *o_walk;
+    free_workspaces();
+    SLIST_FOREACH(o_walk, outputs, slist) {
+        destroy_window(o_walk);
+        FREE(o_walk->workspaces);
+        FREE(o_walk->name);
+    }
+    FREE_SLIST(outputs, i3_output);
+    FREE(outputs);
+
+    xcb_disconnect(xcb_connection);
+
+    ev_check_stop(main_loop, xcb_chk);
+    ev_prepare_stop(main_loop, xcb_prep);
+    ev_io_stop(main_loop, xcb_io);
+
+    FREE(xcb_chk);
+    FREE(xcb_prep);
+    FREE(xcb_io);
+    FREE(font_info);
+}
+
+/*
+ * Get the earlier requested atoms and save them in the prepared data-structure
+ *
+ */
+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"
+    DLOG("Got Atoms\n");
+}
+
+/*
+ * Destroy the bar of the specified output
+ *
+ */
+void destroy_window(i3_output *output) {
+    if (output == NULL) {
+        return;
+    }
+    if (output->bar == XCB_NONE) {
+        return;
+    }
+    xcb_destroy_window(xcb_connection, output->bar);
+    output->bar = XCB_NONE;
+}
+
+/*
+ * Reallocate the statusline-buffer
+ *
+ */
+void realloc_sl_buffer() {
+    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,
+                                                               xcb_screen->width_in_pixels,
+                                                               xcb_screen->height_in_pixels);
+
+    uint32_t mask = XCB_GC_FOREGROUND;
+    uint32_t vals[3] = { colors.bar_bg, colors.bar_bg, xcb_font };
+    xcb_free_gc(xcb_connection, statusline_clear);
+    statusline_clear = xcb_generate_id(xcb_connection);
+    xcb_void_cookie_t clear_ctx_cookie = xcb_create_gc_checked(xcb_connection,
+                                                               statusline_clear,
+                                                               xcb_root,
+                                                               mask,
+                                                               vals);
+
+    mask |= XCB_GC_BACKGROUND | XCB_GC_FONT;
+    vals[0] = colors.bar_fg;
+    statusline_ctx = xcb_generate_id(xcb_connection);
+    xcb_free_gc(xcb_connection, statusline_ctx);
+    xcb_void_cookie_t sl_ctx_cookie = xcb_create_gc_checked(xcb_connection,
+                                                            statusline_ctx,
+                                                            xcb_root,
+                                                            mask,
+                                                            vals);
+
+    if (xcb_request_failed(sl_pm_cookie, "Could not allocate statusline-buffer") ||
+        xcb_request_failed(clear_ctx_cookie, "Could not allocate statusline-buffer-clearcontext") ||
+        xcb_request_failed(sl_ctx_cookie, "Could not allocate statusline-buffer-context")) {
+        exit(EXIT_FAILURE);
+    }
+
+}
+
+/*
+ * Reconfigure all bars and create new for newly activated outputs
+ *
+ */
+void reconfig_windows() {
+    uint32_t mask;
+    uint32_t values[5];
+
+    i3_output *walk;
+    SLIST_FOREACH(walk, outputs, slist) {
+        if (!walk->active) {
+            /* If an output is not active, we destroy it's bar */
+            /* FIXME: Maybe we rather want to unmap? */
+            DLOG("Destroying window for output %s\n", walk->name);
+            destroy_window(walk);
+            continue;
+        }
+        if (walk->bar == XCB_NONE) {
+            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_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK;
+            /* Black background */
+            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[2] = XCB_EVENT_MASK_EXPOSURE;
+            if (!config.disable_ws) {
+                values[2] |= XCB_EVENT_MASK_BUTTON_PRESS;
+            }
+            xcb_void_cookie_t win_cookie = xcb_create_window_checked(xcb_connection,
+                                                                     xcb_screen->root_depth,
+                                                                     walk->bar,
+                                                                     xcb_root,
+                                                                     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_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_screen->root_depth,
+                                                                    walk->buffer,
+                                                                    walk->bar,
+                                                                    walk->rect.w,
+                                                                    walk->rect.h);
+
+            /* We want dock-windows (for now). When override_redirect is set, i3 is ignoring
+             * this one */
+            xcb_void_cookie_t dock_cookie = xcb_change_property(xcb_connection,
+                                                                XCB_PROP_MODE_REPLACE,
+                                                                walk->bar,
+                                                                atoms[_NET_WM_WINDOW_TYPE],
+                                                                XCB_ATOM_ATOM,
+                                                                32,
+                                                                1,
+                                                                (unsigned char*) &atoms[_NET_WM_WINDOW_TYPE_DOCK]);
+
+            /* We need to tell i3, where to reserve space for i3bar */
+            /* left, right, top, bottom, left_start_y, left_end_y,
+             * right_start_y, right_end_y, top_start_x, top_end_x, bottom_start_x,
+             * bottom_end_x */
+            /* A local struct to save the strut_partial property */
+            struct {
+                uint32_t left;
+                uint32_t right;
+                uint32_t top;
+                uint32_t bottom;
+                uint32_t left_start_y;
+                uint32_t left_end_y;
+                uint32_t right_start_y;
+                uint32_t right_end_y;
+                uint32_t top_start_x;
+                uint32_t top_end_x;
+                uint32_t bottom_start_x;
+                uint32_t bottom_end_x;
+            } __attribute__((__packed__)) strut_partial = {0,};
+            switch (config.dockpos) {
+                case DOCKPOS_NONE:
+                    break;
+                case DOCKPOS_TOP:
+                    strut_partial.top = font_height + 6;
+                    strut_partial.top_start_x = walk->rect.x;
+                    strut_partial.top_end_x = walk->rect.x + walk->rect.w;
+                    break;
+                case DOCKPOS_BOT:
+                    strut_partial.bottom = font_height + 6;
+                    strut_partial.bottom_start_x = walk->rect.x;
+                    strut_partial.bottom_end_x = walk->rect.x + walk->rect.w;
+                    break;
+            }
+            xcb_void_cookie_t strut_cookie = xcb_change_property(xcb_connection,
+                                                                 XCB_PROP_MODE_REPLACE,
+                                                                 walk->bar,
+                                                                 atoms[_NET_WM_STRUT_PARTIAL],
+                                                                 XCB_ATOM_CARDINAL,
+                                                                 32,
+                                                                 12,
+                                                                 &strut_partial);
+
+            /* 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), 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 (xcb_request_failed(win_cookie,   "Could not create window") ||
+                xcb_request_failed(pm_cookie,    "Could not create pixmap") ||
+                xcb_request_failed(dock_cookie,  "Could not set dock mode") ||
+                xcb_request_failed(strut_cookie, "Could not set strut")     ||
+                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 {
+            /* We already have a bar, so we just reconfigure it */
+            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("Destroying buffer for output %s", walk->name);
+            xcb_free_pixmap(xcb_connection, walk->buffer);
+
+            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);
+
+            DLOG("Recreating buffer for output %s", walk->name);
+            xcb_void_cookie_t pm_cookie = xcb_create_pixmap_checked(xcb_connection,
+                                                                    xcb_screen->root_depth,
+                                                                    walk->buffer,
+                                                                    walk->bar,
+                                                                    walk->rect.w,
+                                                                    walk->rect.h);
+
+            if (xcb_request_failed(cfg_cookie, "Could not reconfigure window")) {
+                exit(EXIT_FAILURE);
+            }
+            if (xcb_request_failed(pm_cookie,  "Could not create pixmap")) {
+                exit(EXIT_FAILURE);
+            }
+        }
+    }
+}
+
+/*
+ * Render the bars, with buttons and statusline
+ *
+ */
+void draw_bars() {
+    DLOG("Drawing Bars...\n");
+    int i = 0;
+
+    refresh_statusline();
+
+    i3_output *outputs_walk;
+    SLIST_FOREACH(outputs_walk, outputs, slist) {
+        if (!outputs_walk->active) {
+            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();
+        }
+        /* First things first: clear the backbuffer */
+        uint32_t color = colors.bar_bg;
+        xcb_change_gc(xcb_connection,
+                      outputs_walk->bargc,
+                      XCB_GC_FOREGROUND,
+                      &color);
+        xcb_rectangle_t rect = { 0, 0, outputs_walk->rect.w, font_height + 6 };
+        xcb_poly_fill_rectangle(xcb_connection,
+                                outputs_walk->buffer,
+                                outputs_walk->bargc,
+                                1,
+                                &rect);
+
+        if (statusline != NULL) {
+            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,
+                          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);
+        }
+
+        if (config.disable_ws) {
+            continue;
+        }
+
+        i3_ws *ws_walk;
+        TAILQ_FOREACH(ws_walk, outputs_walk->workspaces, tailq) {
+            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) {
+                if (!ws_walk->focused) {
+                    fg_color = colors.active_ws_fg;
+                    bg_color = colors.active_ws_bg;
+                } else {
+                    fg_color = colors.focus_ws_fg;
+                    bg_color = colors.focus_ws_bg;
+                }
+            }
+            if (ws_walk->urgent) {
+                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();
+            }
+            uint32_t mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND;
+            uint32_t vals[] = { bg_color, bg_color };
+            xcb_change_gc(xcb_connection,
+                          outputs_walk->bargc,
+                          mask,
+                          vals);
+            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);
+            xcb_change_gc(xcb_connection,
+                          outputs_walk->bargc,
+                          XCB_GC_FOREGROUND,
+                          &fg_color);
+            xcb_image_text_16(xcb_connection,
+                              ws_walk->name_glyphs,
+                              outputs_walk->buffer,
+                              outputs_walk->bargc,
+                              i + 5, font_info->font_ascent + 2,
+                              ws_walk->ucs2_name);
+            i += 10 + ws_walk->name_width;
+        }
+
+        i = 0;
+    }
+
+    redraw_bars();
+}
+
+/*
+ * 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) {
+        if (!outputs_walk->active) {
+            continue;
+        }
+        xcb_copy_area(xcb_connection,
+                      outputs_walk->buffer,
+                      outputs_walk->bar,
+                      outputs_walk->bargc,
+                      0, 0,
+                      0, 0,
+                      outputs_walk->rect.w,
+                      outputs_walk->rect.h);
+        xcb_flush(xcb_connection);
+    }
+}
diff --git a/i3bar/yajl-fallback/yajl/yajl_version.h b/i3bar/yajl-fallback/yajl/yajl_version.h
new file mode 100644 (file)
index 0000000..c6da442
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef YAJL_VERSION_H_
+#define YAJL_VERSION_H_
+/* Fallback for libyajl 1 which does not provide yajl_version.h */
+#define YAJL_MAJOR 1
+#define YAJL_MINOR 0
+#define YAJL_MICRO 0
+#endif