From 87760af9cdb2ec7d54aa4b48f5964b52bfa24f61 Mon Sep 17 00:00:00 2001 From: Sudaraka Wijesinghe Date: Mon, 16 Jun 2014 20:34:40 +0530 Subject: [PATCH] i3bar XBM icon support Ported the patch from 4.5 to 4.8 Original source: https://github.com/ashinkarov/i3-extras/blob/master/i3bar-xbm-icons.patch --- common.mk | 2 + i3bar/include/common.h | 4 + i3bar/include/xbm_image.h | 14 +++ i3bar/src/child.c | 11 ++ i3bar/src/xbm_image.c | 278 ++++++++++++++++++++++++++++++++++++++++++++++ i3bar/src/xcb.c | 44 +++++++- 6 files changed, 350 insertions(+), 3 deletions(-) create mode 100644 i3bar/include/xbm_image.h create mode 100644 i3bar/src/xbm_image.c diff --git a/common.mk b/common.mk index b086bc8..8929447 100644 --- a/common.mk +++ b/common.mk @@ -80,8 +80,10 @@ ldflags_for_lib = $(shell pkg-config --exists 2>/dev/null $(1) && pkg-config --l # XCB common stuff XCB_CFLAGS := $(call cflags_for_lib, xcb) XCB_CFLAGS += $(call cflags_for_lib, xcb-event) +XCB_CFLAGS += $(call cflags_for_lib, xcb-image) 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 d63780d..ec4b84c 100644 --- a/i3bar/include/common.h +++ b/i3bar/include/common.h @@ -12,6 +12,7 @@ #include #include "libi3.h" #include "queue.h" +#include "xbm_image.h" typedef struct rect_t rect; @@ -41,6 +42,9 @@ struct status_block { uint32_t min_width; blockalign_t align; + struct xbm_image *icon; + char *icon_color; + bool urgent; bool no_separator; diff --git a/i3bar/include/xbm_image.h b/i3bar/include/xbm_image.h new file mode 100644 index 0000000..3c01c84 --- /dev/null +++ b/i3bar/include/xbm_image.h @@ -0,0 +1,14 @@ +#ifndef __XBM_IMAGE_H__ +#define __XBM_IMAGE_H__ + +struct xbm_image { + int width, height; + char * id; + char * data; +}; + + +struct xbm_image * xbm_from_file (char *); +void xbm_free (struct xbm_image *); + +#endif /* __XBM_IMAGE_H__ */ diff --git a/i3bar/src/child.c b/i3bar/src/child.c index 5867ce4..a8f2622 100644 --- a/i3bar/src/child.c +++ b/i3bar/src/child.c @@ -141,6 +141,8 @@ static int stdin_start_array(void *context) { FREE(first->color); FREE(first->name); FREE(first->instance); + FREE(first->icon_color); + xbm_free (first->icon); TAILQ_REMOVE(&statusline_head, first, blocks); free(first); } @@ -187,6 +189,15 @@ static int stdin_string(void *context, const unsigned char *val, size_t len) { if (strcasecmp(ctx->last_map_key, "color") == 0) { sasprintf(&(ctx->block.color), "%.*s", len, val); } + if (strcasecmp(ctx->last_map_key, "icon") == 0) { + char * s; + sasprintf(&s, "%.*s", len, val); + ctx->block.icon = xbm_from_file(s); + FREE(s); + } + if (strcasecmp(ctx->last_map_key, "icon_color") == 0) { + sasprintf(&(ctx->block.icon_color), "%.*s", len, val); + } if (strcasecmp(ctx->last_map_key, "align") == 0) { if (len == strlen("left") && !strncmp((const char *)val, "left", strlen("left"))) { ctx->block.align = ALIGN_LEFT; diff --git a/i3bar/src/xbm_image.c b/i3bar/src/xbm_image.c new file mode 100644 index 0000000..ea99ffa --- /dev/null +++ b/i3bar/src/xbm_image.c @@ -0,0 +1,278 @@ +#include +#include +#include +#include +#include + +#include "xbm_image.h" + +#if FILENAME_CHECKING +/* Assume that file is called xxx.xbm, get xxx out of the filename + for furhter validation during parsing. */ +static char * +validate_fname (char * fname) +{ + char * dot = strrchr (fname, '.'); + char * slash = strrchr (fname, '/'); + char * ret = NULL; + unsigned sz = 0; + + if (NULL == dot) + return NULL; + + if (0 != strncmp (dot, ".xbm", 4)) + return NULL; + + if (slash) + fname = slash + 1; + + sz = dot - fname; + ret = malloc (sz + 1); + strncpy (ret, fname, sz); + ret[sz] = 0; + return ret; +} +#endif + +static char * +read_id (FILE * f) +{ + unsigned sz = 2, ptr = 0; + char * data = malloc (sz); + int c; + + c = fgetc (f); + if (isalpha (c) || c == '_') + data[ptr++] = c; + else + goto out; + + while (true) { + c = fgetc (f); + if (ptr == sz - 1) + data = realloc (data, sz *= 2); + + if (isalnum (c) || c == '_') + data[ptr++] = c; + else { + ungetc (c, f); + break; + } + } + + data[ptr] = '\0'; + return data; + +out: + if (data) + free (data); + + return NULL; +} + + +static bool +read_string (FILE * f, const char * s) +{ + while (*s != '\0') { + int c = fgetc (f); + if (c == EOF || c != *s) { + ungetc (c, f); + return false; + } + s++; + } + + return true; +} + +static bool +skip_spaces (FILE * f) +{ + int c; + while (isspace (c = fgetc (f)) && c != EOF) + ; + + ungetc (c, f); + return true; +} + +static inline bool +string_ends_with (const char * s, const char * postfix) +{ + return strlen (s) > strlen (postfix) + && strncmp (s + strlen (s) - strlen (postfix), + postfix, strlen (postfix)) == 0; +} + +#define READ_WORD_EAT_SPACES(file, word) \ +do { \ + if (!read_string (file, word)) \ + return false; \ + skip_spaces (file); \ +} while (0) + + +static bool +read_define (FILE * f, struct xbm_image * img) +{ + unsigned sz; + char * wh; + + READ_WORD_EAT_SPACES (f, "define"); + + /* read the _width variable, and save for + later validation in the img. */ + if (NULL == (wh = read_id (f))) + return false; + + if (string_ends_with (wh, "_width")) { + if (!img->id) { + unsigned idsz = strlen (wh) - strlen ("_width"); + img->id = malloc (idsz+1); + strncpy (img->id, wh, idsz); + img->id[idsz] = '\0'; + } else if (0 != strncmp (img->id, wh, strlen (img->id))) { + free (wh); + return false; + } + + free (wh); + skip_spaces (f); + if (fscanf (f, "%u", &sz) < 1) + return false; + + img->width = sz; + } else if (string_ends_with (wh, "_height")) { + if (!img->id) { + unsigned idsz = strlen (wh) - strlen ("_height"); + img->id = malloc (idsz+1); + strncpy (img->id, wh, idsz); + img->id[idsz] = '\0'; + } else if (0 != strncmp (img->id, wh, strlen (img->id))) { + free (wh); + return false; + } + + free (wh); + skip_spaces (f); + if (fscanf (f, "%u", &sz) < 1) + return false; + + img->height = sz; + } else + return false; + + return true; +} + +static bool +read_data (FILE * f, struct xbm_image * img) +{ + int sz, i; + char * data; + + /* size in bytes */ + sz = (img->width / 8 + !!(img->width % 8)) * img->height; + + data = malloc (sz); + + READ_WORD_EAT_SPACES (f, "tatic"); + READ_WORD_EAT_SPACES (f, "unsigned"); + READ_WORD_EAT_SPACES (f, "char"); + READ_WORD_EAT_SPACES (f, img->id); + READ_WORD_EAT_SPACES (f, "_bits[]"); + READ_WORD_EAT_SPACES (f, "="); + READ_WORD_EAT_SPACES (f, "{"); + + for (i = 0; i < sz; i++) { + unsigned value; + if (fscanf (f, "%x", &value) < 1 || value > 255) + return false; + + data[i] = (char)value; + skip_spaces (f); + if (i != sz - 1) + READ_WORD_EAT_SPACES (f, ","); + } + + READ_WORD_EAT_SPACES (f, "}"); + READ_WORD_EAT_SPACES (f, ";"); + img->data = data; + return true; +} + + +struct xbm_image * +xbm_from_file (char * fname) +{ + struct xbm_image * img = NULL; + FILE * f = NULL; + int c; + + if (!(f = fopen (fname, "r"))) + goto out; + + img = malloc (sizeof (struct xbm_image)); + img->id = NULL; + img->width = -1; + img->height = -1; + + do { + c = fgetc (f); + if (c == '#') { + if (!read_define (f, img)) + goto out; + } else if (c == 's') { + if (img->width == -1 || img->height == -1 || !img->id + || !read_data (f, img)) + goto out; + } else if (isspace (c) || c == EOF) + ; + else + goto out; + } while (c != EOF); + + fclose (f); + return img; + +out: + if (f) + fclose (f); + + return NULL; +} + +void +xbm_free (struct xbm_image * img) +{ + if (!img) + return; + + if (img->id) + free (img->id); + if (img->data) + free (img->data); + + free (img); +} + +#ifdef TESTING +int +main (int argc, char *argv[]) +{ + struct xbm_image * img; + int i; + + img = xbm_from_file (argv[1]); + + if (img != NULL) { + for (i = 0; i < (img->width /8 + !!(img->width % 8)) * img->height; i++) + printf ("%x, ", (unsigned char)img->data[i]); + + xbm_free (img); + } + + return EXIT_SUCCESS; +} +#endif diff --git a/i3bar/src/xcb.c b/i3bar/src/xcb.c index a29f909..ab63e66 100644 --- a/i3bar/src/xcb.c +++ b/i3bar/src/xcb.c @@ -10,6 +10,7 @@ #include #include #include +#include #ifdef XCB_COMPAT #include "xcb_compat.h" @@ -129,7 +130,7 @@ void refresh_statusline(void) { /* Predict the text width of all blocks (in pixels). */ TAILQ_FOREACH (block, &statusline_head, blocks) { - if (i3string_get_num_bytes(block->full_text) == 0) + if (i3string_get_num_bytes(block->full_text) == 0 && block->icon == NULL) continue; block->width = predict_text_width(block->full_text); @@ -159,6 +160,10 @@ void refresh_statusline(void) { block->width += block->sep_block_width; statusline_width += block->width + block->x_offset + block->x_append; + + /* Add some space between the text and the icon. */ + if (block->icon) + statusline_width += block->icon->width + 5; } /* If the statusline is bigger than our screen we need to make sure that @@ -174,12 +179,45 @@ void refresh_statusline(void) { /* Draw the text of each block. */ uint32_t x = 0; TAILQ_FOREACH (block, &statusline_head, blocks) { - if (i3string_get_num_bytes(block->full_text) == 0) + if (i3string_get_num_bytes(block->full_text) == 0 && block->icon == NULL) continue; + if (block->icon != NULL) { + xcb_image_t * img; + + img = xcb_image_create_native (conn, block->icon->height, + block->icon->width, + XCB_IMAGE_FORMAT_XY_BITMAP, + 1, NULL, ~0, NULL); + + img->data = malloc (img->size); + memset (img->data, 0, img->size); + + for (int i = 0; i < block->icon->height; i++) + for (int j = 0; j < block->icon->width; j++) { + unsigned pos = (img->width /8 + !!(img->width % 8))*i + j/8; + bool p = !!(block->icon->data[pos] & (1 << (j%8))); + xcb_image_put_pixel (img, j, i, p); + } + + uint32_t mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND; + if (block->icon_color) { + uint32_t values[] = { get_colorpixel(block->icon_color), colors.bar_bg }; + xcb_change_gc(xcb_connection, statusline_ctx, mask, values); + } else { + uint32_t values[] = { colors.bar_fg, colors.bar_bg }; + xcb_change_gc(xcb_connection, statusline_ctx, mask, values); + } + + xcb_image_put (conn, statusline_pm, statusline_ctx, + img, x, (font.height - block->icon->height)/2, 0); + xcb_image_destroy(img); + x += block->icon->width + 5; + } + uint32_t colorpixel = (block->color ? get_colorpixel(block->color) : colors.bar_fg); set_font_colors(statusline_ctx, colorpixel, colors.bar_bg); - draw_text(block->full_text, statusline_pm, statusline_ctx, x + block->x_offset, 1, block->width); + 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; if (TAILQ_NEXT(block, blocks) != NULL && !block->no_separator && block->sep_block_width > 0) { -- 2.0.0