Commit Diff


commit - ddd1263ab9c22f3153ff5430e651adef00538e71
commit + 1d13acf5106c060e0290ec408449e42cb55ef798
blob - 53660a1dc7ee54f4a366ede3701b1296dffbaac6
blob + a901b4228b7ef72210324d4ad4d1ca42d1c69554
--- mymenu.c
+++ mymenu.c
@@ -1,8 +1,8 @@
 #include <stdio.h>
 #include <stdlib.h>
-#include <string.h>    // strdup, strnlen, ...
-#include <ctype.h>     // isalnum
-#include <locale.h>    // setlocale
+#include <string.h>		/* strdup, strlen */
+#include <ctype.h>		/* isalnum */
+#include <locale.h>		/* setlocale */
 #include <unistd.h>
 #include <sysexits.h>
 #include <stdbool.h>
@@ -12,9 +12,9 @@
 #include <stdint.h>
 
 #include <X11/Xlib.h>
-#include <X11/Xutil.h> // XLookupString
+#include <X11/Xutil.h>
 #include <X11/Xresource.h>
-#include <X11/Xcms.h>  // colors
+#include <X11/Xcms.h>
 #include <X11/keysym.h>
 
 #ifdef USE_XINERAMA
@@ -29,9 +29,6 @@
 # define VERSION "unknown"
 #endif
 
-// Comfy
-#define nil NULL
-
 #define resname "MyMenu"
 #define resclass "mymenu"
 
@@ -50,1671 +47,1825 @@
 
 #define EXPANDBITS(x)   ((0xffff * x) / 0xff)
 
-// If we don't have it or we don't want an "ignore case" completion
-// style, fall back to `strstr(3)`
+/*
+ * If we don't have or we don't want an "ignore case" completion
+ * style, fall back to `strstr(3)`
+ */
 #ifndef USE_STRCASESTR
 # define strcasestr strstr
 #endif
 
-// The number of char to read
+/* The number of char to read */
 #define STDIN_CHUNKS 64
 
-// the number of lines to allocate in advance
+/* The number of lines to allocate in advance */
 #define LINES_CHUNK 32
 
-// Abort if a is nil
-#define check_allocation(a) {                         \
-    if (a == nil) {                                   \
-      fprintf(stderr, "Could not allocate memory\n"); \
-      abort();                                        \
-    }                                                 \
+/* Abort on NULL */
+#define check_allocation(a) {				\
+    if (a == NULL) {					\
+      fprintf(stderr, "Could not allocate memory\n");	\
+      abort();						\
+    }							\
   }
 
 #define inner_height(r) (r->height - r->border_n - r->border_s)
 #define inner_width(r)  (r->width  - r->border_e - r->border_w)
 
-// The possible state of the event loop.
+/* The states of the event loop */
 enum state {LOOPING, OK_LOOP, OK, ERR};
 
-// for the drawing-related function. The text to be rendered could be
-// the prompt, a completion or a highlighted completion
+/* 
+ * For the drawing-related function. The text to be rendere could be
+ * the prompt, a completion or a highlighted completion
+ */
 enum text_type {PROMPT, COMPL, COMPL_HIGH};
 
-// These are the possible action to be performed after user input.
+/* These are the possible action to be performed after user input. */
 enum action {
-  EXIT,
-  CONFIRM,
-  CONFIRM_CONTINUE,
-  NEXT_COMPL,
-  PREV_COMPL,
-  DEL_CHAR,
-  DEL_WORD,
-  DEL_LINE,
-  ADD_CHAR,
-  TOGGLE_FIRST_SELECTED
+	EXIT,
+	CONFIRM,
+	CONFIRM_CONTINUE,
+	NEXT_COMPL,
+	PREV_COMPL,
+	DEL_CHAR,
+	DEL_WORD,
+	DEL_LINE,
+	ADD_CHAR,
+	TOGGLE_FIRST_SELECTED
 };
 
-// A big set of values that needs to be carried around (for drawing
-// functions). A struct to rule them all
+/* A big set of values that needs to be carried around for drawing. A
+big struct to rule them all */
 struct rendering {
-  Display *d; // connection to xorg
-  Window w;
-  int width;
-  int height;
-  int padding;
-  int x_zero; // the "zero" on the x axis (may not be 0 'cause the border)
-  int y_zero; // the same a x_zero, only for the y axis
+	Display		*d;	/* Connection to xorg */
+	Window		w;
+	int		width;
+	int		height;
+	int		padding;
+	int		x_zero; /* the "zero" on the x axis (may not be exactly 0 'cause the borders) */
+	int		y_zero; /* like x_zero but for the y axis */
 
-  size_t offset; // a scrolling offset
+	size_t		offset; /* scroll offset */
 
-  bool free_text;
-  bool first_selected;
-  bool multiple_select;
+	bool		free_text;
+	bool		first_selected;
+	bool		multiple_select;
 
-  // The four border
-  int border_n;
-  int border_e;
-  int border_s;
-  int border_w;
+	/* four border width */
+	int		border_n;
+	int		border_e;
+	int		border_s;
+	int		border_w;
 
-  bool horizontal_layout;
+	bool		horizontal_layout;
 
-  // the prompt
-  char *ps1;
-  int ps1len;
+	/* prompt */
+	char		*ps1;
+	int		ps1len;
 
-  XIC xic;
+	XIC		xic;
 
-  // colors
-  GC prompt;
-  GC prompt_bg;
-  GC completion;
-  GC completion_bg;
-  GC completion_highlighted;
-  GC completion_highlighted_bg;
-  GC border_n_bg;
-  GC border_e_bg;
-  GC border_s_bg;
-  GC border_w_bg;
+	/* colors */
+	GC		prompt;
+	GC		prompt_bg;
+	GC		completion;
+	GC		completion_bg;
+	GC		completion_highlighted;
+	GC		completion_highlighted_bg;
+	GC		border_n_bg;
+	GC		border_e_bg;
+	GC		border_s_bg;
+	GC		border_w_bg;
 #ifdef USE_XFT
-  XftFont *font;
-  XftDraw *xftdraw;
-  XftColor xft_prompt;
-  XftColor xft_completion;
-  XftColor xft_completion_highlighted;
+	XftFont		*font;
+	XftDraw		*xftdraw;
+	XftColor	xft_prompt;
+	XftColor	xft_completion;
+	XftColor	xft_completion_highlighted;
 #else
-  XFontSet *font;
+	XFontSet	font;
 #endif
 };
 
 struct completion {
-  char *completion;
-  char *rcompletion;
+	char	*completion;
+	char	*rcompletion;
 };
 
-// Wrap the linked list of completions
+/* Wrap the linked list of completions */
 struct completions {
-  struct completion *completions;
-  ssize_t selected;
-  size_t lenght;
+	struct completion	*completions;
+	ssize_t			selected;
+	size_t			length;
 };
 
-// return a newly allocated (and empty) completion list
-struct completions *compls_new(size_t lenght) {
-  struct completions *cs = malloc(sizeof(struct completions));
-
-  if (cs == nil)
-    return cs;
-
-  cs->completions = calloc(lenght, sizeof(struct completion));
-  if (cs->completions == nil) {
-    free(cs);
-    return nil;
-  }
-
-  cs->selected = -1;
-  cs->lenght = lenght;
-  return cs;
-}
-
 /* idea stolen from lemonbar. ty lemonboy */
 typedef union {
-  struct {
-    uint8_t b;
-    uint8_t g;
-    uint8_t r;
-    uint8_t a;
-  };
-  uint32_t v;
+	struct {
+		uint8_t	b;
+		uint8_t	g;
+		uint8_t	r;
+		uint8_t	a;
+	};
+	uint32_t	v;
 } rgba_t;
 
