From 3c2b9ff150d3015f7288a0c2f7d03799312aee1c Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Fri, 21 Nov 2014 14:19:31 +0100 Subject: [PATCH 01/22] Fix alpha mixing as suggested in #75 --- bar.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bar.c b/bar.c index f911913..17e4004 100644 --- a/bar.c +++ b/bar.c @@ -169,9 +169,9 @@ parse_color (const char *str, char **end, const uint32_t def) unsigned int b = (tmp&0x000000ff); if (a) { - r = (r * 255) / a; - g = (g * 255) / a; - b = (b * 255) / a; + r = (r * a) / 255; + g = (g * a) / 255; + b = (b * a) / 255; /* Clamp on overflow */ if (r > 255) r = 255; From 5746d15cf86540ce758fe9821b1362ab7007c91b Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Sat, 13 Dec 2014 12:12:34 +0100 Subject: [PATCH 02/22] Handle escaped : in clickable areas. Fixes #92 --- bar.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/bar.c b/bar.c index 17e4004..540c078 100644 --- a/bar.c +++ b/bar.c @@ -249,6 +249,7 @@ bool area_add (char *str, const char *optend, char **end, monitor_t *mon, const int x, const int align, const int button) { char *p = str; + char *trail; area_t *a = &astack.slot[astack.pos]; if (astack.pos == N) { @@ -286,7 +287,9 @@ area_add (char *str, const char *optend, char **end, monitor_t *mon, const int x return true; } - char *trail = strchr(++p, ':'); + /* Found the closing : and check if it's just an escaped one */ + for (trail = strchr(++p, ':'); trail && trail[-1] == '\\'; trail = strchr(trail + 1, ':')) + ; /* Find the trailing : and make sure it's whitin the formatting block, also reject empty commands */ if (!trail || p == trail || trail > optend) { @@ -296,6 +299,15 @@ area_add (char *str, const char *optend, char **end, monitor_t *mon, const int x *trail = '\0'; + /* Sanitize the user command by unescaping all the : */ + for (char *needle = p; *needle; needle++) { + int delta = trail - &needle[1]; + if (needle[0] == '\\' && needle[1] == ':') { + memmove(&needle[0], &needle[1], delta); + needle[delta] = 0; + } + } + /* This is a pointer to the string buffer allocated in the main */ a->cmd = p; a->align = align; From 145c66b47143cb22b9916424d45f849386b7c477 Mon Sep 17 00:00:00 2001 From: John Vogel Date: Tue, 6 Jan 2015 18:37:08 -0500 Subject: [PATCH 03/22] Add break from main loop when X conn has error --- bar.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/bar.c b/bar.c index 540c078..ba71f91 100644 --- a/bar.c +++ b/bar.c @@ -1119,6 +1119,10 @@ main (int argc, char **argv) for (;;) { bool redraw = false; + /* If connection is in error state, then it has been shut down. */ + if (xcb_connection_has_error(c)) + break; + if (poll(pollin, 2, -1) > 0) { if (pollin[0].revents & POLLHUP) { /* No more data... */ if (permanent) pollin[0].fd = -1; /* ...null the fd and continue polling :D */ From 3b8afc6b05190e62713b27effeb087aecfbcf751 Mon Sep 17 00:00:00 2001 From: Sakari Kapanen Date: Wed, 21 Jan 2015 20:43:22 +0200 Subject: [PATCH 04/22] Allow explicitly specifying the font with %{Tindex} --- README.pod | 6 +++++- bar.c | 34 ++++++++++++++++++++++++++++------ 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/README.pod b/README.pod index b276dd5..b610ab8 100644 --- a/README.pod +++ b/README.pod @@ -84,6 +84,10 @@ Set the text background color. The parameter I can be I<-> or a color in Set the text foreground color. The parameter I can be I<-> or a color in one of the formats mentioned before. The special value I<-> resets the color to the default one. +=item BI + +Set the font used to draw the following text. The parameter I is a 1-based index of the font list supplied to bar. Any other value (for example I<->) resets bar to normal behaviour (matching the first font that can be used for that character). If the selected font can't be used to draw a character, bar will fall back to normal behaviour for that character. + =item BI Set the text underline color. The parameter I can be I<-> or a color in one of the formats mentioned before. The special value I<-> resets the color to the default one. @@ -164,6 +168,6 @@ L Xinerama support was kindly contributed by Stebalien -RandR support was kindly contributed by jvvv +RandR support was kindly contributed by jvvv Clickable areas support was heavily based off u-ra contribution diff --git a/bar.c b/bar.c index ba71f91..cf99ce9 100644 --- a/bar.c +++ b/bar.c @@ -77,6 +77,8 @@ static monitor_t *monhead, *montail; static font_t *font_list[MAX_FONT_COUNT]; static char *font_names[MAX_FONT_COUNT]; static font_t *font_cache[FONT_CACHE_SIZE]; +static int font_count = 0; +static int font_index = -1; static uint32_t attrs = 0; static bool dock = false; static bool topbar = true; @@ -320,17 +322,27 @@ area_add (char *str, const char *optend, char **end, monitor_t *mon, const int x return true; } +bool +font_has_glyph (font_t *font, const uint16_t c) +{ + return c >= font->char_min && c <= font->char_max && + font->width_lut[c - font->char_min].character_width != 0; +} + /* returns NULL if character cannot be printed */ font_t * select_drawable_font (const uint16_t c) { + /* If the user has specified a font to use, try that first. */ + if (font_index != -1 && font_has_glyph(font_list[font_index - 1], c)) + return font_list[font_index - 1]; + /* if the end is reached without finding an apropriate font, return NULL. * If the font can draw the character, return it. */ for (int i = 0; font_list[i] != NULL; i++) { font_t *font = font_list[i]; - if (c >= font->char_min && c <= font->char_max && - font->width_lut[c - font->char_min].character_width != 0) + if (font_has_glyph(font, c)) return font; } return NULL; @@ -422,6 +434,13 @@ parse (char *text) pos_x = 0; break; + case 'T': + font_index = (int)strtoul(p, NULL, 10); + if (!font_index || font_index >= font_count) + font_index = -1; + p = end; + break; + /* In case of error keep parsing after the closing } */ default: p = end; @@ -851,12 +870,15 @@ init (void) /* Load the fonts */ for (int i = 0; font_names[i]; i++) { font_list[i] = font_load(font_names[i]); + font_count++; if (!font_list[i]) exit(EXIT_FAILURE); } - if (!font_list[0]) + if (!font_list[0]) { font_list[0] = font_load("fixed"); + font_count++; + } if (!font_list[0]) exit(EXIT_FAILURE); @@ -1141,7 +1163,7 @@ main (int argc, char **argv) switch (ev->response_type & 0x7F) { case XCB_EXPOSE: - if (expose_ev->count == 0) + if (expose_ev->count == 0) redraw = true; break; case XCB_BUTTON_PRESS: @@ -1150,8 +1172,8 @@ main (int argc, char **argv) area_t *area = area_get(press_ev->event, press_ev->event_x); /* Respond to the click */ if (area && area->button == press_ev->detail) { - write(STDOUT_FILENO, area->cmd, strlen(area->cmd)); - write(STDOUT_FILENO, "\n", 1); + write(STDOUT_FILENO, area->cmd, strlen(area->cmd)); + write(STDOUT_FILENO, "\n", 1); } } break; From 6959085f8dbe944bc86121a36376efdbc6e47017 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Sun, 25 Jan 2015 21:14:06 +0100 Subject: [PATCH 05/22] Fixed two memory leaks. Don't treat unicode sequences over two bytes long as latin1 codepoints (fixes #99). --- Makefile | 2 - bar.c | 396 +++++++++++++++++++++++++++++-------------------------- 2 files changed, 206 insertions(+), 192 deletions(-) diff --git a/Makefile b/Makefile index ff620a8..c6a9d27 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,4 @@ CC ?= gcc -STRIP ?= strip CFLAGS = -std=c99 -Os LDFLAGS = -lxcb -lxcb-xinerama -lxcb-randr CFDEBUG = -g3 -pedantic -Wall -Wunused-parameter -Wlong-long\ @@ -22,7 +21,6 @@ doc: README.pod ${EXEC}: ${OBJS} ${CC} -o ${EXEC} ${OBJS} ${LDFLAGS} - ${STRIP} -s ${EXEC} debug: ${EXEC} debug: CC += ${CFDEBUG} diff --git a/bar.c b/bar.c index cf99ce9..6c24da0 100644 --- a/bar.c +++ b/bar.c @@ -65,8 +65,6 @@ enum { }; #define MAX_FONT_COUNT 5 -/* 0 <= FONT_CACHE_SIZE <= 65536 */ -#define FONT_CACHE_SIZE 256 static xcb_connection_t *c; static xcb_screen_t *scr; @@ -75,15 +73,13 @@ static xcb_visualid_t visual; static xcb_colormap_t colormap; static monitor_t *monhead, *montail; static font_t *font_list[MAX_FONT_COUNT]; -static char *font_names[MAX_FONT_COUNT]; -static font_t *font_cache[FONT_CACHE_SIZE]; static int font_count = 0; static int font_index = -1; static uint32_t attrs = 0; static bool dock = false; static bool topbar = true; static int bw = -1, bh = -1, bx = 0, by = 0; -static int bu = 1; /* Underline height */ +static int bu = 1; // Underline height static uint32_t fgc, bgc, ugc; static uint32_t dfgc, dbgc; static area_stack_t astack; @@ -120,16 +116,16 @@ draw_char (monitor_t *mon, font_t *cur_font, int x, int align, uint16_t ch) break; } - /* Draw the background first */ + // Draw the background first fill_rect(mon->pixmap, gc[GC_CLEAR], x, by, ch_width, bh); - /* xcb accepts string in UCS-2 BE, so swap */ + // xcb accepts string in UCS-2 BE, so swap ch = (ch >> 8) | (ch << 8); - /* String baseline coordinates */ + // String baseline coordinates xcb_image_text_16(c, 1, mon->pixmap, gc[GC_DRAW], x, bh / 2 + cur_font->height / 2 - cur_font->descent, (xcb_char2b_t *)&ch); - /* We can render both at the same time */ + // We can render both at the same time if (attrs & ATTR_OVERL) fill_rect(mon->pixmap, gc[GC_ATTR], x, 0, ch_width, bu); if (attrs & ATTR_UNDERL) @@ -148,23 +144,23 @@ parse_color (const char *str, char **end, const uint32_t def) if (!str) return def; - /* Reset */ + // Reset if (str[0] == '-') { if (end) *end = (char *)str + 1; return def; } - /* Hex representation */ + // Hex representation if (str[0] == '#') { errno = 0; uint32_t tmp = strtoul(str + 1, end, 16); - /* Some error checking is definitely good */ + // Some error checking is definitely good if (errno) tmp = def; - /* Xorg uses colors with premultiplied alpha */ + // Xorg uses colors with premultiplied alpha unsigned int a = (tmp&0xff000000) >> 24; unsigned int r = (tmp&0x00ff0000) >> 16; unsigned int g = (tmp&0x0000ff00) >> 8; @@ -175,7 +171,7 @@ parse_color (const char *str, char **end, const uint32_t def) g = (g * a) / 255; b = (b * a) / 255; - /* Clamp on overflow */ + // Clamp on overflow if (r > 255) r = 255; if (g > 255) g = 255; if (b > 255) b = 255; @@ -185,7 +181,7 @@ parse_color (const char *str, char **end, const uint32_t def) return a << 24 | r << 16 | g << 8 | b; } - /* Actual color name, resolve it */ + // Actual color name, resolve it str_len = 0; while (isalpha(str[str_len])) str_len++; @@ -259,11 +255,11 @@ area_add (char *str, const char *optend, char **end, monitor_t *mon, const int x return false; } - /* A wild close area tag appeared! */ + // A wild close area tag appeared! if (*p != ':') { *end = p; - /* Basic safety checks */ + // Basic safety checks if (!a->cmd || a->align != align || a->window != mon->window) return false; @@ -278,7 +274,7 @@ area_add (char *str, const char *optend, char **end, monitor_t *mon, const int x a->end = a->begin + size; break; case ALIGN_R: - /* The newest is the rightmost one */ + // The newest is the rightmost one a->begin = mon->width - size; a->end = mon->width; break; @@ -289,11 +285,11 @@ area_add (char *str, const char *optend, char **end, monitor_t *mon, const int x return true; } - /* Found the closing : and check if it's just an escaped one */ + // Found the closing : and check if it's just an escaped one for (trail = strchr(++p, ':'); trail && trail[-1] == '\\'; trail = strchr(trail + 1, ':')) ; - /* Find the trailing : and make sure it's whitin the formatting block, also reject empty commands */ + // Find the trailing : and make sure it's whitin the formatting block, also reject empty commands if (!trail || p == trail || trail > optend) { *end = p; return false; @@ -301,7 +297,7 @@ area_add (char *str, const char *optend, char **end, monitor_t *mon, const int x *trail = '\0'; - /* Sanitize the user command by unescaping all the : */ + // Sanitize the user command by unescaping all the : for (char *needle = p; *needle; needle++) { int delta = trail - &needle[1]; if (needle[0] == '\\' && needle[1] == ':') { @@ -310,7 +306,7 @@ area_add (char *str, const char *optend, char **end, monitor_t *mon, const int x } } - /* This is a pointer to the string buffer allocated in the main */ + // This is a pointer to the string buffer allocated in the main a->cmd = p; a->align = align; a->begin = x; @@ -325,39 +321,29 @@ area_add (char *str, const char *optend, char **end, monitor_t *mon, const int x bool font_has_glyph (font_t *font, const uint16_t c) { - return c >= font->char_min && c <= font->char_max && - font->width_lut[c - font->char_min].character_width != 0; + return (c >= font->char_min && + c <= font->char_max && + font->width_lut && + font->width_lut[c - font->char_min].character_width); } -/* returns NULL if character cannot be printed */ +// returns NULL if character cannot be printed font_t * select_drawable_font (const uint16_t c) { - /* If the user has specified a font to use, try that first. */ + // If the user has specified a font to use, try that first. if (font_index != -1 && font_has_glyph(font_list[font_index - 1], c)) return font_list[font_index - 1]; - /* if the end is reached without finding an apropriate font, return NULL. - * If the font can draw the character, return it. - */ - for (int i = 0; font_list[i] != NULL; i++) { - font_t *font = font_list[i]; - if (font_has_glyph(font, c)) - return font; + // If the end is reached without finding an apropriate font, return NULL. + // If the font can draw the character, return it. + for (int i = 0; i < font_count; i++) { + if (font_has_glyph(font_list[i], c)) + return font_list[i]; } return NULL; } -/* returns NULL if character cannot be printed */ -font_t * -select_drawable_font_cache (uint16_t c) -{ - if (c < FONT_CACHE_SIZE) - return font_cache[c]; - else - return select_drawable_font(c); -} - void parse (char *text) { @@ -403,7 +389,7 @@ parse (char *text) case 'A': button = XCB_BUTTON_INDEX_1; - /* The range is 1-5 */ + // The range is 1-5 if (isdigit(*p) && (*p > '0' && *p < '6')) button = *p++ - '0'; area_add(p, end, &p, cur_mon, pos_x, align, button); @@ -441,30 +427,49 @@ parse (char *text) p = end; break; - /* In case of error keep parsing after the closing } */ + // In case of error keep parsing after the closing } default: p = end; } } - /* Eat the trailing } */ + // Eat the trailing } p++; - } else { /* utf-8 -> ucs-2 */ + } else { // utf-8 -> ucs-2 uint8_t *utf = (uint8_t *)p; uint16_t ucs; + // ASCII if (utf[0] < 0x80) { ucs = utf[0]; p += 1; } + // Two byte utf8 sequence else if ((utf[0] & 0xe0) == 0xc0) { ucs = (utf[0] & 0x1f) << 6 | (utf[1] & 0x3f); p += 2; } + // Three byte utf8 sequence else if ((utf[0] & 0xf0) == 0xe0) { ucs = (utf[0] & 0xf) << 12 | (utf[1] & 0x3f) << 6 | (utf[2] & 0x3f); p += 3; } - else { /* Handle ascii > 0x80 */ + // Four byte utf8 sequence + else if ((utf[0] & 0xf8) == 0xf0) { + ucs = 0xfffd; + p += 4; + } + // Five byte utf8 sequence + else if ((utf[0] & 0xfc) == 0xf8) { + ucs = 0xfffd; + p += 5; + } + // Siz byte utf8 sequence + else if ((utf[0] & 0xfe) == 0xfc) { + ucs = 0xfffd; + p += 6; + } + // Not a valid utf-8 sequence + else { ucs = utf[0]; p += 1; } @@ -512,7 +517,15 @@ font_load (const char *str) ret->height = font_info->font_ascent + font_info->font_descent; ret->char_max = font_info->max_byte1 << 8 | font_info->max_char_or_byte2; ret->char_min = font_info->min_byte1 << 8 | font_info->min_char_or_byte2; - ret->width_lut = xcb_query_font_char_infos(font_info); + + // Copy over the width lut as it's part of font_info + int lut_size = sizeof(xcb_charinfo_t) * xcb_query_font_char_infos_length(font_info); + if (lut_size) { + ret->width_lut = malloc(lut_size); + memcpy(ret->width_lut, xcb_query_font_char_infos(font_info), lut_size); + } + + free(font_info); return ret; } @@ -538,7 +551,7 @@ set_ewmh_atoms (void) "_NET_WM_STRUT_PARTIAL", "_NET_WM_STRUT", "_NET_WM_STATE", - /* Leave those at the end since are batch-set */ + // Leave those at the end since are batch-set "_NET_WM_STATE_STICKY", "_NET_WM_STATE_ABOVE", }; @@ -547,8 +560,8 @@ set_ewmh_atoms (void) xcb_atom_t atom_list[atoms]; xcb_intern_atom_reply_t *atom_reply; - /* As suggested fetch all the cookies first (yum!) and then retrieve the - * atoms to exploit the async'ness */ + // As suggested fetch all the cookies first (yum!) and then retrieve the + // atoms to exploit the async'ness for (int i = 0; i < atoms; i++) atom_cookie[i] = xcb_intern_atom(c, 0, strlen(atom_names[i]), atom_names[i]); @@ -560,7 +573,7 @@ set_ewmh_atoms (void) free(atom_reply); } - /* Prepare the strut array */ + // Prepare the strut array for (monitor_t *mon = monhead; mon; mon = mon->next) { int strut[12] = {0}; if (topbar) { @@ -649,14 +662,14 @@ monitor_create_chain (xcb_rectangle_t *rects, const int num) int width = 0, height = 0; int left = bx; - /* Sort before use */ + // Sort before use qsort(rects, num, sizeof(xcb_rectangle_t), rect_sort_cb); for (i = 0; i < num; i++) { int h = rects[i].y + rects[i].height; - /* Accumulated width of all monitors */ + // Accumulated width of all monitors width += rects[i].width; - /* Get height of screen from y_offset + height of lowest monitor */ + // Get height of screen from y_offset + height of lowest monitor if (h >= height) height = h; } @@ -664,16 +677,17 @@ monitor_create_chain (xcb_rectangle_t *rects, const int num) if (bw < 0) bw = width - bx; + // Use the first font height as all the font heights have been set to the biggest of the set if (bh < 0 || bh > height) bh = font_list[0]->height + bu + 2; - /* Check the geometry */ + // Check the geometry if (bx + bw > width || by + bh > height) { fprintf(stderr, "The geometry specified doesn't fit the screen!\n"); exit(EXIT_FAILURE); } - /* Left is a positive number or zero therefore monitors with zero width are excluded */ + // Left is a positive number or zero therefore monitors with zero width are excluded width = bw; for (i = 0; i < num; i++) { if (rects[i].y + rects[i].height < by) @@ -689,7 +703,7 @@ monitor_create_chain (xcb_rectangle_t *rects, const int num) width -= rects[i].width - left; - /* No need to check for other monitors */ + // No need to check for other monitors if (width <= 0) break; } @@ -720,7 +734,7 @@ get_randr_monitors (void) outputs = xcb_randr_get_screen_resources_current_outputs(rres_reply); - /* There should be at least one output */ + // There should be at least one output if (num < 1) { free(rres_reply); return; @@ -728,15 +742,16 @@ get_randr_monitors (void) xcb_rectangle_t rects[num]; - /* Get all outputs */ + // Get all outputs for (i = 0; i < num; i++) { xcb_randr_get_output_info_reply_t *oi_reply; xcb_randr_get_crtc_info_reply_t *ci_reply; oi_reply = xcb_randr_get_output_info_reply(c, xcb_randr_get_output_info(c, outputs[i], XCB_CURRENT_TIME), NULL); - /* Output disconnected or not attached to any CRTC ? */ + // Output disconnected or not attached to any CRTC ? if (!oi_reply || oi_reply->crtc == XCB_NONE || oi_reply->connection != XCB_RANDR_CONNECTION_CONNECTED) { + free(oi_reply); rects[i].width = 0; continue; } @@ -752,7 +767,7 @@ get_randr_monitors (void) return; } - /* There's no need to handle rotated screens here (see #69) */ + // There's no need to handle rotated screens here (see #69) rects[i] = (xcb_rectangle_t){ ci_reply->x, ci_reply->y, ci_reply->width, ci_reply->height }; free(ci_reply); @@ -762,13 +777,13 @@ get_randr_monitors (void) free(rres_reply); - /* Check for clones and inactive outputs */ + // Check for clones and inactive outputs for (i = 0; i < num; i++) { if (rects[i].width == 0) continue; for (j = 0; j < num; j++) { - /* Does I countain J ? */ + // Does I countain J ? if (i != j && rects[j].width) { if (rects[j].x >= rects[i].x && rects[j].x + rects[j].width <= rects[i].x + rects[i].width && @@ -809,7 +824,7 @@ get_xinerama_monitors (void) xcb_rectangle_t rects[screens]; - /* Fetch all the screens first */ + // Fetch all the screens first for (int i = 0; iter.rem; i++) { rects[i].x = iter.data->x_org; rects[i].y = iter.data->y_org; @@ -830,7 +845,7 @@ get_visual (void) iter = xcb_screen_allowed_depths_iterator(scr); - /* Try to find a RGBA visual */ + // Try to find a RGBA visual while (iter.rem) { xcb_visualtype_t *vis = xcb_depth_visuals(iter.data); @@ -840,24 +855,104 @@ get_visual (void) xcb_depth_next(&iter); } - /* Fallback to the default one */ + // Fallback to the default one return scr->root_visual; } +// Parse an X-styled geometry string, we don't support signed offsets tho. +bool +parse_geometry_string (char *str, int *tmp) +{ + char *p = str; + int i = 0, j; + + if (!str || !str[0]) + return false; + + // The leading = is optional + if (*p == '=') + p++; + + while (*p) { + // A geometry string has only 4 fields + if (i >= 4) { + fprintf(stderr, "Invalid geometry specified\n"); + return false; + } + // Move on if we encounter a 'x' or '+' + if (*p == 'x') { + if (i > 0) // The 'x' must precede '+' + break; + i++; p++; continue; + } + if (*p == '+') { + if (i < 1) // Stray '+', skip the first two fields + i = 2; + else + i++; + p++; continue; + } + // A digit must follow + if (!isdigit(*p)) { + fprintf(stderr, "Invalid geometry specified\n"); + return false; + } + // Try to parse the number + errno = 0; + j = strtoul(p, &p, 10); + if (errno) { + fprintf(stderr, "Invalid geometry specified\n"); + return false; + } + tmp[i] = j; + } + + return true; +} + +void +parse_font_list (char *str) +{ + char *tok; + + if (!str) + return; + + tok = strtok(str, ","); + + while (tok) { + if (font_count > MAX_FONT_COUNT - 1) { + fprintf(stderr, "Too many fonts; maximum %i\n", MAX_FONT_COUNT); + return; + } + + // Load the selected font + font_t *font = font_load(tok); + if (font) + font_list[font_count++] = font; + else + fprintf(stderr, "Could not load font \"%s\"...skipping\n", tok); + + tok = strtok(NULL, ","); + } +} + + + void xconn (void) { - /* Connect to X */ + // Connect to X c = xcb_connect (NULL, NULL); if (xcb_connection_has_error(c)) { fprintf(stderr, "Couldn't connect to X\n"); exit(EXIT_FAILURE); } - /* Grab infos from the first screen */ + // Grab infos from the first screen scr = xcb_setup_roots_iterator(xcb_get_setup(c)).data; - /* Try to get a RGBA visual and build the colormap for that */ + // Try to get a RGBA visual and build the colormap for that visual = get_visual(); colormap = xcb_generate_id(c); @@ -867,41 +962,34 @@ xconn (void) void init (void) { - /* Load the fonts */ - for (int i = 0; font_names[i]; i++) { - font_list[i] = font_load(font_names[i]); - font_count++; - if (!font_list[i]) - exit(EXIT_FAILURE); - } + // This has to be declared as an array because otherwise the compiler would turn it into a const + // string, making strtok choke very hard on this + char fallback_font[] = "fixed"; - if (!font_list[0]) { - font_list[0] = font_load("fixed"); - font_count++; - } + // Try to load a default font + if (!font_count) + parse_font_list(fallback_font); - if (!font_list[0]) + // We tried and failed hard, there's something wrong + if (!font_count) exit(EXIT_FAILURE); - for (uint16_t i = 0; i < FONT_CACHE_SIZE; i++) - font_cache[i] = select_drawable_font(i); - - /* To make the alignment uniform, find maximum height */ + // To make the alignment uniform, find maximum height int maxh = font_list[0]->height; - for (int i = 1; font_list[i]; i++) + for (int i = 1; i < font_count; i++) maxh = max(maxh, font_list[i]->height); - /* Set maximum height to all fonts */ - for (int i = 0; font_list[i]; i++) + // Set maximum height to all fonts + for (int i = 0; i < font_count; i++) font_list[i]->height = maxh; - /* Generate a list of screens */ + // Generate a list of screens const xcb_query_extension_reply_t *qe_reply; - /* Initialiaze monitor list head and tail */ + // Initialiaze monitor list head and tail monhead = montail = NULL; - /* Check if RandR is present */ + // Check if RandR is present qe_reply = xcb_get_extension_data(c, &xcb_randr_id); if (qe_reply && qe_reply->present) { @@ -909,7 +997,7 @@ init (void) } else { qe_reply = xcb_get_extension_data(c, &xcb_xinerama_id); - /* Check if Xinerama extension is present and active */ + // Check if Xinerama extension is present and active if (qe_reply && qe_reply->present) { xcb_xinerama_is_active_reply_t *xia_reply; xia_reply = xcb_xinerama_is_active_reply(c, xcb_xinerama_is_active(c), NULL); @@ -922,31 +1010,31 @@ init (void) } if (!monhead) { - /* If I fits I sits */ + // If I fits I sits if (bw < 0) bw = scr->width_in_pixels - bx; - /* Adjust the height */ + // Adjust the height if (bh < 0 || bh > scr->height_in_pixels) bh = maxh + bu + 2; - /* Check the geometry */ + // Check the geometry if (bx + bw > scr->width_in_pixels || by + bh > scr->height_in_pixels) { fprintf(stderr, "The geometry specified doesn't fit the screen!\n"); exit(EXIT_FAILURE); } - /* If no RandR outputs or Xinerama screens, fall back to using whole screen */ + // If no RandR outputs or Xinerama screens, fall back to using whole screen monhead = monitor_new(0, 0, bw, scr->height_in_pixels); } if (!monhead) exit(EXIT_FAILURE); - /* For WM that support EWMH atoms */ + // For WM that support EWMH atoms set_ewmh_atoms(); - /* Create the gc for drawing */ + // Create the gc for drawing gc[GC_DRAW] = xcb_generate_id(c); xcb_create_gc(c, gc[GC_DRAW], monhead->pixmap, XCB_GC_FOREGROUND | XCB_GC_BACKGROUND, (const uint32_t []){ fgc, bgc }); @@ -956,7 +1044,7 @@ init (void) gc[GC_ATTR] = xcb_generate_id(c); xcb_create_gc(c, gc[GC_ATTR], monhead->pixmap, XCB_GC_FOREGROUND, (const uint32_t []){ ugc }); - /* Make the bar visible and clear the pixmap */ + // Make the bar visible and clear the pixmap for (monitor_t *mon = monhead; mon; mon = mon->next) { fill_rect(mon->pixmap, gc[GC_CLEAR], 0, 0, mon->width, bh); xcb_map_window(c, mon->window); @@ -968,10 +1056,10 @@ init (void) void cleanup (void) { - for (int i = 0; font_list[i]; i++) { + for (int i = 0; i < font_count; i++) { xcb_close_font(c, font_list[i]->ptr); + free(font_list[i]->width_lut); free(font_list[i]); - font_list[i] = NULL; } while (monhead) { @@ -1001,78 +1089,6 @@ sighandle (int signal) exit(EXIT_SUCCESS); } -/* Parse an X-styled geometry string, we don't support signed offsets tho. */ -bool -parse_geometry_string (char *str, int *tmp) -{ - char *p = str; - int i = 0, j; - - if (!str || !str[0]) - return false; - - /* The leading = is optional */ - if (*p == '=') - p++; - - while (*p) { - /* A geometry string has only 4 fields */ - if (i >= 4) { - fprintf(stderr, "Invalid geometry specified\n"); - return false; - } - /* Move on if we encounter a 'x' or '+' */ - if (*p == 'x') { - if (i > 0) /* The 'x' must precede '+' */ - break; - i++; p++; continue; - } - if (*p == '+') { - if (i < 1) /* Stray '+', skip the first two fields */ - i = 2; - else - i++; - p++; continue; - } - /* A digit must follow */ - if (!isdigit(*p)) { - fprintf(stderr, "Invalid geometry specified\n"); - return false; - } - /* Try to parse the number */ - errno = 0; - j = strtoul(p, &p, 10); - if (errno) { - fprintf(stderr, "Invalid geometry specified\n"); - return false; - } - tmp[i] = j; - } - - return true; -} - -void -parse_font_list (char *str) -{ - char *tok; - - if (!str) - return; - - tok = strtok(str, ","); - - for (int i = 0; tok; i++) { - if (i >= MAX_FONT_COUNT) { - fprintf(stderr, "Too many fonts; maximum %i\n", MAX_FONT_COUNT); - return; - } - - font_names[i] = tok; - tok = strtok(NULL, ","); - } -} - int main (int argc, char **argv) { @@ -1087,15 +1103,15 @@ main (int argc, char **argv) bool permanent = false; int geom_v[4] = { -1, -1, 0, 0 }; - /* Install the parachute! */ + // Install the parachute! atexit(cleanup); signal(SIGINT, sighandle); signal(SIGTERM, sighandle); - /* Connect to the Xserver and initialize scr */ + // Connect to the Xserver and initialize scr xconn(); - /* B/W combo */ + // B/W combo dbgc = bgc = parse_color("black", NULL, scr->black_pixel); dfgc = fgc = parse_color("white", NULL, scr->white_pixel); @@ -1127,37 +1143,37 @@ main (int argc, char **argv) } } - /* Copy the geometry values in place */ + // Copy the geometry values in place bw = geom_v[0]; bh = geom_v[1]; bx = geom_v[2]; by = geom_v[3]; - /* Do the heavy lifting */ + // Do the heavy lifting init(); - /* Get the fd to Xserver */ + // Get the fd to Xserver pollin[1].fd = xcb_get_file_descriptor(c); for (;;) { bool redraw = false; - /* If connection is in error state, then it has been shut down. */ + // If connection is in error state, then it has been shut down. if (xcb_connection_has_error(c)) break; if (poll(pollin, 2, -1) > 0) { - if (pollin[0].revents & POLLHUP) { /* No more data... */ - if (permanent) pollin[0].fd = -1; /* ...null the fd and continue polling :D */ - else break; /* ...bail out */ + if (pollin[0].revents & POLLHUP) { // No more data... + if (permanent) pollin[0].fd = -1; // ...null the fd and continue polling :D + else break; // ...bail out } - if (pollin[0].revents & POLLIN) { /* New input, process it */ + if (pollin[0].revents & POLLIN) { // New input, process it if (fgets(input, sizeof(input), stdin) == NULL) - break; /* EOF received */ + break; // EOF received parse(input); redraw = true; } - if (pollin[1].revents & POLLIN) { /* Xserver broadcasted an event */ + if (pollin[1].revents & POLLIN) { // Xserver broadcasted an event while ((ev = xcb_poll_for_event(c))) { expose_ev = (xcb_expose_event_t *)ev; @@ -1170,7 +1186,7 @@ main (int argc, char **argv) press_ev = (xcb_button_press_event_t *)ev; { area_t *area = area_get(press_ev->event, press_ev->event_x); - /* Respond to the click */ + // Respond to the click if (area && area->button == press_ev->detail) { write(STDOUT_FILENO, area->cmd, strlen(area->cmd)); write(STDOUT_FILENO, "\n", 1); @@ -1184,7 +1200,7 @@ main (int argc, char **argv) } } - if (redraw) { /* Copy our temporary pixmap onto the window */ + if (redraw) { // Copy our temporary pixmap onto the window for (monitor_t *mon = monhead; mon; mon = mon->next) { xcb_copy_area(c, mon->pixmap, mon->window, gc[GC_DRAW], 0, 0, 0, 0, mon->width, bh); } From e197a15c3a90a9d94b2f34338fb7acacdb0ad6b0 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Tue, 10 Feb 2015 20:35:11 +0100 Subject: [PATCH 06/22] Fix a silly bound-checking error. (#101) Close a long-standing PR by configuring the window position after mapping it Set the window title --- bar.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/bar.c b/bar.c index 6c24da0..90c2229 100644 --- a/bar.c +++ b/bar.c @@ -27,7 +27,7 @@ typedef struct font_t { } font_t; typedef struct monitor_t { - int x, width; + int x, y, width; xcb_window_t window; xcb_pixmap_t pixmap; struct monitor_t *prev, *next; @@ -422,7 +422,9 @@ parse (char *text) case 'T': font_index = (int)strtoul(p, NULL, 10); - if (!font_index || font_index >= font_count) + // User-specified 'font_index' ∊ (0,font_count] + // Otherwise just fallback to the automatic font selection + if (!font_index || font_index > font_count) font_index = -1; p = end; break; @@ -591,6 +593,7 @@ set_ewmh_atoms (void) xcb_change_property(c, XCB_PROP_MODE_REPLACE, mon->window, atom_list[NET_WM_DESKTOP], XCB_ATOM_CARDINAL, 32, 1, (const uint32_t []){ -1 } ); xcb_change_property(c, XCB_PROP_MODE_REPLACE, mon->window, atom_list[NET_WM_STRUT_PARTIAL], XCB_ATOM_CARDINAL, 32, 12, strut); xcb_change_property(c, XCB_PROP_MODE_REPLACE, mon->window, atom_list[NET_WM_STRUT], XCB_ATOM_CARDINAL, 32, 4, strut); + xcb_change_property(c, XCB_PROP_MODE_REPLACE, mon->window, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8, 3, "bar"); } } @@ -606,15 +609,14 @@ monitor_new (int x, int y, int width, int height) } ret->x = x; + ret->y = (topbar ? by : height - bh - by) + y; ret->width = width; ret->next = ret->prev = NULL; - - int win_y = (topbar ? by : height - bh - by) + y; ret->window = xcb_generate_id(c); int depth = (visual == scr->root_visual) ? XCB_COPY_FROM_PARENT : 32; xcb_create_window(c, depth, ret->window, scr->root, - x, win_y, width, bh, 0, + ret->x, ret->y, width, bh, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, visual, XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL | XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK | XCB_CW_COLORMAP, (const uint32_t []){ bgc, bgc, dock, XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_PRESS, colormap }); @@ -800,11 +802,11 @@ get_randr_monitors (void) return; } - xcb_rectangle_t r[valid]; + xcb_rectangle_t r[valid]; - for (i = j = 0; i < num && j < valid; i++) - if (rects[i].width != 0) - r[j++] = rects[i]; + for (i = j = 0; i < num && j < valid; i++) + if (rects[i].width != 0) + r[j++] = rects[i]; monitor_create_chain(r, valid); } @@ -1048,6 +1050,10 @@ init (void) for (monitor_t *mon = monhead; mon; mon = mon->next) { fill_rect(mon->pixmap, gc[GC_CLEAR], 0, 0, mon->width, bh); xcb_map_window(c, mon->window); + + // Make sure that the window really gets in the place it's supposed to be + // Some WM such as Openbox need this + xcb_configure_window(c, mon->window, XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, (const uint32_t []){ mon->x, mon->y }); } xcb_flush(c); From 7d2c7ab438afa5cde93969a1ade4fc8d39d2f1e1 Mon Sep 17 00:00:00 2001 From: Otto Modinos Date: Fri, 16 Jan 2015 01:37:45 +0200 Subject: [PATCH 07/22] Allow having clickable areas inside another Previously, if you started several areas, one inside another, only the inermost one would get registered and eventually triggered. This patch fixes that, allowing you to use multiple areas around text to respond to several different buttons, for example, both scroll-up and scroll-down. If you define two areas that respond to the same button, only the innermost one would get triggered. I think this makes sense. --- bar.c | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/bar.c b/bar.c index 90c2229..a4492e9 100644 --- a/bar.c +++ b/bar.c @@ -34,6 +34,7 @@ typedef struct monitor_t { } monitor_t; typedef struct area_t { + bool active; int begin, end, align, button; xcb_window_t window; char *cmd; @@ -219,11 +220,15 @@ set_attribute (const char modifier, const char attribute) area_t * -area_get (xcb_window_t win, const int x) +area_get (xcb_window_t win, const int btn, const int x) { - for (int i = 0; i < astack.pos; i++) - if (astack.slot[i].window == win && x >= astack.slot[i].begin && x < astack.slot[i].end) - return &astack.slot[i]; + /* Looping backwards ensures that we get the innermost area first */ + for (int i = astack.pos; i >= 0; i--) { + area_t *a = &astack.slot[i]; + if (a->window == win && a->button == btn + && x >= a->begin && x < a->end) + return a; + } return NULL; } @@ -248,17 +253,18 @@ area_add (char *str, const char *optend, char **end, monitor_t *mon, const int x { char *p = str; char *trail; - area_t *a = &astack.slot[astack.pos]; - - if (astack.pos == N) { - fprintf(stderr, "astack overflow!\n"); - return false; - } + area_t *a; // A wild close area tag appeared! if (*p != ':') { *end = p; + /* Find most recent unclosed area. */ + int i; + for (i = astack.pos - 1; i >= 0 && !astack.slot[i].active; i--) + ; + a = &astack.slot[i]; + // Basic safety checks if (!a->cmd || a->align != align || a->window != mon->window) return false; @@ -280,11 +286,16 @@ area_add (char *str, const char *optend, char **end, monitor_t *mon, const int x break; } - astack.pos++; - + a->active = false; return true; } + if (astack.pos >= N) { + fprintf(stderr, "astack overflow!\n"); + return false; + } + a = &astack.slot[astack.pos++]; + // Found the closing : and check if it's just an escaped one for (trail = strchr(++p, ':'); trail && trail[-1] == '\\'; trail = strchr(trail + 1, ':')) ; @@ -308,6 +319,7 @@ area_add (char *str, const char *optend, char **end, monitor_t *mon, const int x // This is a pointer to the string buffer allocated in the main a->cmd = p; + a->active = true; a->align = align; a->begin = x; a->window = mon->window; @@ -1191,7 +1203,7 @@ main (int argc, char **argv) case XCB_BUTTON_PRESS: press_ev = (xcb_button_press_event_t *)ev; { - area_t *area = area_get(press_ev->event, press_ev->event_x); + area_t *area = area_get(press_ev->event, press_ev->detail, press_ev->event_x); // Respond to the click if (area && area->button == press_ev->detail) { write(STDOUT_FILENO, area->cmd, strlen(area->cmd)); From 3483f1b083e33d17cffb8842c12f9b7755eb3ddf Mon Sep 17 00:00:00 2001 From: Otto Modinos Date: Fri, 16 Jan 2015 01:40:04 +0200 Subject: [PATCH 08/22] typo, whitin -> within --- bar.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bar.c b/bar.c index a4492e9..a6441fc 100644 --- a/bar.c +++ b/bar.c @@ -300,7 +300,7 @@ area_add (char *str, const char *optend, char **end, monitor_t *mon, const int x for (trail = strchr(++p, ':'); trail && trail[-1] == '\\'; trail = strchr(trail + 1, ':')) ; - // Find the trailing : and make sure it's whitin the formatting block, also reject empty commands + // Find the trailing : and make sure it's within the formatting block, also reject empty commands if (!trail || p == trail || trail > optend) { *end = p; return false; From bb466a8c16b4110e5afc07849bfb56a45f7d611e Mon Sep 17 00:00:00 2001 From: Otto Modinos Date: Fri, 16 Jan 2015 01:41:56 +0200 Subject: [PATCH 09/22] area_add: Use str directly I don't why p was used, if there was a reason, please don't merge. --- bar.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/bar.c b/bar.c index a6441fc..5b6f18f 100644 --- a/bar.c +++ b/bar.c @@ -251,13 +251,12 @@ area_shift (xcb_window_t win, const int align, int delta) bool area_add (char *str, const char *optend, char **end, monitor_t *mon, const int x, const int align, const int button) { - char *p = str; char *trail; area_t *a; // A wild close area tag appeared! - if (*p != ':') { - *end = p; + if (*str != ':') { + *end = str; /* Find most recent unclosed area. */ int i; @@ -297,19 +296,19 @@ area_add (char *str, const char *optend, char **end, monitor_t *mon, const int x a = &astack.slot[astack.pos++]; // Found the closing : and check if it's just an escaped one - for (trail = strchr(++p, ':'); trail && trail[-1] == '\\'; trail = strchr(trail + 1, ':')) + for (trail = strchr(++str, ':'); trail && trail[-1] == '\\'; trail = strchr(trail + 1, ':')) ; // Find the trailing : and make sure it's within the formatting block, also reject empty commands - if (!trail || p == trail || trail > optend) { - *end = p; + if (!trail || str == trail || trail > optend) { + *end = str; return false; } *trail = '\0'; // Sanitize the user command by unescaping all the : - for (char *needle = p; *needle; needle++) { + for (char *needle = str; *needle; needle++) { int delta = trail - &needle[1]; if (needle[0] == '\\' && needle[1] == ':') { memmove(&needle[0], &needle[1], delta); @@ -318,7 +317,7 @@ area_add (char *str, const char *optend, char **end, monitor_t *mon, const int x } // This is a pointer to the string buffer allocated in the main - a->cmd = p; + a->cmd = str; a->active = true; a->align = align; a->begin = x; From 4ad9cbd823e229b8af262ccf707889ce8b97eb03 Mon Sep 17 00:00:00 2001 From: Otto Modinos Date: Fri, 16 Jan 2015 23:15:00 +0200 Subject: [PATCH 10/22] remove duplicate test --- bar.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bar.c b/bar.c index 5b6f18f..3da875d 100644 --- a/bar.c +++ b/bar.c @@ -1204,7 +1204,7 @@ main (int argc, char **argv) { area_t *area = area_get(press_ev->event, press_ev->detail, press_ev->event_x); // Respond to the click - if (area && area->button == press_ev->detail) { + if (area) { write(STDOUT_FILENO, area->cmd, strlen(area->cmd)); write(STDOUT_FILENO, "\n", 1); } From a313800686f3c6a24feb9646f646af6bfcc0d27d Mon Sep 17 00:00:00 2001 From: Otto Modinos Date: Fri, 16 Jan 2015 23:20:14 +0200 Subject: [PATCH 11/22] move a declaration to the top of the function --- bar.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bar.c b/bar.c index 3da875d..ccc18bd 100644 --- a/bar.c +++ b/bar.c @@ -251,6 +251,7 @@ area_shift (xcb_window_t win, const int align, int delta) bool area_add (char *str, const char *optend, char **end, monitor_t *mon, const int x, const int align, const int button) { + int i; char *trail; area_t *a; @@ -259,7 +260,6 @@ area_add (char *str, const char *optend, char **end, monitor_t *mon, const int x *end = str; /* Find most recent unclosed area. */ - int i; for (i = astack.pos - 1; i >= 0 && !astack.slot[i].active; i--) ; a = &astack.slot[i]; From 8492309a883ae8bc9cd667fa91b7682d9a47d1e8 Mon Sep 17 00:00:00 2001 From: Otto Modinos Date: Wed, 11 Feb 2015 14:54:47 +0200 Subject: [PATCH 12/22] change comment style --- bar.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bar.c b/bar.c index ccc18bd..3733910 100644 --- a/bar.c +++ b/bar.c @@ -222,7 +222,7 @@ set_attribute (const char modifier, const char attribute) area_t * area_get (xcb_window_t win, const int btn, const int x) { - /* Looping backwards ensures that we get the innermost area first */ + // Looping backwards ensures that we get the innermost area first for (int i = astack.pos; i >= 0; i--) { area_t *a = &astack.slot[i]; if (a->window == win && a->button == btn @@ -259,7 +259,7 @@ area_add (char *str, const char *optend, char **end, monitor_t *mon, const int x if (*str != ':') { *end = str; - /* Find most recent unclosed area. */ + // Find most recent unclosed area. for (i = astack.pos - 1; i >= 0 && !astack.slot[i].active; i--) ; a = &astack.slot[i]; From 927c05604c62d7d08c45601a90db90c1c4978cb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Marques?= Date: Wed, 11 Feb 2015 20:03:31 +0000 Subject: [PATCH 13/22] Fix typo in README.pod about the scroll up/down buttons --- README.pod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.pod b/README.pod index b610ab8..fb1c482 100644 --- a/README.pod +++ b/README.pod @@ -98,7 +98,7 @@ Create a clickable area starting from the current position, when the area is cli Eg. I<%{A:reboot:} Click here to reboot %{A}> -The I