-
-
Save nicklan/39f825bf8e80b7ed6c5b to your computer and use it in GitHub Desktop.
Patch to allow i3bar to display xbm icons
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/common.mk b/common.mk | |
index de5c7e9..eff3468 100644 | |
--- a/common.mk | |
+++ b/common.mk | |
@@ -83,6 +83,7 @@ XCB_CFLAGS := $(call cflags_for_lib, xcb) | |
XCB_CFLAGS += $(call cflags_for_lib, xcb-event) | |
XCB_LIBS := $(call ldflags_for_lib, xcb,xcb) | |
XCB_LIBS += $(call ldflags_for_lib, xcb-event,xcb-event) | |
+XCB_LIBS += $(call ldflags_for_lib, xcb-image,xcb-image) | |
ifeq ($(shell pkg-config --exists xcb-util 2>/dev/null || echo 1),1) | |
XCB_CFLAGS += $(call cflags_for_lib, xcb-atom) | |
XCB_CFLAGS += $(call cflags_for_lib, xcb-aux) | |
diff --git a/i3bar/include/common.h b/i3bar/include/common.h | |
index 05fb5aa..382db2d 100644 | |
--- a/i3bar/include/common.h | |
+++ b/i3bar/include/common.h | |
@@ -11,6 +11,7 @@ | |
#include <stdbool.h> | |
#include <xcb/xcb.h> | |
#include <xcb/xproto.h> | |
+#include <xcb/xcb_image.h> | |
#include "libi3.h" | |
#include "queue.h" | |
@@ -39,6 +40,7 @@ struct status_block { | |
i3String *full_text; | |
char *color; | |
+ char *icon; | |
uint32_t min_width; | |
blockalign_t align; | |
@@ -49,6 +51,7 @@ struct status_block { | |
uint32_t width; | |
uint32_t x_offset; | |
uint32_t x_append; | |
+ xcb_image_t *icon_image; | |
TAILQ_ENTRY(status_block) blocks; | |
}; | |
@@ -62,6 +65,7 @@ TAILQ_HEAD(statusline_head, status_block) statusline_head; | |
#include "workspaces.h" | |
#include "mode.h" | |
#include "trayclients.h" | |
+#include "xbm.h" | |
#include "xcb.h" | |
#include "config.h" | |
#include "libi3.h" | |
diff --git a/i3bar/include/xbm.h b/i3bar/include/xbm.h | |
new file mode 100644 | |
index 0000000..c899406 | |
--- /dev/null | |
+++ b/i3bar/include/xbm.h | |
@@ -0,0 +1,47 @@ | |
+/* | |
+ * vim:ts=4:sw=4:expandtab | |
+ * | |
+ * i3bar - an xcb-based status- and ws-bar for i3 | |
+ * © 2010-2011 Axel Wagner and contributors (see also: LICENSE) | |
+ * | |
+ * xbm.c: Parsing/Loading xbm data | |
+ * | |
+ */ | |
+#ifndef XBM_H_ | |
+#define XBM_H_ | |
+ | |
+#include "queue.h" | |
+ | |
+#define BUFSZ 512 | |
+ | |
+struct raw_xbm { | |
+ uint32_t w,h; | |
+ uint32_t dlen; | |
+ uint8_t *data; | |
+}; | |
+ | |
+struct Cached_Xbm { | |
+ char *path; | |
+ xcb_image_t *icon; | |
+ int used; | |
+ SLIST_ENTRY(Cached_Xbm) xbm_cache; | |
+}; | |
+ | |
+/* Set the format information for loaded icons */ | |
+void set_format(xcb_connection_t * c, uint8_t depth, uint8_t bpp); | |
+ | |
+/* Set xcb context color for icon, needed with pango patch */ | |
+void set_icon_color(xcb_connection_t *c, xcb_gcontext_t gc, | |
+ uint32_t foreground, uint32_t background); | |
+ | |
+/* get the xcb image for the given icon path */ | |
+xcb_image_t* get_icon(char* path); | |
+ | |
+/* cache functions */ | |
+void xbm_clear_cache_used(); | |
+xcb_image_t* xbm_get_cached_icon(char* path); | |
+void xbm_cache_icon(char* path, xcb_image_t* icon); | |
+void xbm_free_unused_icons(); | |
+ | |
+ | |
+#endif | |
diff --git a/i3bar/src/child.c b/i3bar/src/child.c | |
index bea1d58..e5ad23d 100644 | |
--- a/i3bar/src/child.c | |
+++ b/i3bar/src/child.c | |
@@ -85,6 +85,7 @@ static int stdin_start_array(void *context) { | |
first = TAILQ_FIRST(&statusline_head); | |
I3STRING_FREE(first->full_text); | |
FREE(first->color); | |
+ FREE(first->icon); | |
TAILQ_REMOVE(&statusline_head, first, blocks); | |
free(first); | |
} | |
@@ -132,6 +133,9 @@ static int stdin_string(void *context, const unsigned char *val, unsigned int le | |
if (strcasecmp(ctx->last_map_key, "color") == 0) { | |
sasprintf(&(ctx->block.color), "%.*s", len, val); | |
} | |
+ if (strcasecmp(ctx->last_map_key, "icon") == 0) { | |
+ sasprintf(&(ctx->block.icon), "%.*s", len, val); | |
+ } | |
if (strcasecmp(ctx->last_map_key, "align") == 0) { | |
if (len == strlen("left") && !strncmp((const char*)val, "left", strlen("left"))) { | |
ctx->block.align = ALIGN_LEFT; | |
@@ -176,6 +180,7 @@ static int stdin_end_array(void *context) { | |
TAILQ_FOREACH(current, &statusline_head, blocks) { | |
DLOG("full_text = %s\n", i3string_as_utf8(current->full_text)); | |
DLOG("color = %s\n", current->color); | |
+ DLOG("icon = %s\n",current->icon); | |
} | |
DLOG("end of dump\n"); | |
return 1; | |
diff --git a/i3bar/src/xbm.c b/i3bar/src/xbm.c | |
new file mode 100644 | |
index 0000000..af7d18c | |
--- /dev/null | |
+++ b/i3bar/src/xbm.c | |
@@ -0,0 +1,239 @@ | |
+/* | |
+ * vim:ts=4:sw=4:expandtab | |
+ * | |
+ * i3bar - an xcb-based status- and ws-bar for i3 | |
+ * © 2010-2011 Axel Wagner and contributors (see also: LICENSE) | |
+ * | |
+ * xbm.c: Parsing/Loading xbm data | |
+ * | |
+ * set_format from: | |
+ * http://vincentsanders.blogspot.de/2010/04/xcb-programming-is-hard.html | |
+ */ | |
+ | |
+#include <string.h> | |
+ | |
+#include <xcb/xcb.h> | |
+#include <stdio.h> | |
+#include <stdlib.h> | |
+#include <sys/types.h> | |
+#include <unistd.h> | |
+#include <errno.h> | |
+#include <string.h> | |
+ | |
+#include "common.h" | |
+ | |
+static xcb_format_t* format = NULL; | |
+ | |
+void set_format(xcb_connection_t * c, uint8_t depth, uint8_t bpp) { | |
+ const xcb_setup_t *setup = xcb_get_setup(c); | |
+ xcb_format_t *fmt = xcb_setup_pixmap_formats(setup); | |
+ xcb_format_t *fmtend = fmt + xcb_setup_pixmap_formats_length(setup); | |
+ for(; fmt != fmtend; ++fmt) | |
+ if((fmt->depth == depth) && (fmt->bits_per_pixel == bpp)) | |
+ format = fmt; | |
+} | |
+ | |
+/* With pango we need to set colors ourselves, since the pango | |
+ * patch only sets font color */ | |
+void set_icon_color(xcb_connection_t *c, xcb_gcontext_t gc, | |
+ uint32_t foreground, uint32_t background) { | |
+ uint32_t mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND; | |
+ uint32_t values[] = {foreground, background }; | |
+ xcb_change_gc(c, gc, mask, values); | |
+} | |
+ | |
+static int | |
+parse_xbm(char* file, struct raw_xbm* data) { | |
+ FILE *f; | |
+ char *str; | |
+ int i,p,r,lim; | |
+ char *c; | |
+ uint8_t *d; | |
+ | |
+ int in_array = 0; | |
+ | |
+ data->w = data->h = -1; | |
+ | |
+ f = fopen(file,"r"); | |
+ if (!f) { | |
+ ELOG("Could not open xbm file: %s",file); | |
+ return 1; | |
+ } | |
+ | |
+ str=malloc(BUFSZ); | |
+ if (!str) { | |
+ ELOG("Could not malloc buffer for xbm parsing"); | |
+ fclose(f); | |
+ return 1; | |
+ } | |
+ | |
+ while (fscanf(f," #define %s %d",str,&i) == 2) { | |
+ if (strstr(str,"width") != NULL) | |
+ data->w = i; | |
+ else if (strstr(str,"height") != NULL) | |
+ data->h = i; | |
+ } | |
+ | |
+ if (data->w <= 0 || data->h <= 0) { | |
+ ELOG("Could not find height and/or width in xbm: %s\n",file); | |
+ fclose(f); | |
+ free(str); | |
+ return 1; | |
+ } | |
+ | |
+ d = malloc(data->w*data->h); | |
+ p = 0; | |
+ | |
+ r = fread(str,1,BUFSZ,f); | |
+ lim = r; | |
+ while(r>0) { | |
+ if(in_array) { | |
+ while(c < (str+lim)) { | |
+ char* t = c; | |
+ while (*t && t!=(str+lim-1) && *t!=',' && *t!='}') t++; | |
+ if (*t && (*t==',' || *t=='}')) | |
+ d[p++] = strtol(c,NULL,16); | |
+ else | |
+ break; | |
+ c=t+1; | |
+ } | |
+ } else { | |
+ c = str; | |
+ while (*c && c!=(str+lim) && *c!='{') | |
+ c++; | |
+ if (*c && c!=(str+lim) && *c=='{') { | |
+ in_array=1; | |
+ c++; | |
+ if (c<(str+lim)) continue; | |
+ } | |
+ } | |
+ | |
+ i = (str+lim)-c; | |
+ if (i > 0) memcpy(str,c,i); | |
+ | |
+ r = fread(str+i,1,BUFSZ-i,f); | |
+ c=str; | |
+ lim = r+i; | |
+ } | |
+ | |
+ fclose(f); | |
+ free(str); | |
+ | |
+ /* will free if p==0, so we only have malloced mem | |
+ * when p!=0 */ | |
+ d = realloc(d,p); | |
+ | |
+ data->dlen = p; | |
+ data->data = d; | |
+ return p==0; | |
+} | |
+ | |
+static xcb_image_t * | |
+create_icon_image(struct raw_xbm *xbm) { | |
+ int i,rowsize,done,di; | |
+ unsigned char *imgdata; | |
+ if (format == NULL) return NULL; | |
+ | |
+ imgdata = (unsigned char *)malloc(xbm->w*xbm->h); | |
+ memset(imgdata,0,sizeof(imgdata)); | |
+ | |
+ rowsize = format->scanline_pad; | |
+ while(rowsize < xbm->w) rowsize+=format->scanline_pad; | |
+ | |
+ for(done=0,di=0,i=0;i<xbm->dlen;i++) { | |
+ imgdata[di] = xbm->data[i]; | |
+ di++; | |
+ done+=8; | |
+ if (done >= xbm->w) { | |
+ // pad out to rowsize | |
+ while (done < rowsize) { | |
+ di++; | |
+ done+=8; | |
+ } | |
+ done=0; | |
+ } | |
+ } | |
+ | |
+ return xcb_image_create(xbm->w, | |
+ xbm->h, | |
+ XCB_IMAGE_FORMAT_XY_BITMAP, | |
+ format->scanline_pad, | |
+ format->depth, | |
+ format->bits_per_pixel, | |
+ 0, | |
+ XCB_IMAGE_ORDER_LSB_FIRST, | |
+ XCB_IMAGE_ORDER_LSB_FIRST, | |
+ imgdata, | |
+ xbm->w*xbm->h, | |
+ imgdata); | |
+} | |
+ | |
+xcb_image_t* get_icon(char* path) { | |
+ xcb_image_t* ret; | |
+ struct raw_xbm rxbm; | |
+ | |
+ ret = xbm_get_cached_icon(path); | |
+ if (ret) return ret; | |
+ | |
+ DLOG("Loading xbm: %s\n",path); | |
+ | |
+ rxbm.data = NULL; | |
+ if (parse_xbm(path,&rxbm)) { | |
+ ELOG("Cannot parse xbm: %s\n",path); | |
+ return NULL; | |
+ } | |
+ ret = create_icon_image(&rxbm); | |
+ xbm_cache_icon(path,ret); | |
+ free(rxbm.data); | |
+ return ret; | |
+} | |
+ | |
+/* Below we implement a cache for parsed xbm icons. | |
+ * | |
+ * If an icon is used in a draw of the bar, it sticks around until the | |
+ * next draw, otherwise we throw it away at the end of a update. The | |
+ * idea being that most icons are fairly static, or change rarely | |
+ * (like when a battery threshold is reached). Therefore, we are very | |
+ * aggresive about throwing things out of the cache. | |
+ */ | |
+ | |
+static SLIST_HEAD(xbm_cache_head, Cached_Xbm) xbm_cache; | |
+ | |
+void xbm_clear_cache_used() { | |
+ struct Cached_Xbm* cxbm; | |
+ SLIST_FOREACH(cxbm,&xbm_cache,xbm_cache) { | |
+ cxbm->used = 0; | |
+ } | |
+} | |
+ | |
+xcb_image_t* xbm_get_cached_icon(char* path) { | |
+ struct Cached_Xbm* cxbm; | |
+ SLIST_FOREACH(cxbm,&xbm_cache,xbm_cache) { | |
+ if (!strcmp(path,cxbm->path)) { | |
+ cxbm->used = 1; | |
+ return cxbm->icon; | |
+ } | |
+ } | |
+ return NULL; | |
+} | |
+ | |
+void xbm_cache_icon(char* path, xcb_image_t* icon) { | |
+ struct Cached_Xbm *cxbm = smalloc(sizeof(struct Cached_Xbm)); | |
+ cxbm->used = 1; | |
+ cxbm->path = strdup(path); | |
+ cxbm->icon = icon; | |
+ SLIST_INSERT_HEAD(&xbm_cache,cxbm,xbm_cache); | |
+} | |
+ | |
+void xbm_free_unused_icons() { | |
+ struct Cached_Xbm* cxbm; | |
+ SLIST_FOREACH(cxbm,&xbm_cache,xbm_cache) { | |
+ if (!cxbm->used) { | |
+ SLIST_REMOVE(&xbm_cache, cxbm, Cached_Xbm, xbm_cache); | |
+ free(cxbm->path); | |
+ xcb_image_destroy(cxbm->icon); | |
+ free(cxbm); | |
+ } | |
+ } | |
+} | |
+ | |
diff --git a/i3bar/src/xcb.c b/i3bar/src/xcb.c | |
index 6bd8039..4ca42bb 100644 | |
--- a/i3bar/src/xcb.c | |
+++ b/i3bar/src/xcb.c | |
@@ -124,6 +124,14 @@ void refresh_statusline(void) { | |
block->width = predict_text_width(block->full_text); | |
+ if (block->icon) { | |
+ block->icon_image = get_icon(block->icon); | |
+ if (!block->icon_image) | |
+ ELOG("Could not load icon: %s\n",block->icon); | |
+ else | |
+ statusline_width += block->icon_image->width; | |
+ } | |
+ | |
/* Compute offset and append for text aligment in min_width. */ | |
if (block->min_width <= block->width) { | |
block->x_offset = 0; | |
@@ -168,6 +176,18 @@ void refresh_statusline(void) { | |
uint32_t colorpixel = (block->color ? get_colorpixel(block->color) : colors.bar_fg); | |
set_font_colors(statusline_ctx, colorpixel, colors.bar_bg); | |
+ | |
+ if (block->icon_image) { | |
+ int h = (font.height-block->icon_image->height)/2; | |
+ set_icon_color(xcb_connection,statusline_ctx, | |
+ colorpixel,colors.bar_bg); | |
+ xcb_image_put(xcb_connection,statusline_pm,statusline_ctx, | |
+ block->icon_image,x,h,0); | |
+ set_icon_color(xcb_connection,statusline_ctx, | |
+ get_colorpixel("#666666"),colors.bar_bg); | |
+ x+=block->icon_image->width+1; | |
+ } | |
+ | |
draw_text(block->full_text, statusline_pm, statusline_ctx, x + block->x_offset, 0, block->width); | |
x += block->width + block->x_offset + block->x_append; | |
@@ -923,6 +943,8 @@ char *init_xcb_early() { | |
} | |
} | |
+ /* Set the format for icons */ | |
+ set_format(xcb_connection,1,1); | |
if (xcb_request_failed(sl_pm_cookie, "Could not allocate statusline-buffer") || | |
xcb_request_failed(clear_ctx_cookie, "Could not allocate statusline-buffer-clearcontext") || | |
@@ -1434,6 +1456,7 @@ void draw_bars(bool unhide) { | |
DLOG("Drawing Bars...\n"); | |
int i = 0; | |
+ xbm_clear_cache_used(); | |
refresh_statusline(); | |
static char *last_urgent_ws = NULL; | |
@@ -1601,6 +1624,8 @@ void draw_bars(bool unhide) { | |
} | |
} | |
+ xbm_free_unused_icons(); | |
+ | |
redraw_bars(); | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Lines 407-408 don't belong. It sets the foreground colour to #666666.