-// Delete the wrapper and the whole list
-void compls_delete(struct completions *cs) {
-  if (cs == nil)
-    return;
+/* Return a newly allocated (and empty) completion list */
+struct completions *
+compls_new(size_t length)
+{
+	struct completions *cs = malloc(sizeof(struct completions));
 
-  free(cs->completions);
-  free(cs);
-}
+	if (cs == NULL)
+		return cs;
 
-// create a completion list from a text and the list of possible
-// completions (null terminated). Expects a non-null `cs'. lines and
-// vlines should have the same lenght OR vlines is null
-void filter(struct completions *cs, char *text, char **lines, char **vlines) {
-  size_t index = 0;
-  size_t matching = 0;
+	cs->completions = calloc(length, sizeof(struct completion));
+	if (cs->completions == NULL) {
+		free(cs);
+		return NULL;
+	}
 
-  if (vlines == nil)
-    vlines = lines;
+	cs->selected = -1;
+	cs->length = length;
+	return cs;
+}
 
-  while (true) {
-    if (lines[index] == nil)
-      break;
+/* Delete the wrapper and the whole list */
+void
+compls_delete(struct completions *cs)
+{
+	if (cs == NULL)
+		return;
 
-    char *l = vlines[index] != nil ? vlines[index] : lines[index];
+	free(cs->completions);
+	free(cs);
+}
 
-    if (strcasestr(l, text) != nil) {
-      struct completion *c = &cs->completions[matching];
-      c->completion  = l;
-      c->rcompletion = lines[index];
-      matching++;
-    }
+/* 
+ * Create a completion list from a text and the list of possible
+ * completions (null terminated). Expects a non-null `cs'. `lines' and
+ * `vlines' should have the same length OR `vlines' is NULL.
+ */
+void
+filter(struct completions *cs, char *text, char **lines, char **vlines)
+{
+	size_t	index = 0;
+	size_t	matching = 0;
 
-    index++;
-  }
-  cs->lenght = matching;
-  cs->selected = -1;
-}
+	if (vlines == NULL)
+		vlines = lines;
 
-// update the given completion, that is: clean the old cs & generate a new one.
-void update_completions(struct completions *cs, char *text, char **lines, char **vlines, bool first_selected) {
-  filter(cs, text, lines, vlines);
-  if (first_selected && cs->lenght > 0)
-    cs->selected = 0;
-}
+	while (true) {
+		if (lines[index] == NULL)
+			break;
 
-// select the next, or the previous, selection and update some
-// state. `text' will be updated with the text of the completion and
-// `textlen' with the new lenght of `text'. If the memory cannot be
-// allocated, `status' will be set to `ERR'.
-void complete(struct completions *cs, bool first_selected, bool p, char **text, int *textlen, enum state *status) {
-  if (cs == nil || cs->lenght == 0)
-    return;
+		char *l = vlines[index] != NULL ? vlines[index] : lines[index];
 
-  // if the first is always selected, and the first entry is different
-  // from the text, expand the text and return
-  if (first_selected
-      && cs->selected == 0
-      && strcmp(cs->completions->completion, *text) != 0
-      && !p) {
-    free(*text);
-    *text = strdup(cs->completions->completion);
-    if (text == nil) {
-      *status = ERR;
-      return;
-    }
-    *textlen = strlen(*text);
-    return;
-  }
+		if (strcasestr(l, text) != NULL) {
+			struct completion *c = &cs->completions[matching];
+			c->completion  = l;
+			c->rcompletion = lines[index];
+			matching++;
+		}
 
-  int index = cs->selected;
+		index++;
+	}
+	cs->length = matching;
+	cs->selected = -1;
+}
 
-  if (index == -1 && p)
-    index = 0;
-  index = cs->selected = (cs->lenght + (p ? index - 1 : index + 1)) % cs->lenght;
+/* Update the given completion */
+void
+update_completions(struct completions *cs, char *text, char **lines, char **vlines, bool first_selected)
+{
+	filter(cs, text, lines, vlines);
+	if (first_selected && cs->length > 0)
+		cs->selected = 0;
+}
 
-  struct completion *n = &cs->completions[cs->selected];
+/*
+ * Select the next or previous selection and update some state. `text'
+ * will be updated with the text of the completion and `textlen' with
+ * the new length. If the memory cannot be allocated `status' will be
+ * set to `ERR'.
+ */
+void
+complete(struct completions *cs, bool first_selected, bool p, char **text, int *textlen, enum state *status)
+{
+	struct completion	*n;
+	int			index;
 
-  free(*text);
-  *text = strdup(n->completion);
-  if (text == nil) {
-    fprintf(stderr, "Memory allocation error!\n");
-    *status = ERR;
-    return;
-  }
-  *textlen = strlen(*text);
-}
+	if (cs == NULL || cs->length == 0)
+		return;
 
-// push the character c at the end of the string pointed by p
-int pushc(char **p, int maxlen, char c) {
-  int len = strnlen(*p, maxlen);
+	/*
+	 * If the first is always selected and the first entry is
+	 * different from the text, expand the text and return
+	 */
+	if (first_selected
+	    && cs->selected == 0
+	    && strcmp(cs->completions->completion, *text) != 0
+	    && !p) {
+		free(*text);
+		*text = strdup(cs->completions->completion);
+		if (text == NULL) {
+			*status = ERR;
+			return;
+		}
+		*textlen = strlen(*text);
+		return;
+	}
 
-  if (!(len < maxlen -2)) {
-    maxlen += maxlen >> 1;
-    char *newptr = realloc(*p, maxlen);
-    if (newptr == nil) { // bad!
-      return -1;
-    }
-    *p = newptr;
-  }
+	index = cs->selected;
 
-  (*p)[len] = c;
-  (*p)[len+1] = '\0';
-  return maxlen;
-}
+	if (index == -1 && p)
+		index = 0;
+	index = cs->selected = (cs->length + (p ? index - 1 : index + 1)) % cs->length;
 
-// remove the last rune from the *utf8* string! This is different from
-// just setting the last byte to 0 (in some cases ofc). Return a
-// pointer (e) to the last non zero char. If e < p then p is empty!
-char* popc(char *p) {
-  int len = strlen(p);
-  if (len == 0)
-    return p;
+	n = &cs->completions[cs->selected];
 
-  char *e = p + len - 1;
-
-  do {
-    char c = *e;
-    *e = 0;
-    e--;
-
-    // if c is a starting byte (11......) or is under U+007F (ascii,
-    // basically) we're done
-    if (((c & 0x80) && (c & 0x40)) || !(c & 0x80))
-      break;
-  } while (e >= p);
-
-  return e;
+	free(*text);
+	*text = strdup(n->completion);
+	if (text == NULL) {
+		fprintf(stderr, "Memory allocation error!\n");
+		*status = ERR;
+		return;
+	}
+	*textlen = strlen(*text);
 }
 
-// remove the last word plus trailing whitespaces from the give string
-void popw(char *w) {
-  int len = strlen(w);
-  if (len == 0)
-    return;
+/* Push the character c at the end of the string pointed by p */
+int
+pushc(char **p, int maxlen, char c)
+{
+	int len = strnlen(*p, maxlen);
 
-  bool in_word = true;
-  while (true) {
-    char *e = popc(w);
+	if (!(len < maxlen -2)) {
+		char *newptr;
 
-    if (e < w)
-      return;
+		maxlen += maxlen >> 1;
+		newptr = realloc(*p, maxlen);
+		if (newptr == NULL) /* bad */
+			return -1;
+		*p = newptr;
+	}
 
-    if (in_word && isspace(*e))
-      in_word = false;
-
-    if (!in_word && !isspace(*e))
-      return;
-  }
+	(*p)[len] = c;
+	(*p)[len+1] = '\0';
+	return maxlen;
 }
 
-// If the string is surrounded by quotes (`"`) remove them and replace
-// every `\"` in the string with `"`
-char *normalize_str(const char *str) {
-  int len = strlen(str);
-  if (len == 0)
-    return nil;
+/* 
+ * Remove the last rune from the *UTF-8* string! This is different
+ * from just setting the last byte to 0 (in some cases ofc). Return a
+ * pointer (e) to the last nonzero char. If e < p then p is empty!
+ */
+char *
+popc(char *p)
+{
+	int len = strlen(p);
+	char *e;
 
-  char *s = calloc(len, sizeof(char));
-  check_allocation(s);
-  int p = 0;
-  while (*str) {
-    char c = *str;
-    if (*str == '\\') {
-      if (*(str + 1)) {
-        s[p] = *(str + 1);
-        p++;
-        str += 2; // skip this and the next char
-        continue;
-      } else {
-        break;
-      }
-    }
-    if (c == '"') {
-      str++; // skip only this char
-      continue;
-    }
-    s[p] = c;
-    p++;
-    str++;
-  }
-  return s;
-}
+	if (len == 0)
+		return p;
 
-size_t read_stdin(char **buf) {
-  size_t offset = 0;
-  size_t len = STDIN_CHUNKS;
-  *buf = malloc(len * sizeof(char));
-  if (*buf == nil)
-    goto err;
+	e = p + len - 1;
 
-  while (true) {
-    ssize_t r = read(0, *buf + offset, STDIN_CHUNKS);
-    if (r < 1)
-      return len;
+	do {
+		char c = *e;
 
-    offset += r;
+		*e = 0;
+		e--;
 
-    len += STDIN_CHUNKS;
-    *buf = realloc(*buf, len);
-    if (*buf == nil)
-      goto err;
+		/*
+		 * If c is a starting byte (11......) or is under
+		 * U+007F we're done.
+		 */
+		if (((c & 0x80) && (c & 0x40)) || !(c & 0x80))
+			break;
+	} while (e >= p);
 
-    for (size_t i = offset; i < len; ++i)
-      (*buf)[i] = '\0';
-  }
-
- err:
-  fprintf(stderr, "Error in allocating memory for stdin.\n");
-  exit(EX_UNAVAILABLE);
+	return e;
 }
 
-//
-size_t readlines(char ***lns, char **buf) {
-  *buf = nil;
-  size_t len = read_stdin(buf);
+/* Remove the last word plus trailing white spaces from the given string */
+void
+popw(char *w)
+{
+	int len;
+	bool in_word;
 
-  size_t ll = LINES_CHUNK;
-  *lns = malloc(ll * sizeof(char*));
-  if (*lns == nil) goto err;
+	len = strlen(w);
+	if (len == 0)
+		return;
 
-  size_t lines = 0;
-  bool in_line = false;
-  for (size_t i = 0; i < len; i++) {
-    char c = (*buf)[i];
+	in_word = true;
+	while (true) {
+		char *e = popc(w);
 
-    if (c == '\0')
-      break;
+		if (e < w)
+			return;
 
-    if (c == '\n')
-      (*buf)[i] = '\0';
+		if (in_word && isspace(*e))
+			in_word = false;
 
-    if (in_line && c == '\n')
-      in_line = false;
+		if (!in_word && !isspace(*e))
+			return;
+	}
+}
 
-    if (!in_line && c != '\n') {
-      in_line = true;
-      (*lns)[lines] = (*buf) + i;
-      lines++;
+/* 
+ * If the string is surrounded by quates (`"') remove them and replace
+ * every `\"' in the string with a single double-quote.
+ */
+char *
+normalize_str(const char *str)
+{
+	int len, p;
+	char *s;
 
-      if (lines == ll) { // resize
-        ll += LINES_CHUNK;
-        *lns = realloc(*lns, ll * sizeof(char*));
-        if (*lns == nil) goto err;
-      }
-    }
-  }
+	len = strlen(str);
+	if (len == 0)
+		return NULL;
 
-  (*lns)[lines] = nil;
+	s = calloc(len, sizeof(char));
+	check_allocation(s);
+	p = 0;
 
-  return lines;
+	while (*str) {
+		char c = *str;
 
- err:
-  fprintf(stderr, "Error in memory allocation.\n");
-  exit(EX_UNAVAILABLE);
-}
+		if (*str == '\\') {
+			if (*(str + 1)) {
+				s[p] = *(str + 1);
+				p++;
+				str += 2; /* skip this and the next char */
+				continue;
+			} else
+				break;
+		}
+		if (c == '"') {
+			str++;	/* skip only this char */
+			continue;
+		}
+		s[p] = c;
+		p++;
+		str++;
+	}
 
-// Compute the dimension of the string str once rendered, return the
-// width and save the width and the height in ret_width and ret_height
-int text_extents(char *str, int len, struct rendering *r, int *ret_width, int *ret_height) {
-  int height;
-  int width;
-#ifdef USE_XFT
-  XGlyphInfo gi;
-  XftTextExtentsUtf8(r->d, r->font, str, len, &gi);
-  height = r->font->ascent - r->font->descent;
-  width = gi.width - gi.x;
-#else
-  XRectangle rect;
-  XmbTextExtents(*r->font, str, len, nil, &rect);
-  height = rect.height;
-  width = rect.width;
-#endif
-  if (ret_width != nil)  *ret_width = width;
-  if (ret_height != nil) *ret_height = height;
-  return width;
+	return s;
 }
 
-// Draw the string str
-void draw_string(char *str, int len, int x, int y, struct rendering *r, enum text_type tt) {
-#ifdef USE_XFT
-  XftColor xftcolor;
-  if (tt == PROMPT)     xftcolor = r->xft_prompt;
-  if (tt == COMPL)      xftcolor = r->xft_completion;
-  if (tt == COMPL_HIGH) xftcolor = r->xft_completion_highlighted;
+size_t
+read_stdin(char **buf)
+{
+	size_t offset = 0;
+	size_t len = STDIN_CHUNKS;
 
-  XftDrawStringUtf8(r->xftdraw, &xftcolor, r->font, x, y, str, len);
-#else
-  GC gc;
-  if (tt == PROMPT)     gc = r->prompt;
-  if (tt == COMPL)      gc = r->completion;
-  if (tt == COMPL_HIGH) gc = r->completion_highlighted;
-  Xutf8DrawString(r->d, r->w, *r->font, gc, x, y, str, len);
-#endif
-}
+	*buf = malloc(len * sizeof(char));
+	if (*buf == NULL)
+		goto err;
 
-// Duplicate the string str and substitute every space with a 'n'
-char *strdupn(char *str) {
-  int len = strlen(str);
+	while (true) {
+		ssize_t r;
+		size_t i;
 
-  if (str == nil || len == 0)
-    return nil;
+		r = read(0, *buf + offset, STDIN_CHUNKS);
 
-  char *dup = strdup(str);
-  if (dup == nil)
-    return nil;
+		if (r < 1)
+			return len;
 
-  for (int i = 0; i < len; ++i)
-    if (dup[i] == ' ')
-      dup[i] = 'n';
+		offset += r;
 
-  return dup;
+		len += STDIN_CHUNKS;
+		*buf = realloc(*buf, len);
+		if (*buf == NULL)
+			goto err;
+
+		for (i = offset; i < len; ++i)
+			(*buf)[i] = '\0';
+	}
+
+ err:
+	fprintf(stderr, "Error in allocating memory for stdin.\n");
+	exit(EX_UNAVAILABLE);
 }
 
-// |------------------|----------------------------------------------|
-// | 20 char text     | completion | completion | completion | compl |
-// |------------------|----------------------------------------------|
-void draw_horizontally(struct rendering *r, char *text, struct completions *cs) {
-  int prompt_width = 20; // char
+size_t
+readlines(char ***lns, char **buf)
+{
+	size_t len, ll, lines;
+	bool in_line = false;
 
-  char *ps1dup = strdupn(r->ps1);
-  int width, height;
-  int ps1xlen = text_extents(ps1dup != nil ? ps1dup : r->ps1, r->ps1len, r, &width, &height);
-  free(ps1dup);
-  int start_at = ps1xlen;
+	lines = 0;
 
-  start_at = r->x_zero + text_extents("n", 1, r, nil, nil);
-  start_at = start_at * prompt_width + r->padding;
+	*buf = NULL;
+	len = read_stdin(buf);
 
-  int texty = (inner_height(r) + height + r->y_zero) / 2;
+	ll = LINES_CHUNK;
+	*lns = malloc(ll * sizeof(char*));
 
-  XFillRectangle(r->d, r->w, r->prompt_bg, r->x_zero, r->y_zero, start_at, inner_height(r));
+	if (*lns == NULL)
+		goto err;
 
-  int text_len = strlen(text);
-  if (text_len > prompt_width)
-    text = text + (text_len - prompt_width);
-  draw_string(r->ps1, r->ps1len, r->x_zero + r->padding, texty, r, PROMPT);
-  draw_string(text, MIN(text_len, prompt_width), r->x_zero + r->padding + ps1xlen, texty, r, PROMPT);
+	for (size_t i = 0; i < len; i++) {
+		char c = (*buf)[i];
 
-  XFillRectangle(r->d, r->w, r->completion_bg, start_at, r->y_zero, r->width, inner_height(r));
+		if (c == '\0')
+			break;
 
-  for (size_t i = r->offset; i < cs->lenght; ++i) {
-    struct completion *c = &cs->completions[i];
+		if (c == '\n')
+			(*buf)[i] = '\0';
 
-    enum text_type tt = cs->selected == (ssize_t)i ? COMPL_HIGH : COMPL;
-    GC h = cs->selected == (ssize_t)i ? r->completion_highlighted_bg : r->completion_bg;
+		if (in_line && c == '\n')
+			in_line = false;
 
-    int len = strlen(c->completion);
-    int text_width = text_extents(c->completion, len, r, nil, nil);
+		if (!in_line && c != '\n') {
+			in_line = true;
+			(*lns)[lines] = (*buf) + i;
+			lines++;
 
-    XFillRectangle(r->d, r->w, h, start_at, r->y_zero, text_width + r->padding*2, inner_height(r));
+			if (lines == ll) { // resize
+				ll += LINES_CHUNK;
+				*lns = realloc(*lns, ll * sizeof(char*));
+				if (*lns == NULL)
+					goto err;
+			}
+		}
+	}
 
-    draw_string(c->completion, len, start_at + r->padding, texty, r, tt);
+	(*lns)[lines] = NULL;
 
-    start_at += text_width + r->padding * 2;
+	return lines;
 
-    if (start_at > inner_width(r))
-      break; // don't draw completion if the space isn't enough
-  }
+ err:
+	fprintf(stderr, "Error in memory allocation.\n");
+	exit(EX_UNAVAILABLE);
 }
 
-// |-----------------------------------------------------------------|
-// |  prompt                                                         |
-// |-----------------------------------------------------------------|
-// |  completion                                                     |
-// |-----------------------------------------------------------------|
-// |  completion                                                     |
-// |-----------------------------------------------------------------|
-void draw_vertically(struct rendering *r, char *text, struct completions *cs) {
-  int height, width;
-  text_extents("fjpgl", 5, r, nil, &height);
-  int start_at = r->padding*2 + height;
+/*
+ * Compute the dimensions of the string str once rendered.
+ * It'll return the width and set ret_width and ret_height if not NULL
+ */
+int
+text_extents(char *str, int len, struct rendering *r, int *ret_width, int *ret_height)
+{
+	int height;
+	int width;
+#ifdef USE_XFT
+	XGlyphInfo gi;
+	XftTextExtentsUtf8(r->d, r->font, str, len, &gi);
+	height = r->font->ascent - r->font->descent;
+	width = gi.width - gi.x;
+#else
+	XRectangle rect;
+	XmbTextExtents(r->font, str, len, NULL, &rect);
+	height = rect.height;
+	width = rect.width;
+#endif
+	if (ret_width != NULL)  *ret_width = width;
+	if (ret_height != NULL) *ret_height = height;
+	return width;
+}
 
-  XFillRectangle(r->d, r->w, r->completion_bg, r->x_zero, r->y_zero, r->width, r->height);
-  XFillRectangle(r->d, r->w, r->prompt_bg, r->x_zero, r->y_zero, r->width, start_at);
+void
+draw_string(char *str, int len, int x, int y, struct rendering *r, enum text_type tt)
+{
+#ifdef USE_XFT
+	XftColor xftcolor;
+	if (tt == PROMPT)     xftcolor = r->xft_prompt;
+	if (tt == COMPL)      xftcolor = r->xft_completion;
+	if (tt == COMPL_HIGH) xftcolor = r->xft_completion_highlighted;
 
-  char *ps1dup = strdupn(r->ps1);
-  int ps1xlen = text_extents(ps1dup != nil ? ps1dup : r->ps1, r->ps1len, r, nil, nil);
-  free(ps1dup);
+	XftDrawStringUtf8(r->xftdraw, &xftcolor, r->font, x, y, str, len);
+#else
+	GC gc;
+	if (tt == PROMPT)     gc = r->prompt;
+	if (tt == COMPL)      gc = r->completion;
+	if (tt == COMPL_HIGH) gc = r->completion_highlighted;
+	Xutf8DrawString(r->d, r->w, r->font, gc, x, y, str, len);
+#endif
+}
 
-  draw_string(r->ps1, r->ps1len, r->x_zero + r->padding, r->y_zero + height + r->padding, r, PROMPT);
-  draw_string(text, strlen(text), r->x_zero + r->padding + ps1xlen, r->y_zero + height + r->padding, r, PROMPT);
+/* Duplicate the string and substitute every space with a 'n` */
+char *
+strdupn(char *str)
+{
+	int len, i;
+	char *dup;
 
-  start_at += r->y_zero;
+	len = strlen(str);
 
-  for (size_t i = r->offset; i < cs->lenght; ++i){
-    struct completion *c = &cs->completions[i];
-    enum text_type tt = cs->selected == (ssize_t)i ? COMPL_HIGH : COMPL;
-    GC h = cs->selected == (ssize_t)i ? r->completion_highlighted_bg : r->completion_bg;
+	if (str == NULL || len == 0)
+		return NULL;
 
-    int len = strlen(c->completion);
-    text_extents(c->completion, len, r, &width, &height);
-    XFillRectangle(r->d, r->w, h, r->x_zero, start_at, inner_width(r), height + r->padding*2);
-    draw_string(c->completion, len, r->x_zero + r->padding, start_at + height + r->padding, r, tt);
+	dup = strdup(str);
+	if (dup == NULL)
+		return NULL;
 
-    start_at += height + r->padding *2;
+	for (i = 0; i < len; ++i)
+		if (dup[i] == ' ')
+			dup[i] = 'n';
 
-    if (start_at > inner_height(r))
-      break; // don't draw completion if the space isn't enough
-  }
+	return dup;
 }
 
-void draw(struct rendering *r, char *text, struct completions *cs) {
-  if (r->horizontal_layout)
-    draw_horizontally(r, text, cs);
-  else
-    draw_vertically(r, text, cs);
+/* |------------------|----------------------------------------------| */
+/* | 20 char text     | completion | completion | completion | compl | */
+/* |------------------|----------------------------------------------| */
+void
+draw_horizontally(struct rendering *r, char *text, struct completions *cs)
+{
+	size_t	i;
+	int	prompt_width, ps1xlen, start_at;
+	int	width, height;
+	int	texty, textlen;
+	char	*ps1dup;
 
-  // draw the borders
+	prompt_width = 20; 	/* TODO: calculate the correct amount of char to show */
 
-  if (r->border_w != 0)
-    XFillRectangle(r->d, r->w, r->border_w_bg, 0, 0, r->border_w, r->height);
+	ps1dup = strdupn(r->ps1);
+	ps1xlen = text_extents(ps1dup != NULL ? ps1dup : r->ps1, r->ps1len, r, &width, &height);
+	free(ps1dup);
 
-  if (r->border_e != 0)
-    XFillRectangle(r->d, r->w, r->border_e_bg, r->width - r->border_e, 0, r->border_e, r->height);
+	start_at  = r->x_zero + text_extents("n", 1, r, NULL, NULL);
+	start_at *= prompt_width;
+	start_at += r->padding;
 
-  if (r->border_n != 0)
-    XFillRectangle(r->d, r->w, r->border_n_bg, 0, 0, r->width, r->border_n);
+	texty = (inner_height(r) + height + r->y_zero) / 2;
 
-  if (r->border_s != 0)
-    XFillRectangle(r->d, r->w, r->border_s_bg, 0, r->height - r->border_s, r->width, r->border_s);
+	XFillRectangle(r->d, r->w, r->prompt_bg, r->x_zero, r->y_zero, start_at, inner_height(r));
 
-  // send all the work to x
-  XFlush(r->d);
+	textlen = strlen(text);
+	if (textlen > prompt_width)
+		text = text + textlen - prompt_width;
+
+	draw_string(r->ps1, r->ps1len, r->x_zero + r->padding, texty, r, PROMPT);
+	draw_string(text, MIN(textlen, prompt_width), r->x_zero + r->padding + ps1xlen, texty, r, PROMPT);
+
+	XFillRectangle(r->d, r->w, r->completion_bg, start_at, r->y_zero, r->width, inner_height(r));
+
+	for (i = r->offset; i < cs->length; ++i) {
+		struct completion	*c;
+		enum text_type		tt;
+		GC			h;
+		int			len, text_width;
+
+		c = &cs->completions[i];
+		tt = cs->selected == (ssize_t)i ? COMPL_HIGH : COMPL;
+		h  = cs->selected == (ssize_t)i ? r->completion_highlighted_bg : r->completion_bg;
+		len = strlen(c->completion);
+		text_width = text_extents(c->completion, len, r, NULL, NULL);
+
+		XFillRectangle(r->d, r->w, h, start_at, r->y_zero, text_width + r->padding*2, inner_height(r));
+		draw_string(c->completion, len, start_at + r->padding, texty, r, tt);
+
+		start_at += text_width + r->padding * 2;
+
+		if (start_at > inner_width(r))
+			break; 	/* don't draw completion out of the window */
+	}
 }
 
-/* Set some WM stuff */
-void set_win_atoms_hints(Display *d, Window w, int width, int height) {
-  Atom type;
-  type = XInternAtom(d, "_NET_WM_WINDOW_TYPE_DOCK", false);
-  XChangeProperty(
-                  d,
-                  w,
-                  XInternAtom(d, "_NET_WM_WINDOW_TYPE", false),
-                  XInternAtom(d, "ATOM", false),
-                  32,
-                  PropModeReplace,
-                  (unsigned char *)&type,
-                  1
-                  );
+/* |-----------------------------------------------------------------| */
+/* |  prompt                                                         | */
+/* |-----------------------------------------------------------------| */
+/* |  completion                                                     | */
+/* |-----------------------------------------------------------------| */
+/* |  completion                                                     | */
+/* |-----------------------------------------------------------------| */
+void
+draw_vertically(struct rendering *r, char *text, struct completions *cs)
+{
+	size_t	i;
+	int	height, start_at;
+	int	ps1xlen;
+	char	*ps1dup;
 
-  /* some window managers honor this properties */
-  type = XInternAtom(d, "_NET_WM_STATE_ABOVE", false);
-  XChangeProperty(d,
-                  w,
-                  XInternAtom(d, "_NET_WM_STATE", false),
-                  XInternAtom(d, "ATOM", false),
-                  32,
-                  PropModeReplace,
-                  (unsigned char *)&type,
-                  1
-                  );
+	text_extents("fjpgl", 5, r, NULL, &height);
+	start_at = r->padding * 2 + height;
 
-  type = XInternAtom(d, "_NET_WM_STATE_FOCUSED", false);
-  XChangeProperty(d,
-                  w,
-                  XInternAtom(d, "_NET_WM_STATE", false),
-                  XInternAtom(d, "ATOM", false),
-                  32,
-                  PropModeAppend,
-                  (unsigned char *)&type,
-                  1
-                  );
+	XFillRectangle(r->d, r->w, r->completion_bg, r->x_zero, r->y_zero, r->width, r->height);
+	XFillRectangle(r->d, r->w, r->prompt_bg, r->x_zero, r->y_zero, r->width, start_at);
 
-  // setting window hints
-  XClassHint *class_hint = XAllocClassHint();
-  if (class_hint == nil) {
-    fprintf(stderr, "Could not allocate memory for class hint\n");
-    exit(EX_UNAVAILABLE);
-  }
-  class_hint->res_name = resname;
-  class_hint->res_class = resclass;
-  XSetClassHint(d, w, class_hint);
-  XFree(class_hint);
+	ps1dup = strdupn(r->ps1);
+	ps1xlen = text_extents(ps1dup == NULL ? ps1dup : r->ps1, r->ps1len, r, NULL, NULL);
+	free(ps1dup);
 
-  XSizeHints *size_hint = XAllocSizeHints();
-  if (size_hint == nil) {
-    fprintf(stderr, "Could not allocate memory for size hint\n");
-    exit(EX_UNAVAILABLE);
-  }
-  size_hint->flags = PMinSize | PBaseSize;
-  size_hint->min_width = width;
-  size_hint->base_width = width;
-  size_hint->min_height = height;
-  size_hint->base_height = height;
+	draw_string(r->ps1, r->ps1len, r->x_zero + r->padding, r->y_zero + height + r->padding, r, PROMPT);
+	draw_string(text, strlen(text), r->x_zero + r->padding + ps1xlen, r->y_zero + height + r->padding, r, PROMPT);
 
-  XFlush(d);
+	start_at += r->y_zero;
+
+	for (i = r->offset; i < cs->length; ++i) {
+		struct completion	*c;
+		enum text_type		tt;
+		GC			h;
+		int			len;
+
+		c   = &cs->completions[i];
+		tt  = cs->selected == (ssize_t)i ? COMPL_HIGH : COMPL;
+		h   = cs->selected == (ssize_t)i ? r->completion_highlighted_bg : r->completion_bg;
+		len = strlen(c->completion);
+
+		XFillRectangle(r->d, r->w, h, r->x_zero, start_at, inner_width(r), height + r->padding*2);
+		draw_string(c->completion, len, r->x_zero + r->padding, start_at + height + r->padding, r, tt);
+
+		start_at += height + r->padding * 2;
+
+		if (start_at > inner_height(r))
+			break;	/* don't draw completion out of the window */
+	}
 }
 
-// write the width and height of the window `w' respectively in `width'
-// and `height'.
-void get_wh(Display *d, Window *w, int *width, int *height) {
-  XWindowAttributes win_attr;
-  XGetWindowAttributes(d, *w, &win_attr);
-  *height = win_attr.height;
-  *width = win_attr.width;
-}
+void
+draw(struct rendering *r, char *text, struct completions *cs)
+{
+	if (r->horizontal_layout)
+		draw_horizontally(r, text, cs);
+	else
+		draw_vertically(r, text, cs);
+
+	/* Draw the borders */
+	if (r->border_w != 0)
+		XFillRectangle(r->d, r->w, r->border_w_bg, 0, 0, r->border_w, r->height);
+
+	if (r->border_e != 0)
+		XFillRectangle(r->d, r->w, r->border_e_bg, r->width - r->border_e, 0, r->border_e, r->height);
+
+	if (r->border_n != 0)
+		XFillRectangle(r->d, r->w, r->border_n_bg, 0, 0, r->width, r->border_n);
+
+	if (r->border_s != 0)
+		XFillRectangle(r->d, r->w, r->border_s_bg, 0, r->height - r->border_s, r->width, r->border_s);
+
+	/* render! */
+	XFlush(r->d);
+}
+
+/* Set some WM stuff */
+void
+set_win_atoms_hints(Display *d, Window w, int width, int height)
+{
+	Atom		type;
+	XClassHint	*class_hint;
+	XSizeHints	*size_hint;
+
+	type = XInternAtom(d, "_NET_WM_WINDOW_TYPE_DOCK", false);
+	XChangeProperty(
+			d,
+			w,
+			XInternAtom(d, "_NET_WM_WINDOW_TYPE", false),
+			XInternAtom(d, "ATOM", false),
+			32,
+			PropModeReplace,
+			(unsigned char *)&type,
+			1
+			);
+
+	/* some window managers honor this properties */
+	type = XInternAtom(d, "_NET_WM_STATE_ABOVE", false);
+	XChangeProperty(d,
+			w,
+			XInternAtom(d, "_NET_WM_STATE", false),
+			XInternAtom(d, "ATOM", false),
+			32,
+			PropModeReplace,
+			(unsigned char *)&type,
+			1
+			);
+
+	type = XInternAtom(d, "_NET_WM_STATE_FOCUSED", false);
+	XChangeProperty(d,
+			w,
+			XInternAtom(d, "_NET_WM_STATE", false),
+			XInternAtom(d, "ATOM", false),
+			32,
+			PropModeAppend,
+			(unsigned char *)&type,
+			1
+			);
+
+	/* Setting window hints */
+	class_hint = XAllocClassHint();
+	if (class_hint == NULL) {
+		fprintf(stderr, "Could not allocate memory for class hint\n");
+		exit(EX_UNAVAILABLE);
+	}
+	class_hint->res_name = resname;
+	class_hint->res_class = resclass;
+	XSetClassHint(d, w, class_hint);
+	XFree(class_hint);
 
-int grabfocus(Display *d, Window w) {
-  for (int i = 0; i < 100; ++i) {
-    Window focuswin;
-    int revert_to_win;
-    XGetInputFocus(d, &focuswin, &revert_to_win);
-    if (focuswin == w)
-      return true;
-    XSetInputFocus(d, w, RevertToParent, CurrentTime);
-    usleep(1000);
-  }
-  return 0;
-}
+	size_hint = XAllocSizeHints();
+	if (size_hint == NULL) {
+		fprintf(stderr, "Could not allocate memory for size hint\n");
+		exit(EX_UNAVAILABLE);
+	}
+	size_hint->flags = PMinSize | PBaseSize;
+	size_hint->min_width = width;
+	size_hint->base_width = width;
+	size_hint->min_height = height;
+	size_hint->base_height = height;
 
-// I know this may seem a little hackish BUT is the only way I managed
-// to actually grab that goddam keyboard. Only one call to
-// XGrabKeyboard does not always end up with the keyboard grabbed!
-int take_keyboard(Display *d, Window w) {
-  int i;
-  for (i = 0; i < 100; i++) {
-    if (XGrabKeyboard(d, w, True, GrabModeAsync, GrabModeAsync, CurrentTime) == GrabSuccess)
-      return 1;
-    usleep(1000);
-  }
-  fprintf(stderr, "Cannot grab keyboard\n");
-  return 0;
+	XFlush(d);
 }
 
-// release the keyboard.
-void release_keyboard(Display *d) {
-  XUngrabKeyboard(d, CurrentTime);
+/* Get the width and height of the window `w' */
+void
+get_wh(Display *d, Window *w, int *width, int *height)
+{
+	XWindowAttributes win_attr;
+
+	XGetWindowAttributes(d, *w, &win_attr);
+	*height = win_attr.height;
+	*width = win_attr.width;
 }
 
-unsigned long parse_color(const char *str, const char *def) {
-  if (str == nil)
-    goto invc;
+int
+grabfocus(Display *d, Window w)
+{
+	int	i;
+	for (i = 0; i < 100; ++i) {
+		Window focuswin;
+		int revert_to_win;
 
-  size_t len = strlen(str);
+		XGetInputFocus(d, &focuswin, &revert_to_win);
 
-  // +1 for the '#' at the start, hence 9 and 4 (instead of 8 and 3)
-  if (*str != '#' || len > 9 || len < 4)
-    goto invc;
-  ++str; // skip the '#'
+		if (focuswin == w)
+			return true;
 
-  char *ep;
-  errno = 0;
-  rgba_t tmp = (rgba_t)(uint32_t)strtoul(str, &ep, 16);
+		XSetInputFocus(d, w, RevertToParent, CurrentTime);
+		usleep(1000);
+	}
+	return 0;
+}
 
-  if (errno)
-    goto invc;
+/* 
+ * I know this may seem a little hackish BUT is the only way I managed
+ * to actually grab that goddam keyboard. Only one call to
+ * XGrabKeyboard does not always end up with the keyboard grabbed!
+ */
+int
+take_keyboard(Display *d, Window w)
+{
+	int i;
 
-  switch (len-1) {
-  case 3:
-    // expand: #rgb -> #rrggbb
-    tmp.v = (tmp.v & 0xf00) * 0x1100
-          | (tmp.v & 0x0f0) * 0x0110
-          | (tmp.v & 0x00f) * 0x0011;
-  case 6:
-    // assume it has 100% opacity
-    tmp.a = 0xff;
-    break;
-  } // colors in aarrggbb format needs no fixes
-
-  // premultiply the alpha
-  if (tmp.a) {
-    tmp.r = (tmp.r * tmp.a) / 255;
-    tmp.g = (tmp.g * tmp.a) / 255;
-    tmp.b = (tmp.b * tmp.a) / 255;
-    return tmp.v;
-  }
-
-  return 0U;
-
-invc:
-  fprintf(stderr, "Invalid color: \"%s\".\n", str);
-  if (def != nil)
-    return parse_color(def, nil);
-  else
-    return 0U;
+	for (i = 0; i < 100; i++) {
+		if (XGrabKeyboard(d, w, True, GrabModeAsync, GrabModeAsync, CurrentTime) == GrabSuccess)
+			return 1;
+		usleep(1000);
+	}
+	fprintf(stderr, "Cannot grab keyboard\n");
+	return 0;
 }
 
-// Given a string, try to parse it as a number or return
-// `default_value'.
-int parse_integer(const char *str, int default_value) {
-  errno = 0;
-  char *ep;
-  long lval = strtol(str, &ep, 10);
-  if (str[0] == '\0' || *ep != '\0') { // NaN
-    fprintf(stderr, "'%s' is not a valid number! Using %d as default.\n", str, default_value);
-    return default_value;
-  }
-  if ((errno == ERANGE && (lval == LONG_MAX || lval == LONG_MIN)) ||
-      (lval > INT_MAX || lval < INT_MIN)) {
-    fprintf(stderr, "%s out of range! Using %d as default.\n", str, default_value);
-    return default_value;
-  }
-  return lval;
-}
+unsigned long
+parse_color(const char *str, const char *def)
+{
+	size_t len;
+	rgba_t tmp;
+	char *ep;
 
-// like parse_integer, but if the value ends with a `%' then its
-// treated like a percentage (`max' is used to compute the percentage)
-int parse_int_with_percentage(const char *str, int default_value, int max) {
-  int len = strlen(str);
-  if (len > 0 && str[len-1] == '%') {
-    char *cpy = strdup(str);
-    check_allocation(cpy);
-    cpy[len-1] = '\0';
-    int val = parse_integer(cpy, default_value);
-    free(cpy);
-    return val * max / 100;
-  }
-  return parse_integer(str, default_value);
-}
+	if (str == NULL)
+		goto invc;
 
-// like parse_int_with_percentage but understands some special values
-// - "middle" that is (max - self) / 2
-// - "start"  that is 0
-// - "end"    that is (max - self)
-int parse_int_with_pos(const char *str, int default_value, int max, int self) {
-  if (!strcmp(str, "start"))
-    return 0;
-  if (!strcmp(str, "middle"))
-    return (max - self)/2;
-  if (!strcmp(str, "end"))
-    return max-self;
-  return parse_int_with_percentage(str, default_value, max);
-}
+	len = strlen(str);
 
-// parse a string like a css value (for example like the css
-// margin/padding properties). Will ALWAYS return an array of 4 word
-// TODO: harden this function!
-char **parse_csslike(const char *str) {
-  char *s = strdup(str);
-  if (s == nil)
-    return nil;
+	/* +1 for the # ath the start */
+	if (*str != '#' || len > 9 || len < 4)
+		goto invc;
+	++str; 			/* skip the # */
 
-  char **ret = malloc(4 * sizeof(char*));
-  if (ret == nil) {
-    free(s);
-    return nil;
-  }
+	errno = 0;
+	tmp = (rgba_t)(uint32_t)strtoul(str, &ep, 16);
 
-  int i = 0;
-  char *token;
-  while ((token = strsep(&s, " ")) != NULL && i < 4) {
-    ret[i] = strdup(token);
-    i++;
-  }
+	if (errno)
+		goto invc;
 
-  if (i == 1)
-    for (int j = 1; j < 4; j++)
-      ret[j] = strdup(ret[0]);
+	switch (len-1) {
+	case 3:
+		/* expand #rgb -> #rrggbb */
+		tmp.v = (tmp.v & 0xf00) * 0x1100
+			| (tmp.v & 0x0f0) * 0x0110
+			| (tmp.v & 0x00f) * 0x0011;
+	case 6:
+		/* assume 0xff opacity */
+		tmp.a = 0xff;
+		break;
+	} /* colors in #aarrggbb need no adjustments */
 
-  if (i == 2) {
-    ret[2] = strdup(ret[0]);
-    ret[3] = strdup(ret[1]);
-  }
+	/* premultiply the alpha */
+	if (tmp.a) {
+		tmp.r = (tmp.r * tmp.a) / 255;
+		tmp.g = (tmp.g * tmp.a) / 255;
+		tmp.b = (tmp.b * tmp.a) / 255;
+		return tmp.v;
+	}
 
-  if (i == 3)
-    ret[3] = strdup(ret[1]);
+	return 0U;
 
-  // Before we didn't check for the return type of strdup, here we will
+ invc:
+	fprintf(stderr, "Invalid color: \"%s\".\n", str);
+	if (def != NULL)
+		return parse_color(def, NULL);
+	else
+		return 0U;
+}
 
-  bool any_null = false;
-  for (int i = 0; i < 4; ++i)
-    any_null = ret[i] == nil || any_null;
+// Given a string, try to parse it as a number or return
+// `default_value'.
+int
+parse_integer(const char *str, int default_value)
+{
+	long	lval;
+	char	*ep;
 
-  if (any_null)
-    for (int i = 0; i < 4; ++i)
-      if (ret[i] != nil)
-        free(ret[i]);
+	errno = 0;
+	lval = strtol(str, &ep, 10);
 
-  if (i == 0 || any_null) {
-    free(s);
-    free(ret);
-    return nil;
-  }
+	if (str[0] == '\0' || *ep != '\0') { // NaN
+		fprintf(stderr, "'%s' is not a valid number! Using %d as default.\n", str, default_value);
+		return default_value;
+	}
 
-  return ret;
+	if ((errno == ERANGE && (lval == LONG_MAX || lval == LONG_MIN)) ||
+	    (lval > INT_MAX || lval < INT_MIN)) {
+		fprintf(stderr, "%s out of range! Using %d as default.\n", str, default_value);
+		return default_value;
+	}
+
+	return lval;
 }
 
-// Given an event, try to understand what the user wants. If the
-// return value is ADD_CHAR then `input' is a pointer to a string that
-// will need to be free'ed.
-enum action parse_event(Display *d, XKeyPressedEvent *ev, XIC xic, char **input) {
-  if (ev->keycode == XKeysymToKeycode(d, XK_BackSpace))
-    return DEL_CHAR;
+/* Like parse_integer but recognize the percentages (i.e. strings ending with `%') */
+int
+parse_int_with_percentage(const char *str, int default_value, int max)
+{
+	int len = strlen(str);
 
-  if (ev->keycode == XKeysymToKeycode(d, XK_Tab))
-    return ev->state & ShiftMask ? PREV_COMPL : NEXT_COMPL;
+	if (len > 0 && str[len-1] == '%') {
+		int val;
+		char *cpy;
 
-  if (ev->keycode == XKeysymToKeycode(d, XK_Return))
-    return CONFIRM;
-
-  if (ev->keycode == XKeysymToKeycode(d, XK_Escape))
-    return EXIT;
-
-  // try to read what the user pressed
-  char str[SYM_BUF_SIZE] = {0};
-  Status s = 0;
-  Xutf8LookupString(xic, ev, str, SYM_BUF_SIZE, 0, &s);
-  if (s == XBufferOverflow) {
-    // should not happen since there are no utf-8 characters larger
-    // than 24bits
-    fprintf(stderr, "Buffer overflow when trying to create keyboard symbol map.\n");
-    return EXIT;
-  }
-
-  if (ev->state & ControlMask) {
-    if (!strcmp(str, "")) // C-u
-      return DEL_LINE;
-    if (!strcmp(str, "")) // C-w
-      return DEL_WORD;
-    if (!strcmp(str, "")) // C-h
-      return DEL_CHAR;
-    if (!strcmp(str, "\r")) // C-m
-      return CONFIRM_CONTINUE;
-    if (!strcmp(str, "")) // C-p
-      return PREV_COMPL;
-    if (!strcmp(str, "")) // C-n
-      return NEXT_COMPL;
-    if (!strcmp(str, "")) // C-c
-      return EXIT;
-    if (!strcmp(str, "\t")) // C-i
-      return TOGGLE_FIRST_SELECTED;
-  }
-
-  *input = strdup(str);
-  if (*input == nil) {
-    fprintf(stderr, "Error while allocating memory for key.\n");
-    return EXIT;
-  }
-
-  return ADD_CHAR;
+		cpy = strdup(str);
+		check_allocation(cpy);
+		cpy[len-1] = '\0';
+		val = parse_integer(cpy, default_value);
+		free(cpy);
+		return val * max / 100;
+	}
+	return parse_integer(str, default_value);
 }
 
-// Given the name of the program (argv[0]?) print a small help on stderr
-void usage(char *prgname) {
-  fprintf(stderr, "%s [-Aamvh] [-B colors] [-b borders] [-C color] [-c color]\n"
-                  "       [-d separator] [-e window] [-f font] [-H height] [-l layout]\n"
-                  "       [-P padding] [-p prompt] [-T color] [-t color] [-S color]\n"
-                  "       [-s color] [-W width] [-x coord] [-y coord]\n", prgname);
+/* 
+ * Like parse_int_with_percentage but understands some special values:
+ * - middle that is (max-self)/2
+ * - start  that is 0
+ * - end    that is (max-self)
+ */
+int
+parse_int_with_pos(const char *str, int default_value, int max, int self)
+{
+	if (!strcmp(str, "start"))
+		return 0;
+	if (!strcmp(str, "middle"))
+		return (max - self)/2;
+	if (!strcmp(str, "end"))
+		return max-self;
+	return parse_int_with_percentage(str, default_value, max);
 }
 
-// small function used in the event loop
-void confirm(enum state *status, struct rendering *r, struct completions *cs, char **text, int *textlen) {
-  if ((cs->selected != -1) || (cs->lenght > 0 && r->first_selected)) {
-    // if there is something selected expand it and return
-    int index = cs->selected == -1 ? 0 : cs->selected;
-    struct completion *c = cs->completions;
-    while (true) {
-      if (index == 0)
-        break;
-      c++;
-      index--;
-    }
-    char *t = c->rcompletion;
-    free(*text);
-    *text = strdup(t);
-    if (*text == nil) {
-      fprintf(stderr, "Memory allocation error\n");
-      *status = ERR;
-    }
-    *textlen = strlen(*text);
-  } else {
-    if (!r->free_text) {
-      // cannot accept arbitrary text
-      *status = LOOPING;
-    }
-  }
-}
+/* Parse a string like a CSS value. */
+/* TODO: harden a bit this function */
+char **
+parse_csslike(const char *str)
+{
+	int	i;
+	char	*s, *token, **ret;
+	bool	any_null;
 
-// event loop
-enum state loop(struct rendering *r, char **text, int *textlen, struct completions *cs, char **lines, char **vlines) {
-  enum state status = LOOPING;
-  while (status == LOOPING) {
-    XEvent e;
-    XNextEvent(r->d, &e);
+	s = strdup(str);
+	if (s == NULL)
+		return NULL;
 
-    if (XFilterEvent(&e, r->w))
-      continue;
+	ret = malloc(4 * sizeof(char*));
+	if (ret == NULL) {
+		free(s);
+		return NULL;
+	}
 
-    switch (e.type) {
-      case KeymapNotify:
-        XRefreshKeyboardMapping(&e.xmapping);
-        break;
+	i = 0;
+	while ((token = strsep(&s, " ")) != NULL && i < 4) {
+		ret[i] = strdup(token);
+		i++;
+	}
 
-      case FocusIn:
-        // re-grab focus
-        if (e.xfocus.window != r->w)
-          grabfocus(r->d, r->w);
-        break;
+	if (i == 1)
+		for (int j = 1; j < 4; j++)
+			ret[j] = strdup(ret[0]);
 
-      case VisibilityNotify:
-        if (e.xvisibility.state != VisibilityUnobscured)
-          XRaiseWindow(r->d, r->w);
-        break;
+	if (i == 2) {
+		ret[2] = strdup(ret[0]);
+		ret[3] = strdup(ret[1]);
+	}
 
-      case MapNotify:
-        get_wh(r->d, &r->w, &r->width, &r->height);
-        draw(r, *text, cs);
-        break;
+	if (i == 3)
+		ret[3] = strdup(ret[1]);
 
-      case KeyPress: {
-        XKeyPressedEvent *ev = (XKeyPressedEvent*)&e;
+	/* before we didn't check for the return type of strdup, here we will */
 
-        char *input;
-        switch (parse_event(r->d, ev, r->xic, &input)) {
-          case EXIT:
-            status = ERR;
-            break;
+	any_null = false;
+	for (i = 0; i < 4; ++i)
+		any_null = ret[i] == NULL || any_null;
 
-          case CONFIRM: {
-            status = OK;
-            confirm(&status, r, cs, text, textlen);
-            break;
-          }
+	if (any_null)
+		for (i = 0; i < 4; ++i)
+			if (ret[i] != NULL)
+				free(ret[i]);
 
-          case CONFIRM_CONTINUE: {
-            status = OK_LOOP;
-            confirm(&status, r, cs, text, textlen);
-            break;
-          }
+	if (i == 0 || any_null) {
+		free(s);
+		free(ret);
+		return NULL;
+	}
 
-          case PREV_COMPL: {
-            complete(cs, r->first_selected, true, text, textlen, &status);
-            r->offset = cs->selected;
-            break;
-          }
+	return ret;
+}
 
-          case NEXT_COMPL: {
-            complete(cs, r->first_selected, false, text, textlen, &status);
-            r->offset = cs->selected;
-            break;
-          }
+/* 
+ * Given an event, try to understand what the users wants. If the
+ * return value is ADD_CHAR then `input' is a pointer to a string that
+ * will need to be free'ed later.
+ */
+enum
+action parse_event(Display *d, XKeyPressedEvent *ev, XIC xic, char **input)
+{
+	char	str[SYM_BUF_SIZE] = {0};
+	Status	s;
 
-          case DEL_CHAR:
-            popc(*text);
-            update_completions(cs, *text, lines, vlines, r->first_selected);
-            r->offset = 0;
-            break;
+	if (ev->keycode == XKeysymToKeycode(d, XK_BackSpace))
+		return DEL_CHAR;
 
-          case DEL_WORD: {
-            popw(*text);
-            update_completions(cs, *text, lines, vlines, r->first_selected);
-            break;
-          }
+	if (ev->keycode == XKeysymToKeycode(d, XK_Tab))
+		return ev->state & ShiftMask ? PREV_COMPL : NEXT_COMPL;
 
-          case DEL_LINE: {
-            for (int i = 0; i < *textlen; ++i)
-              *(*text + i) = 0;
-            update_completions(cs, *text, lines, vlines, r->first_selected);
-            r->offset = 0;
-            break;
-          }
+	if (ev->keycode == XKeysymToKeycode(d, XK_Return))
+		return CONFIRM;
 
-          case ADD_CHAR: {
-            int str_len = strlen(input);
+	if (ev->keycode == XKeysymToKeycode(d, XK_Escape))
+		return EXIT;
 
-            // sometimes a strange key is pressed (i.e. ctrl alone),
-            // so input will be empty. Don't need to update completion
-            // in this case
-            if (str_len == 0)
-              break;
+	/* Try to read what key was pressed */
+	s = 0;
+	Xutf8LookupString(xic, ev, str, SYM_BUF_SIZE, 0, &s);
+	if (s == XBufferOverflow) {
+		fprintf(stderr, "Buffer overflow when trying to create keyboard symbol map.\n");
+		return EXIT;
+	}
 
-            for (int i = 0; i < str_len; ++i) {
-              *textlen = pushc(text, *textlen, input[i]);
-              if (*textlen == -1) {
-                fprintf(stderr, "Memory allocation error\n");
-                status = ERR;
-                break;
-              }
-            }
-            if (status != ERR) {
-              update_completions(cs, *text, lines, vlines, r->first_selected);
-              free(input);
-            }
-            r->offset = 0;
-            break;
-          }
+	if (ev->state & ControlMask) {
+		if (!strcmp(str, "")) /* C-u */
+			return DEL_LINE;
+		if (!strcmp(str, "")) /* C-w */
+			return DEL_WORD;
+		if (!strcmp(str, "")) /* C-h */
+			return DEL_CHAR;
+		if (!strcmp(str, "\r")) /* C-m */
+			return CONFIRM_CONTINUE;
+		if (!strcmp(str, "")) /* C-p */
+			return PREV_COMPL;
+		if (!strcmp(str, "")) /* C-n */
+			return NEXT_COMPL;
+		if (!strcmp(str, "")) /* C-c */
+			return EXIT;
+		if (!strcmp(str, "\t")) /* C-i */
+			return TOGGLE_FIRST_SELECTED;
+	}
 
-          case TOGGLE_FIRST_SELECTED:
-            r->first_selected = !r->first_selected;
-            if (r->first_selected && cs->selected < 0)
-              cs->selected = 0;
-            if (!r->first_selected && cs->selected == 0)
-              cs->selected = -1;
-            break;
-        }
-      }
+	*input = strdup(str);
+	if (*input == NULL) {
+		fprintf(stderr, "Error while allocating memory for key.\n");
+		return EXIT;
+	}
 
-      case ButtonPress: {
-        XButtonPressedEvent *ev = (XButtonPressedEvent*)&e;
-        /* if (ev->button == Button1) { /\* click *\/ */
-        /*   int x = ev->x - r.border_w; */
-        /*   int y = ev->y - r.border_n; */
-        /*   fprintf(stderr, "Click @ (%d, %d)\n", x, y); */
-        /* } */
+	return ADD_CHAR;
+}
 
-        if (ev->button == Button4) /* scroll up */
-          r->offset = MAX((ssize_t)r->offset - 1, 0);
+void
+confirm(enum state *status, struct rendering *r, struct completions *cs, char **text, int *textlen)
+{
+	if ((cs->selected != -1) || (cs->length > 0 && r->first_selected)) {
+		/* if there is something selected expand it and return */
+		int index = cs->selected == -1 ? 0 : cs->selected;
+		struct completion *c = cs->completions;
+		char *t;
 
-        if (ev->button == Button5) /* scroll down */
-          r->offset = MIN(r->offset + 1, cs->lenght - 1);
+		while (true) {
+			if (index == 0)
+				break;
+			c++;
+			index--;
+		}
 
-        break;
-      }
-    }
+		t = c->rcompletion;
+		free(*text);
+		*text = strdup(t);
 
-    draw(r, *text, cs);
-  }
+		if (*text == NULL) {
+			fprintf(stderr, "Memory allocation error\n");
+			*status = ERR;
+		}
 
-  return status;
+		*textlen = strlen(*text);
+		return;
+	}
+
+	if (!r->free_text) /* cannot accept arbitrary text */
+		*status = LOOPING;
 }
 
-int main(int argc, char **argv) {
-#ifdef __OpenBSD__
-  // stdio & rpat: to read and write stdio/stdout
-  // unix:         to connect to Xorg
-  pledge("stdio rpath unix", "");
-#endif
+/* event loop */
+enum state
+loop(struct rendering *r, char **text, int *textlen, struct completions *cs, char **lines, char **vlines)
+{
+	enum state status = LOOPING;
 
-  char *sep = nil;
+	while (status == LOOPING) {
+		XEvent e;
+		XNextEvent(r->d, &e);
 
-  // by default the first completion isn't selected
-  bool first_selected = false;
+		if (XFilterEvent(&e, r->w))
+			continue;
 
-  // our parent window
-  char *parent_window_id = nil;
+		switch (e.type) {
+		case KeymapNotify:
+			XRefreshKeyboardMapping(&e.xmapping);
+			break;
 
-  // the user can input arbitrary text
-  bool free_text = true;
+		case FocusIn:
+			/* Re-grab focus */
+			if (e.xfocus.window != r->w)
+				grabfocus(r->d, r->w);
+			break;
 
-  // the user can select multiple entries
-  bool multiple_select = false;
+		case VisibilityNotify:
+			if (e.xvisibility.state != VisibilityUnobscured)
+				XRaiseWindow(r->d, r->w);
+			break;
 
-  // first round of args parsing
-  int ch;
-  while ((ch = getopt(argc, argv, ARGS)) != -1) {
-    switch (ch) {
-      case 'h': // help
-        usage(*argv);
-        return 0;
-      case 'v': // version
-        fprintf(stderr, "%s version: %s\n", *argv, VERSION);
-        return 0;
-      case 'e': // embed
-        parent_window_id = strdup(optarg);
-        check_allocation(parent_window_id);
-        break;
-      case 'd': {
-        sep = strdup(optarg);
-        check_allocation(sep);
-      }
-      case 'A': {
-        free_text = false;
-        break;
-      }
-      case 'm': {
-        multiple_select = true;
-        break;
-      }
-      default:
-        break;
-    }
-  }
+		case MapNotify:
+			get_wh(r->d, &r->w, &r->width, &r->height);
+			draw(r, *text, cs);
+			break;
 
-  // read the lines from stdin
-  char **lines = nil;
-  char *buf = nil;
-  size_t nlines = readlines(&lines, &buf);
+		case KeyPress: {
+			XKeyPressedEvent *ev = (XKeyPressedEvent*)&e;
+			char *input;
 
-  char **vlines = nil;
-  if (sep != nil) {
-    int l = strlen(sep);
-    vlines = calloc(nlines, sizeof(char*));
-    check_allocation(vlines);
+			switch (parse_event(r->d, ev, r->xic, &input)) {
+			case EXIT:
+				status = ERR;
+				break;
 
-    for (size_t i = 0; i < nlines; i++) {
-      char *t = strstr(lines[i], sep);
-      if (t == nil)
-        vlines[i] = lines[i];
-      else
-        vlines[i] = t + l;
-    }
-  }
+			case CONFIRM: {
+				status = OK;
+				confirm(&status, r, cs, text, textlen);
+				break;
+			}
 
-  setlocale(LC_ALL, getenv("LANG"));
+			case CONFIRM_CONTINUE: {
+				status = OK_LOOP;
+				confirm(&status, r, cs, text, textlen);
+				break;
+			}
 
-  enum state status = LOOPING;
+			case PREV_COMPL: {
+				complete(cs, r->first_selected, true, text, textlen, &status);
+				r->offset = cs->selected;
+				break;
+			}
 
-  // where the monitor start (used only with xinerama)
-  int offset_x = 0;
-  int offset_y = 0;
+			case NEXT_COMPL: {
+				complete(cs, r->first_selected, false, text, textlen, &status);
+				r->offset = cs->selected;
+				break;
+			}
 
-  // width and height of the window
-  int width = 400;
-  int height = 20;
+			case DEL_CHAR:
+				popc(*text);
+				update_completions(cs, *text, lines, vlines, r->first_selected);
+				r->offset = 0;
+				break;
 
-  // position on the screen
-  int x = 0;
-  int y = 0;
+			case DEL_WORD: {
+				popw(*text);
+				update_completions(cs, *text, lines, vlines, r->first_selected);
+				break;
+			}
 
-  // the default padding
-  int padding = 10;
+			case DEL_LINE: {
+				int i;
+				for (i = 0; i < *textlen; ++i)
+					*(*text + i) = 0;
+				update_completions(cs, *text, lines, vlines, r->first_selected);
+				r->offset = 0;
+				break;
+			}
 
-  // the default borders
-  int border_n = 0;
-  int border_e = 0;
-  int border_s = 0;
-  int border_w = 0;
+			case ADD_CHAR: {
+				int str_len, i;
 
-  // the prompt. We duplicate the string so later is easy to free (in
-  // the case the user provide its own prompt)
-  char *ps1 = strdup("$ ");
-  check_allocation(ps1);
+				str_len = strlen(input);
 
-  // same for the font name
-  char *fontname = strdup(default_fontname);
-  check_allocation(fontname);
+				/*
+				 * sometimes a strange key is pressed
+				 * i.e. ctrl alone), so input will be
+				 * empty. Don't need to update
+				 * completion in that case
+				 */
+				if (str_len == 0)
+					break;
 
-  int textlen = 10;
-  char *text = malloc(textlen * sizeof(char));
-  check_allocation(text);
+				for (i = 0; i < str_len; ++i) {
+					*textlen = pushc(text, *textlen, input[i]);
+					if (*textlen == -1) {
+						fprintf(stderr, "Memory allocation error\n");
+						status = ERR;
+						break;
+					}
+				}
 
-  /* struct completions *cs = filter(text, lines); */
-  struct completions *cs = compls_new(nlines);
-  check_allocation(cs);
+				if (status != ERR) {
+					update_completions(cs, *text, lines, vlines, r->first_selected);
+					free(input);
+				}
 
-  // start talking to xorg
-  Display *d = XOpenDisplay(nil);
-  if (d == nil) {
-    fprintf(stderr, "Could not open display!\n");
-    return EX_UNAVAILABLE;
-  }
+				r->offset = 0;
+				break;
+			}
 
-  Window parent_window;
-  bool embed = true;
-  if (! (parent_window_id && (parent_window = strtol(parent_window_id, nil, 0)))) {
-    parent_window = DefaultRootWindow(d);
-    embed = false;
-  }
+			case TOGGLE_FIRST_SELECTED:
+				r->first_selected = !r->first_selected;
+				if (r->first_selected && cs->selected < 0)
+					cs->selected = 0;
+				if (!r->first_selected && cs->selected == 0)
+					cs->selected = -1;
+				break;
+			}
+		}
 
-  // get display size
-  int d_width;
-  int d_height;
-  get_wh(d, &parent_window, &d_width, &d_height);
+		case ButtonPress: {
+			XButtonPressedEvent *ev = (XButtonPressedEvent*)&e;
+			/* if (ev->button == Button1) { /\* click *\/ */
+			/*   int x = ev->x - r.border_w; */
+			/*   int y = ev->y - r.border_n; */
+			/*   fprintf(stderr, "Click @ (%d, %d)\n", x, y); */
+			/* } */
 
-#ifdef USE_XINERAMA
-  if (!embed && XineramaIsActive(d)) {
-    // find the mice
-    int number_of_screens = XScreenCount(d);
-    Window r;
-    Window root;
-    int root_x, root_y, win_x, win_y;
-    unsigned int mask;
-    bool res;
-    for (int i = 0; i < number_of_screens; ++i) {
-      root = XRootWindow(d, i);
-      res = XQueryPointer(d, root, &r, &r, &root_x, &root_y, &win_x, &win_y, &mask);
-      if (res) break;
-    }
-    if (!res) {
-      fprintf(stderr, "No mouse found.\n");
-      root_x = 0;
-      root_y = 0;
-    }
+			if (ev->button == Button4) /* scroll up */
+				r->offset = MAX((ssize_t)r->offset - 1, 0);
 
-    // now find in which monitor the mice is on
-    int monitors;
-    XineramaScreenInfo *info = XineramaQueryScreens(d, &monitors);
-    if (info) {
-      for (int i = 0; i < monitors; ++i) {
-        if (info[i].x_org <= root_x && root_x <= (info[i].x_org + info[i].width)
-            && info[i].y_org <= root_y && root_y <= (info[i].y_org + info[i].height)) {
-          offset_x = info[i].x_org;
-          offset_y = info[i].y_org;
-          d_width = info[i].width;
-          d_height = info[i].height;
-          break;
-        }
-      }
-    }
-    XFree(info);
-  }
-#endif
+			if (ev->button == Button5) /* scroll down */
+				r->offset = MIN(r->offset + 1, cs->length - 1);
 
-  /* Colormap cmap = DefaultColormap(d, DefaultScreen(d)); */
-  XVisualInfo vinfo;
-  XMatchVisualInfo(d, DefaultScreen(d), 32, TrueColor, &vinfo);
+			break;
+		}
+		}
 
-  Colormap cmap;
-  cmap = XCreateColormap(d, XDefaultRootWindow(d), vinfo.visual, AllocNone);
+		draw(r, *text, cs);
+	}
 
-  unsigned long p_fg = parse_color("#fff", nil);
-  unsigned long compl_fg = parse_color("#fff", nil);
-  unsigned long compl_highlighted_fg = parse_color("#000", nil);
+	return status;
+}
 
-  unsigned long p_bg = parse_color("#000", nil);
-  unsigned long compl_bg = parse_color("#000", nil);
-  unsigned long compl_highlighted_bg = parse_color("#fff", nil);
+int
+load_font(struct rendering *r, const char *fontname)
+{
+#ifdef USE_XFT
+	r->font = XftFontOpenName(r->d, DefaultScreen(r->d), fontname);
+	return 0;
+#else
+	char **missing_charset_list;
+	int missing_charset_count;
 
-  unsigned long border_n_bg, border_e_bg, border_s_bg, border_w_bg;
-  border_n_bg = border_e_bg = border_s_bg = border_w_bg = parse_color("#000", nil);
+	r->font = XCreateFontSet(r->d, fontname, &missing_charset_list, &missing_charset_count, NULL);
+	if (r->font != NULL)
+		return 0;
 
-  bool horizontal_layout = true;
+	fprintf(stderr, "Unable to load the font(s) %s\n", fontname);
 
-  // read resource
-  XrmInitialize();
-  char *xrm = XResourceManagerString(d);
-  XrmDatabase xdb = nil;
-  if (xrm != nil) {
-    xdb = XrmGetStringDatabase(xrm);
-    XrmValue value;
-    char *datatype[20];
+	if (!strcmp(fontname, default_fontname))
+		return -1;
 
-    if (XrmGetResource(xdb, "MyMenu.font", "*", datatype, &value) == true) {
-      free(fontname);
-      fontname = strdup(value.addr);
-      check_allocation(fontname);
-    } else {
-      fprintf(stderr, "no font defined, using %s\n", fontname);
-    }
+	return load_font(r, default_fontname);
+#endif
+}
 
-    if (XrmGetResource(xdb, "MyMenu.layout", "*", datatype, &value) == true)
-      horizontal_layout = !strcmp(value.addr, "horizontal");
-    else
-      fprintf(stderr, "no layout defined, using horizontal\n");
+void
+usage(char *prgname)
+{
+	fprintf(stderr, "%s [-Aamvh] [-B colors] [-b borders] [-C color] [-c color]\n"
+		        "       [-d separator] [-e window] [-f font] [-H height] [-l layout]\n"
+		        "       [-P padding] [-p prompt] [-T color] [-t color] [-S color]\n"
+		        "       [-s color] [-W width] [-x coord] [-y coord]\n", prgname);
+}
 
-    if (XrmGetResource(xdb, "MyMenu.prompt", "*", datatype, &value) == true) {
-      free(ps1);
-      ps1 = normalize_str(value.addr);
-    } else {
-      fprintf(stderr, "no prompt defined, using \"%s\" as default\n", ps1);
-    }
-
-    if (XrmGetResource(xdb, "MyMenu.width", "*", datatype, &value) == true)
-      width = parse_int_with_percentage(value.addr, width, d_width);
-    else
-      fprintf(stderr, "no width defined, using %d\n", width);
+int
+main(int argc, char **argv)
+{
+	struct completions *cs;
+	XSetWindowAttributes attr;
 
-    if (XrmGetResource(xdb, "MyMenu.height", "*", datatype, &value) == true)
-      height = parse_int_with_percentage(value.addr, height, d_height);
-    else
-      fprintf(stderr, "no height defined, using %d\n", height);
+	size_t		nlines, i;
+	Display		*d;
+	Window		parent_window, w;
+	XVisualInfo	vinfo;
+	Colormap	cmap;
+	XGCValues	values;
+	XrmDatabase	xdb;
+	XIM		xim;
+	XIMStyle	best_match_style;
+	XIMStyles	*xis;
+	unsigned long	p_fg, compl_fg, compl_highlighted_fg;
+	unsigned long	p_bg, compl_bg, compl_highlighted_bg;
+	unsigned long	border_n_bg, border_e_bg, border_s_bg, border_w_bg;
+	int		ch;
+	int		offset_x, offset_y, width, height, x, y;
+	int		padding, textlen;
+	int		border_n, border_e, border_s, border_w;
+	int		d_width, d_height;
+	enum state	status;
+	bool		first_selected, free_text, multiple_select;
+	bool		embed, horizontal_layout;
+	char		*sep, *parent_window_id;
+	char		**lines, *buf, **vlines;
+	char		*ps1, *fontname, *text, *xrm;
 
-    if (XrmGetResource(xdb, "MyMenu.x", "*", datatype, &value) == true)
-      x = parse_int_with_pos(value.addr, x, d_width, width);
-    else
-      fprintf(stderr, "no x defined, using %d\n", x);
+#ifdef __OpenBSD__
+	/* stdio & rpath: to read/write stdio/stdout/stderr */
+	/* unix:          to connect to XOrg */
+	pledge("stdio rpath unix", "");
+#endif
 
-    if (XrmGetResource(xdb, "MyMenu.y", "*", datatype, &value) == true)
-      y = parse_int_with_pos(value.addr, y, d_height, height);
-    else
-      fprintf(stderr, "no y defined, using %d\n", y);
+	sep = NULL;
+	first_selected = false;
+	parent_window_id = NULL;
+	free_text = true;
+	multiple_select = false;
 
-    if (XrmGetResource(xdb, "MyMenu.padding", "*", datatype, &value) == true)
-      padding = parse_integer(value.addr, padding);
-    else
-      fprintf(stderr, "no padding defined, using %d\n", padding);
+	while ((ch = getopt(argc, argv, ARGS)) != -1) {
+		switch (ch) {
+		case 'h':	/* help */
+			usage(*argv);
+			return 0;
+		case 'v':	/* version */
+			fprintf(stderr, "%s version: %s\n", *argv, VERSION);
+			return 0;
+		case 'e':	/* embed */
+			parent_window_id = strdup(optarg);
+			check_allocation(parent_window_id);
+			break;
+		case 'd': {
+			sep = strdup(optarg);
+			check_allocation(sep);
+		}
+		case 'A': {
+			free_text = false;
+			break;
+		}
+		case 'm': {
+			multiple_select = true;
+			break;
+		}
+		default:
+			break;
+		}
+	}
 
-    if (XrmGetResource(xdb, "MyMenu.border.size", "*", datatype, &value) == true) {
-      char **borders = parse_csslike(value.addr);
-      if (borders != nil) {
-        border_n = parse_integer(borders[0], 0);
-        border_e = parse_integer(borders[1], 0);
-        border_s = parse_integer(borders[2], 0);
-        border_w = parse_integer(borders[3], 0);
-      } else {
-        fprintf(stderr, "error while parsing MyMenu.border.size\n");
-      }
-    } else {
-      fprintf(stderr, "no border defined, using 0.\n");
-    }
+	/* Read the completions */
+	lines = NULL;
+	buf = NULL;
+	nlines = readlines(&lines, &buf);
 
-    /* XColor tmp; */
-    // TODO: tmp needs to be free'd after every allocation?
+	vlines = NULL;
+	if (sep != NULL) {
+		int l;
+		l = strlen(sep);
+		vlines = calloc(nlines, sizeof(char*));
+		check_allocation(vlines);
 
-    // prompt
-    if (XrmGetResource(xdb, "MyMenu.prompt.foreground", "*", datatype, &value) == true)
-      p_fg = parse_color(value.addr, "#fff");
+		for (i = 0; i < nlines; i++) {
+			char *t;
+			t = strstr(lines[i], sep);
+			if (t == NULL)
+				vlines[i] = lines[i];
+			else
+				vlines[i] = t + l;
+		}
+	}
 
-    if (XrmGetResource(xdb, "MyMenu.prompt.background", "*", datatype, &value) == true)
-      p_bg = parse_color(value.addr, "#000");
+	setlocale(LC_ALL, getenv("LANG"));
 
-    // completion
-    if (XrmGetResource(xdb, "MyMenu.completion.foreground", "*", datatype, &value) == true)
-      compl_fg = parse_color(value.addr, "#fff");
+	status = LOOPING;
 
-    if (XrmGetResource(xdb, "MyMenu.completion.background", "*", datatype, &value) == true)
-      compl_bg = parse_color(value.addr, "#000");
-    else
-      compl_bg = parse_color("#000", nil);
+	/* where the monitor start (used only with xinerama) */
+	offset_x = offset_y = 0;
 
-    // completion highlighted
-    if (XrmGetResource(xdb, "MyMenu.completion_highlighted.foreground", "*", datatype, &value) == true)
-      compl_highlighted_fg = parse_color(value.addr, "#000");
+	/* default width and height */
+	width = 400;
+	height = 20;
 
-    if (XrmGetResource(xdb, "MyMenu.completion_highlighted.background", "*", datatype, &value) == true)
-      compl_highlighted_bg = parse_color(value.addr, "#fff");
-    else
-      compl_highlighted_bg = parse_color("#fff", nil);
+	/* default position on the screen */
+	x = y = 0;
 
-    // border
-    if (XrmGetResource(xdb, "MyMenu.border.color", "*", datatype, &value) == true) {
-      char **colors = parse_csslike(value.addr);
-      if (colors != nil) {
-        border_n_bg = parse_color(colors[0], "#000");
-        border_e_bg = parse_color(colors[1], "#000");
-        border_s_bg = parse_color(colors[2], "#000");
-        border_w_bg = parse_color(colors[3], "#000");
-      } else {
-        fprintf(stderr, "error while parsing MyMenu.border.color\n");
-      }
-    }
-  }
+	/* default padding */
+	padding = 10;
 
-  // second round of args parsing
-  optind = 0; // reset the option index
-  while ((ch = getopt(argc, argv, ARGS)) != -1) {
-    switch (ch) {
-      case 'a':
-        first_selected = true;
-        break;
-      case 'A':
-        // free_text -- this case was already catched
-        break;
-      case 'd':
-        // separator -- this case was already catched
-        break;
-      case 'e':
-        // (embedding mymenu) this case was already catched.
-      case 'm':
-        // (multiple selection) this case was already catched.
-        break;
-      case 'p': {
-        char *newprompt = strdup(optarg);
-        if (newprompt != nil) {
-          free(ps1);
-          ps1 = newprompt;
-        }
-        break;
-      }
-      case 'x':
-        x = parse_int_with_pos(optarg, x, d_width, width);
-        break;
-      case 'y':
-        y = parse_int_with_pos(optarg, y, d_height, height);
-        break;
-      case 'P':
-        padding = parse_integer(optarg, padding);
-        break;
-      case 'l':
-        horizontal_layout = !strcmp(optarg, "horizontal");
-        break;
-      case 'f': {
-        char *newfont = strdup(optarg);
-        if (newfont != nil) {
-          free(fontname);
-          fontname = newfont;
-        }
-        break;
-      }
-      case 'W':
-        width = parse_int_with_percentage(optarg, width, d_width);
-        break;
-      case 'H':
-        height = parse_int_with_percentage(optarg, height, d_height);
-        break;
-      case 'b': {
-        char **borders = parse_csslike(optarg);
-        if (borders != nil) {
-          border_n = parse_integer(borders[0], 0);
-          border_e = parse_integer(borders[1], 0);
-          border_s = parse_integer(borders[2], 0);
-          border_w = parse_integer(borders[3], 0);
-        } else {
-          fprintf(stderr, "Error parsing b option\n");
-        }
-        break;
-      }
-      case 'B': {
-        char **colors = parse_csslike(optarg);
-        if (colors != nil) {
-          border_n_bg = parse_color(colors[0], "#000");
-          border_e_bg = parse_color(colors[1], "#000");
-          border_s_bg = parse_color(colors[2], "#000");
-          border_w_bg = parse_color(colors[3], "#000");
-        } else {
-          fprintf(stderr, "error while parsing B option\n");
-        }
-        break;
-      }
-      case 't': {
-        p_fg = parse_color(optarg, nil);
-        break;
-      }
-      case 'T': {
-        p_bg = parse_color(optarg, nil);
-        break;
-      }
-      case 'c': {
-        compl_fg = parse_color(optarg, nil);
-        break;
-      }
-      case 'C': {
-        compl_bg = parse_color(optarg, nil);
-        break;
-      }
-      case 's': {
-        compl_highlighted_fg = parse_color(optarg, nil);
-        break;
-      }
-      case 'S': {
-        compl_highlighted_bg = parse_color(optarg, nil);
-        break;
-      }
-      default:
-        fprintf(stderr, "Unrecognized option %c\n", ch);
-        status = ERR;
-        break;
-    }
-  }
+	/* default borders */
+	border_n = border_e = border_s = border_w = 0;
 
-  // since only now we know if the first should be selected, update
-  // the completion here
-  update_completions(cs, text, lines, vlines, first_selected);
+	/* the prompt. We duplicate the string so later is easy to
+	 * free (in the case it's been overwritten by the user) */
+	ps1 = strdup("$ ");
+	check_allocation(ps1);
 
-  // load the font
-#ifdef USE_XFT
-  XftFont *font = XftFontOpenName(d, DefaultScreen(d), fontname);
-#else
-  char **missing_charset_list;
-  int missing_charset_count;
-  XFontSet font = XCreateFontSet(d, fontname, &missing_charset_list, &missing_charset_count, nil);
-  if (font == nil) {
-    fprintf(stderr, "Unable to load the font(s) %s\n", fontname);
-    return EX_UNAVAILABLE;
-  }
-#endif
+	/* same for the font name */
+	fontname = strdup(default_fontname);
+	check_allocation(fontname);
 
-  // create the window
-  XSetWindowAttributes attr;
-  attr.colormap = cmap;
-  attr.override_redirect = true;
-  attr.border_pixel = 0;
-  attr.background_pixel = 0x80808080;
-  attr.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask;
+	textlen = 10;
+	text = malloc(textlen * sizeof(char));
+	check_allocation(text);
 
-  Window w = XCreateWindow(d,                                   // display
-                           parent_window,                       // parent
-                           x + offset_x, y + offset_y,          // x y
-                           width, height,                       // w h
-                           0,                                   // border width
-                           vinfo.depth,                         // depth
-                           InputOutput,                         // class
-                           vinfo.visual,                        // visual
-                           CWBorderPixel | CWBackPixel | CWColormap | CWEventMask | CWOverrideRedirect, // value mask
-                           &attr);
+	/* struct completions *cs = filter(text, lines); */
+	cs = compls_new(nlines);
+	check_allocation(cs);
 
-  set_win_atoms_hints(d, w, width, height);
+	/* start talking to xorg */
+	d = XOpenDisplay(NULL);
+	if (d == NULL) {
+		fprintf(stderr, "Could not open display!\n");
+		return EX_UNAVAILABLE;
+	}
 
-  // we want some events
-  XSelectInput(d, w, StructureNotifyMask | KeyPressMask | KeymapStateMask | ButtonPressMask);
-  XMapRaised(d, w);
+	embed = true;
+	if (! (parent_window_id && (parent_window = strtol(parent_window_id, NULL, 0)))) {
+		parent_window = DefaultRootWindow(d);
+		embed = false;
+	}
 
-  // if embed, listen for other events as well
-  if (embed) {
-    XSelectInput(d, parent_window, FocusChangeMask);
-    Window *children, parent, root;
-    unsigned int children_no;
-    if (XQueryTree(d, parent_window, &root, &parent, &children, &children_no) && children) {
-      for (unsigned int i = 0; i < children_no && children[i] != w; ++i)
-        XSelectInput(d, children[i], FocusChangeMask);
-      XFree(children);
-    }
-    grabfocus(d, w);
-  }
+	/* get display size */
+	get_wh(d, &parent_window, &d_width, &d_height);
 
-  // grab keyboard
-  take_keyboard(d, w);
+#ifdef USE_XINERAMA
+	if (!embed && XineramaIsActive(d)) { /* find the mice */
+		XineramaScreenInfo *info;
+		Window		r;
+		Window		root;
+		int		number_of_screens, monitors, i;
+		int		root_x, root_y, win_x, win_y;
+		unsigned int	mask;
+		bool		res;
 
-  // Create some graphics contexts
-  XGCValues values;
-  /* values.font = font->fid; */
+		number_of_screens = XScreenCount(d);
+		for (i = 0; i < number_of_screens; ++i) {
+			root = XRootWindow(d, i);
+			res = XQueryPointer(d, root, &r, &r, &root_x, &root_y, &win_x, &win_y, &mask);
+			if (res) break;
+		}
+		if (!res) {
+			fprintf(stderr, "No mouse found.\n");
+			root_x = 0;
+			root_y = 0;
+		}
 
-  struct rendering r = {
-    .d                          = d,
-    .w                          = w,
-    .width                      = width,
-    .height                     = height,
-    .padding                    = padding,
-    .x_zero                     = border_w,
-    .y_zero                     = border_n,
-    .offset                     = 0,
-    .free_text                  = free_text,
-    .first_selected             = first_selected,
-    .multiple_select            = multiple_select,
-    .border_n                   = border_n,
-    .border_e                   = border_e,
-    .border_s                   = border_s,
-    .border_w                   = border_w,
-    .horizontal_layout          = horizontal_layout,
-    .ps1                        = ps1,
-    .ps1len                     = strlen(ps1),
-    .prompt                     = XCreateGC(d, w, 0, &values),
-    .prompt_bg                  = XCreateGC(d, w, 0, &values),
-    .completion                 = XCreateGC(d, w, 0, &values),
-    .completion_bg              = XCreateGC(d, w, 0, &values),
-    .completion_highlighted     = XCreateGC(d, w, 0, &values),
-    .completion_highlighted_bg  = XCreateGC(d, w, 0, &values),
-    .border_n_bg                = XCreateGC(d, w, 0, &values),
-    .border_e_bg                = XCreateGC(d, w, 0, &values),
-    .border_s_bg                = XCreateGC(d, w, 0, &values),
-    .border_w_bg                = XCreateGC(d, w, 0, &values),
-#ifdef USE_XFT
-    .font                       = font,
-#else
-    .font                       = &font,
+		// now find in which monitor the mice is on
+		info = XineramaQueryScreens(d, &monitors);
+		if (info) {
+			for (i = 0; i < monitors; ++i) {
+				if (info[i].x_org <= root_x && root_x <= (info[i].x_org + info[i].width)
+				    && info[i].y_org <= root_y && root_y <= (info[i].y_org + info[i].height)) {
+					offset_x = info[i].x_org;
+					offset_y = info[i].y_org;
+					d_width = info[i].width;
+					d_height = info[i].height;
+					break;
+				}
+			}
+		}
+		XFree(info);
+	}
 #endif
-  };
+	XMatchVisualInfo(d, DefaultScreen(d), 32, TrueColor, &vinfo);
 
-#ifdef USE_XFT
-  r.xftdraw = XftDrawCreate(d, w, vinfo.visual, DefaultColormap(d, 0));
+	cmap = XCreateColormap(d, XDefaultRootWindow(d), vinfo.visual, AllocNone);
 
-  {
-    rgba_t c;
+	p_fg = compl_fg = parse_color("#fff", NULL);
+	compl_highlighted_fg = parse_color("#000", NULL);
 
-    XRenderColor xrcolor;
+	p_bg = compl_bg = parse_color("#000", NULL);
+	compl_highlighted_bg = parse_color("#fff", NULL);
 
-    // prompt
-    c = *(rgba_t*)&p_fg;
-    xrcolor.red          = EXPANDBITS(c.r);
-    xrcolor.green        = EXPANDBITS(c.g);
-    xrcolor.blue         = EXPANDBITS(c.b);
-    xrcolor.alpha        = EXPANDBITS(c.a);
-    XftColorAllocValue(d, DefaultVisual(d, 0), DefaultColormap(d, 0), &xrcolor, &r.xft_prompt);
+	border_n_bg = border_e_bg = border_s_bg = border_w_bg = parse_color("#000", NULL);
 
-    // completion
-    c = *(rgba_t*)&compl_fg;
-    xrcolor.red          = EXPANDBITS(c.r);
-    xrcolor.green        = EXPANDBITS(c.g);
-    xrcolor.blue         = EXPANDBITS(c.b);
-    xrcolor.alpha        = EXPANDBITS(c.a);
-    XftColorAllocValue(d, DefaultVisual(d, 0), DefaultColormap(d, 0), &xrcolor, &r.xft_completion);
+	horizontal_layout = true;
 
-    // completion highlighted
-    c = *(rgba_t*)&compl_highlighted_fg;
-    xrcolor.red          = EXPANDBITS(c.r);
-    xrcolor.green        = EXPANDBITS(c.g);
-    xrcolor.blue         = EXPANDBITS(c.b);
-    xrcolor.alpha        = EXPANDBITS(c.a);
-    XftColorAllocValue(d, DefaultVisual(d, 0), DefaultColormap(d, 0), &xrcolor, &r.xft_completion_highlighted);
-  }
+	/* Read the resources */
+	XrmInitialize();
+	xrm = XResourceManagerString(d);
+	xdb = NULL;
+	if (xrm != NULL) {
+		XrmValue value;
+		char *datatype[20];
+
+		xdb = XrmGetStringDatabase(xrm);
+
+		if (XrmGetResource(xdb, "MyMenu.font", "*", datatype, &value) == true) {
+			free(fontname);
+			fontname = strdup(value.addr);
+			check_allocation(fontname);
+		} else {
+			fprintf(stderr, "no font defined, using %s\n", fontname);
+		}
+
+		if (XrmGetResource(xdb, "MyMenu.layout", "*", datatype, &value) == true)
+			horizontal_layout = !strcmp(value.addr, "horizontal");
+		else
+			fprintf(stderr, "no layout defined, using horizontal\n");
+
+		if (XrmGetResource(xdb, "MyMenu.prompt", "*", datatype, &value) == true) {
+			free(ps1);
+			ps1 = normalize_str(value.addr);
+		} else {
+			fprintf(stderr, "no prompt defined, using \"%s\" as default\n", ps1);
+		}
+
+		if (XrmGetResource(xdb, "MyMenu.width", "*", datatype, &value) == true)
+			width = parse_int_with_percentage(value.addr, width, d_width);
+		else
+			fprintf(stderr, "no width defined, using %d\n", width);
+
+		if (XrmGetResource(xdb, "MyMenu.height", "*", datatype, &value) == true)
+			height = parse_int_with_percentage(value.addr, height, d_height);
+		else
+			fprintf(stderr, "no height defined, using %d\n", height);
+
+		if (XrmGetResource(xdb, "MyMenu.x", "*", datatype, &value) == true)
+			x = parse_int_with_pos(value.addr, x, d_width, width);
+		else
+			fprintf(stderr, "no x defined, using %d\n", x);
+
+		if (XrmGetResource(xdb, "MyMenu.y", "*", datatype, &value) == true)
+			y = parse_int_with_pos(value.addr, y, d_height, height);
+		else
+			fprintf(stderr, "no y defined, using %d\n", y);
+
+		if (XrmGetResource(xdb, "MyMenu.padding", "*", datatype, &value) == true)
+			padding = parse_integer(value.addr, padding);
+		else
+			fprintf(stderr, "no padding defined, using %d\n", padding);
+
+		if (XrmGetResource(xdb, "MyMenu.border.size", "*", datatype, &value) == true) {
+			char **borders;
+
+			borders = parse_csslike(value.addr);
+			if (borders != NULL) {
+				border_n = parse_integer(borders[0], 0);
+				border_e = parse_integer(borders[1], 0);
+				border_s = parse_integer(borders[2], 0);
+				border_w = parse_integer(borders[3], 0);
+			} else
+				fprintf(stderr, "error while parsing MyMenu.border.size\n");
+		} else
+			fprintf(stderr, "no border defined, using 0.\n");
+
+		/* Prompt */
+		if (XrmGetResource(xdb, "MyMenu.prompt.foreground", "*", datatype, &value) == true)
+			p_fg = parse_color(value.addr, "#fff");
+
+		if (XrmGetResource(xdb, "MyMenu.prompt.background", "*", datatype, &value) == true)
+			p_bg = parse_color(value.addr, "#000");
+
+		/* Completions */
+		if (XrmGetResource(xdb, "MyMenu.completion.foreground", "*", datatype, &value) == true)
+			compl_fg = parse_color(value.addr, "#fff");
+
+		if (XrmGetResource(xdb, "MyMenu.completion.background", "*", datatype, &value) == true)
+			compl_bg = parse_color(value.addr, "#000");
+		else
+			compl_bg = parse_color("#000", NULL);
+
+		/* Completion Highlighted */
+		if (XrmGetResource(xdb, "MyMenu.completion_highlighted.foreground", "*", datatype, &value) == true)
+			compl_highlighted_fg = parse_color(value.addr, "#000");
+
+		if (XrmGetResource(xdb, "MyMenu.completion_highlighted.background", "*", datatype, &value) == true)
+			compl_highlighted_bg = parse_color(value.addr, "#fff");
+		else
+			compl_highlighted_bg = parse_color("#fff", NULL);
+
+		// border
+		if (XrmGetResource(xdb, "MyMenu.border.color", "*", datatype, &value) == true) {
+			char **colors;
+			colors = parse_csslike(value.addr);
+			if (colors != NULL) {
+				border_n_bg = parse_color(colors[0], "#000");
+				border_e_bg = parse_color(colors[1], "#000");
+				border_s_bg = parse_color(colors[2], "#000");
+				border_w_bg = parse_color(colors[3], "#000");
+			} else
+				fprintf(stderr, "error while parsing MyMenu.border.color\n");
+		}
+	}
+
+	// second round of args parsing
+	optind = 0; // reset the option index
+	while ((ch = getopt(argc, argv, ARGS)) != -1) {
+		switch (ch) {
+		case 'a':
+			first_selected = true;
+			break;
+
+		case 'A':
+			/* free_text -- already catched */
+		case 'd':
+			/* separator -- this case was already catched */
+		case 'e':
+			/* embedding mymenu this case was already catched. */
+		case 'm':
+			/* multiple selection this case was already catched. */
+
+			break;
+		case 'p': {
+			char *newprompt;
+			newprompt = strdup(optarg);
+			if (newprompt != NULL) {
+				free(ps1);
+				ps1 = newprompt;
+			}
+			break;
+		}
+		case 'x':
+			x = parse_int_with_pos(optarg, x, d_width, width);
+			break;
+		case 'y':
+			y = parse_int_with_pos(optarg, y, d_height, height);
+			break;
+		case 'P':
+			padding = parse_integer(optarg, padding);
+			break;
+		case 'l':
+			horizontal_layout = !strcmp(optarg, "horizontal");
+			break;
+		case 'f': {
+			char *newfont;
+			if ((newfont = strdup(optarg)) != NULL) {
+				free(fontname);
+				fontname = newfont;
+			}
+			break;
+		}
+		case 'W':
+			width = parse_int_with_percentage(optarg, width, d_width);
+			break;
+		case 'H':
+			height = parse_int_with_percentage(optarg, height, d_height);
+			break;
+		case 'b': {
+			char **borders;
+			if ((borders = parse_csslike(optarg)) != NULL) {
+				border_n = parse_integer(borders[0], 0);
+				border_e = parse_integer(borders[1], 0);
+				border_s = parse_integer(borders[2], 0);
+				border_w = parse_integer(borders[3], 0);
+			} else
+				fprintf(stderr, "Error parsing b option\n");
+			break;
+		}
+		case 'B': {
+			char **colors;
+			if ((colors = parse_csslike(optarg)) != NULL) {
+				border_n_bg = parse_color(colors[0], "#000");
+				border_e_bg = parse_color(colors[1], "#000");
+				border_s_bg = parse_color(colors[2], "#000");
+				border_w_bg = parse_color(colors[3], "#000");
+			} else
+				fprintf(stderr, "error while parsing B option\n");
+			break;
+		}
+		case 't': {
+			p_fg = parse_color(optarg, NULL);
+			break;
+		}
+		case 'T': {
+			p_bg = parse_color(optarg, NULL);
+			break;
+		}
+		case 'c': {
+			compl_fg = parse_color(optarg, NULL);
+			break;
+		}
+		case 'C': {
+			compl_bg = parse_color(optarg, NULL);
+			break;
+		}
+		case 's': {
+			compl_highlighted_fg = parse_color(optarg, NULL);
+			break;
+		}
+		case 'S': {
+			compl_highlighted_bg = parse_color(optarg, NULL);
+			break;
+		}
+		default:
+			fprintf(stderr, "Unrecognized option %c\n", ch);
+			status = ERR;
+			break;
+		}
+	}
+
+	/* since only now we know if the first should be selected,
+	 * update the completion here */
+	update_completions(cs, text, lines, vlines, first_selected);
+
+	// create the window
+	attr.colormap = cmap;
+	attr.override_redirect = true;
+	attr.border_pixel = 0;
+	attr.background_pixel = 0x80808080;
+	attr.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask;
+
+	w = XCreateWindow(d,
+		parent_window,
+		x + offset_x, y + offset_y,
+		width, height,
+		0,
+		vinfo.depth,
+		InputOutput,
+		vinfo.visual,
+		CWBorderPixel | CWBackPixel | CWColormap | CWEventMask | CWOverrideRedirect,
+		&attr);
+
+	set_win_atoms_hints(d, w, width, height);
+
+	XSelectInput(d, w, StructureNotifyMask | KeyPressMask | KeymapStateMask | ButtonPressMask);
+	XMapRaised(d, w);
+
+	/* If embed, listen for other events as well */
+	if (embed) {
+		Window		*children, parent, root;
+		unsigned int	children_no;
+
+		XSelectInput(d, parent_window, FocusChangeMask);
+		if (XQueryTree(d, parent_window, &root, &parent, &children, &children_no) && children) {
+			for (i = 0; i < children_no && children[i] != w; ++i)
+				XSelectInput(d, children[i], FocusChangeMask);
+			XFree(children);
+		}
+		grabfocus(d, w);
+	}
+
+	take_keyboard(d, w);
+
+	struct rendering r = {
+		.d                          = d,
+		.w                          = w,
+		.width                      = width,
+		.height                     = height,
+		.padding                    = padding,
+		.x_zero                     = border_w,
+		.y_zero                     = border_n,
+		.offset                     = 0,
+		.free_text                  = free_text,
+		.first_selected             = first_selected,
+		.multiple_select            = multiple_select,
+		.border_n                   = border_n,
+		.border_e                   = border_e,
+		.border_s                   = border_s,
+		.border_w                   = border_w,
+		.horizontal_layout          = horizontal_layout,
+		.ps1                        = ps1,
+		.ps1len                     = strlen(ps1),
+		.prompt                     = XCreateGC(d, w, 0, &values),
+		.prompt_bg                  = XCreateGC(d, w, 0, &values),
+		.completion                 = XCreateGC(d, w, 0, &values),
+		.completion_bg              = XCreateGC(d, w, 0, &values),
+		.completion_highlighted     = XCreateGC(d, w, 0, &values),
+		.completion_highlighted_bg  = XCreateGC(d, w, 0, &values),
+		.border_n_bg                = XCreateGC(d, w, 0, &values),
+		.border_e_bg                = XCreateGC(d, w, 0, &values),
+		.border_s_bg                = XCreateGC(d, w, 0, &values),
+		.border_w_bg                = XCreateGC(d, w, 0, &values),
+	};
+
+	if (load_font(&r, fontname) == -1)
+		status = ERR;
+
+#ifdef USE_XFT
+	r.xftdraw = XftDrawCreate(d, w, vinfo.visual, DefaultColormap(d, 0));
+
+	{
+		rgba_t c;
+		XRenderColor xrcolor;
+
+		/* Prompt */
+		c = *(rgba_t*)&p_fg;
+		xrcolor.red          = EXPANDBITS(c.r);
+		xrcolor.green        = EXPANDBITS(c.g);
+		xrcolor.blue         = EXPANDBITS(c.b);
+		xrcolor.alpha        = EXPANDBITS(c.a);
+		XftColorAllocValue(d, DefaultVisual(d, 0), DefaultColormap(d, 0), &xrcolor, &r.xft_prompt);
+
+		/* Completion */
+		c = *(rgba_t*)&compl_fg;
+		xrcolor.red          = EXPANDBITS(c.r);
+		xrcolor.green        = EXPANDBITS(c.g);
+		xrcolor.blue         = EXPANDBITS(c.b);
+		xrcolor.alpha        = EXPANDBITS(c.a);
+		XftColorAllocValue(d, DefaultVisual(d, 0), DefaultColormap(d, 0), &xrcolor, &r.xft_completion);
+
+		/* Completion highlighted */
+		c = *(rgba_t*)&compl_highlighted_fg;
+		xrcolor.red          = EXPANDBITS(c.r);
+		xrcolor.green        = EXPANDBITS(c.g);
+		xrcolor.blue         = EXPANDBITS(c.b);
+		xrcolor.alpha        = EXPANDBITS(c.a);
+		XftColorAllocValue(d, DefaultVisual(d, 0), DefaultColormap(d, 0), &xrcolor, &r.xft_completion_highlighted);
+	}
 #endif
 
-  // load the colors in our GCs
-  /* XSetForeground(d, r.prompt, p_fg.pixel); */
-  XSetForeground(d, r.prompt, p_fg);
-  XSetForeground(d, r.prompt_bg, p_bg);
-  /* XSetForeground(d, r.completion, compl_fg.pixel); */
-  XSetForeground(d, r.completion, compl_fg);
-  XSetForeground(d, r.completion_bg, compl_bg);
-  /* XSetForeground(d, r.completion_highlighted, compl_highlighted_fg.pixel); */
-  XSetForeground(d, r.completion_highlighted, compl_highlighted_fg);
-  XSetForeground(d, r.completion_highlighted_bg, compl_highlighted_bg);
-  XSetForeground(d, r.border_n_bg, border_n_bg);
-  XSetForeground(d, r.border_e_bg, border_e_bg);
-  XSetForeground(d, r.border_s_bg, border_s_bg);
-  XSetForeground(d, r.border_w_bg, border_w_bg);
+	/* Load the colors in our GCs */
+	XSetForeground(d, r.prompt, p_fg);
+	XSetForeground(d, r.prompt_bg, p_bg);
+	XSetForeground(d, r.completion, compl_fg);
+	XSetForeground(d, r.completion_bg, compl_bg);
+	XSetForeground(d, r.completion_highlighted, compl_highlighted_fg);
+	XSetForeground(d, r.completion_highlighted_bg, compl_highlighted_bg);
+	XSetForeground(d, r.border_n_bg, border_n_bg);
+	XSetForeground(d, r.border_e_bg, border_e_bg);
+	XSetForeground(d, r.border_s_bg, border_s_bg);
+	XSetForeground(d, r.border_w_bg, border_w_bg);
 
-  // open the X input method
-  XIM xim = XOpenIM(d, xdb, resname, resclass);
-  check_allocation(xim);
+	/* Open the X input method */
+	xim = XOpenIM(d, xdb, resname, resclass);
+	check_allocation(xim);
 
-  XIMStyles *xis = nil;
-  if (XGetIMValues(xim, XNQueryInputStyle, &xis, NULL) || !xis) {
-    fprintf(stderr, "Input Styles could not be retrieved\n");
-    return EX_UNAVAILABLE;
-  }
+	if (XGetIMValues(xim, XNQueryInputStyle, &xis, NULL) || !xis) {
+		fprintf(stderr, "Input Styles could not be retrieved\n");
+		return EX_UNAVAILABLE;
+	}
 
-  XIMStyle bestMatchStyle = 0;
-  for (int i = 0; i < xis->count_styles; ++i) {
-    XIMStyle ts = xis->supported_styles[i];
-    if (ts == (XIMPreeditNothing | XIMStatusNothing)) {
-      bestMatchStyle = ts;
-      break;
-    }
-  }
-  XFree(xis);
+	best_match_style = 0;
+	for (int i = 0; i < xis->count_styles; ++i) {
+		XIMStyle ts = xis->supported_styles[i];
+		if (ts == (XIMPreeditNothing | XIMStatusNothing)) {
+			best_match_style = ts;
+			break;
+		}
+	}
+	XFree(xis);
 
-  if (!bestMatchStyle) {
-    fprintf(stderr, "No matching input style could be determined\n");
-  }
+	if (!best_match_style)
+		fprintf(stderr, "No matching input style could be determined\n");
 
-  r.xic = XCreateIC(xim, XNInputStyle, bestMatchStyle, XNClientWindow, w, XNFocusWindow, w, NULL);
-  check_allocation(r.xic);
+	r.xic = XCreateIC(xim, XNInputStyle, best_match_style, XNClientWindow, w, XNFocusWindow, w, NULL);
+	check_allocation(r.xic);
 
-  // draw the window for the first time
-  draw(&r, text, cs);
+	/* Draw the window for the first time */
+	draw(&r, text, cs);
 
-  // main loop
-  while (status == LOOPING || status == OK_LOOP) {
-    status = loop(&r, &text, &textlen, cs, lines, vlines);
+	/* Main loop */
+	while (status == LOOPING || status == OK_LOOP) {
+		status = loop(&r, &text, &textlen, cs, lines, vlines);
 
-    if (status != ERR)
-      printf("%s\n", text);
+		if (status != ERR)
+			printf("%s\n", text);
 
-    if (!multiple_select && status == OK_LOOP)
-      status = OK;
-  }
+		if (!multiple_select && status == OK_LOOP)
+			status = OK;
+	}
 
-  release_keyboard(r.d);
+	XUngrabKeyboard(d, CurrentTime);
 
 #ifdef USE_XFT
-  XftColorFree(r.d, DefaultVisual(r.d, 0), DefaultColormap(r.d, 0), &r.xft_prompt);
-  XftColorFree(r.d, DefaultVisual(r.d, 0), DefaultColormap(r.d, 0), &r.xft_completion);
-  XftColorFree(r.d, DefaultVisual(r.d, 0), DefaultColormap(r.d, 0), &r.xft_completion_highlighted);
+	XftColorFree(r.d, DefaultVisual(r.d, 0), DefaultColormap(r.d, 0), &r.xft_prompt);
+	XftColorFree(r.d, DefaultVisual(r.d, 0), DefaultColormap(r.d, 0), &r.xft_completion);
+	XftColorFree(r.d, DefaultVisual(r.d, 0), DefaultColormap(r.d, 0), &r.xft_completion_highlighted);
 #endif
 
-  free(ps1);
-  free(fontname);
-  free(text);
+	free(ps1);
+	free(fontname);
+	free(text);
 
-  free(buf);
-  free(lines);
-  free(vlines);
-  compls_delete(cs);
+	free(buf);
+	free(lines);
+	free(vlines);
+	compls_delete(cs);
 
-  XDestroyWindow(r.d, r.w);
-  XCloseDisplay(r.d);
+	XDestroyWindow(r.d, r.w);
+	XCloseDisplay(r.d);
 
-  return status != OK;
+	return status != OK;
 }