From 38422c5014e63afd3981b3518b5e09b96aab1448 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Sat, 24 Oct 2015 14:04:37 +0200 Subject: [PATCH 1/6] Don't parse named colors. Expand #rgb format. --- README.pod | 2 +- lemonbar.c | 98 ++++++++++++++++++++++++++---------------------------- 2 files changed, 49 insertions(+), 51 deletions(-) diff --git a/README.pod b/README.pod index 44f09f0..a38a7df 100644 --- a/README.pod +++ b/README.pod @@ -47,7 +47,7 @@ Sets the underline width in pixels. The default is 1. =item B<-B> I -Set the background color of the bar. I might be either in hex format (#aarrggbb) or in the symbolic name format (eg. white, indianred, darkgray). If no compositor such as compton or xcompmgr is running the alpha channel is silently ignored. +Set the background color of the bar. I must be specified in the hex format (#aarrggbb, #rrggbb, #rgb). If no compositor such as compton or xcompmgr is running the alpha channel is silently ignored. =item B<-F> I diff --git a/lemonbar.c b/lemonbar.c index 39c4975..5764537 100644 --- a/lemonbar.c +++ b/lemonbar.c @@ -233,9 +233,7 @@ draw_char (monitor_t *mon, font_t *cur_font, int x, int align, uint16_t ch) rgba_t parse_color (const char *str, char **end, const rgba_t def) { - xcb_alloc_named_color_reply_t *nc_reply; int string_len; - rgba_t ret; char *ep; if (!str) @@ -250,61 +248,61 @@ parse_color (const char *str, char **end, const rgba_t def) } // Hex representation - if (str[0] == '#') { - errno = 0; - rgba_t tmp = (rgba_t)(uint32_t)strtoul(str + 1, &ep, 16); - + if (str[0] != '#') { if (end) - *end = ep; + *end = (char *)str; - // Some error checking is definitely good - if (errno) { - fprintf(stderr, "Invalid color specified\n"); - return def; - } - - string_len = ep - (str + 1); - - // If the code is in #rrggbb form then assume it's opaque - if (string_len <= 6) - tmp.a = 255; - - // Premultiply the alpha in - if (tmp.a) { - // The components are clamped automagically as the rgba_t is made of uint8_t - return (rgba_t){ - .r = (tmp.r * tmp.a) / 255, - .g = (tmp.g * tmp.a) / 255, - .b = (tmp.b * tmp.a) / 255, - .a = tmp.a, - }; - } - - return (rgba_t)0U; + fprintf(stderr, "Invalid color specified\n"); + return def; } - // Actual color name, resolve it - for (string_len = 0; isalpha(str[string_len]); string_len++) - ; - - nc_reply = xcb_alloc_named_color_reply(c, xcb_alloc_named_color(c, colormap, string_len, str), NULL); - - if (!nc_reply) - fprintf(stderr, "Could not allocate the color \"%.*s\"\n", string_len, str); - - ret = nc_reply? - (rgba_t)nc_reply->pixel: - def; - - free(nc_reply); + errno = 0; + rgba_t tmp = (rgba_t)(uint32_t)strtoul(str + 1, &ep, 16); if (end) - *end = (char *)str + string_len; + *end = ep; - return ret; + // Some error checking is definitely good + if (errno) { + fprintf(stderr, "Invalid color specified\n"); + return def; + } + + string_len = ep - (str + 1); + + switch (string_len) { + case 3: + // Expand the #rgb format into #rrggbb (aa is set to 0xff) + tmp.v = (tmp.v & 0xf00) * 0x1100 + | (tmp.v & 0x0f0) * 0x0110 + | (tmp.v & 0x00f) * 0x0011; + case 6: + // If the code is in #rrggbb form then assume it's opaque + tmp.a = 255; + break; + case 7: + case 8: + // Colors in #aarrggbb format, those need no adjustments + break; + default: + fprintf(stderr, "Invalid color specified\n"); + return def; + } + + // Premultiply the alpha in + if (tmp.a) { + // The components are clamped automagically as the rgba_t is made of uint8_t + return (rgba_t){ + .r = (tmp.r * tmp.a) / 255, + .g = (tmp.g * tmp.a) / 255, + .b = (tmp.b * tmp.a) / 255, + .a = tmp.a, + }; + } + + return (rgba_t)0U; } - void set_attribute (const char modifier, const char attribute) { @@ -1229,8 +1227,8 @@ main (int argc, char **argv) xconn(); // B/W combo - dbgc = bgc = parse_color("black", NULL, (rgba_t)scr->black_pixel); - dfgc = fgc = parse_color("white", NULL, (rgba_t)scr->white_pixel); + dbgc = bgc = (rgba_t)scr->black_pixel; + dfgc = fgc = (rgba_t)scr->white_pixel; ugc = fgc; From 255c93dc7a58d8c7bbc0c7c32d46c0fa9e6cb723 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Sat, 24 Oct 2015 14:28:51 +0200 Subject: [PATCH 2/6] Make the number of clickable areas configurable. --- lemonbar.c | 80 ++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 53 insertions(+), 27 deletions(-) diff --git a/lemonbar.c b/lemonbar.c index 5764537..ef3b3cf 100644 --- a/lemonbar.c +++ b/lemonbar.c @@ -29,15 +29,18 @@ typedef struct font_t { } font_t; typedef struct monitor_t { - int x, y, width; + int x:16, y:16, width:16; xcb_window_t window; xcb_pixmap_t pixmap; struct monitor_t *prev, *next; } monitor_t; typedef struct area_t { - bool active; - int begin, end, align, button; + unsigned int begin:16; + unsigned int end:16; + bool active:1; + int align:3; + int button:3; xcb_window_t window; char *cmd; } area_t; @@ -52,11 +55,9 @@ typedef union rgba_t { uint32_t v; } rgba_t; -#define N 20 - typedef struct area_stack_t { - int pos; - area_t slot[N]; + int at, max; + area_t *area; } area_stack_t; enum { @@ -95,7 +96,7 @@ static int bw = -1, bh = -1, bx = 0, by = 0; static int bu = 1; // Underline height static rgba_t fgc, bgc, ugc; static rgba_t dfgc, dbgc; -static area_stack_t astack; +static area_stack_t area_stack; void update_gc (void) @@ -325,8 +326,8 @@ area_t * area_get (xcb_window_t win, const int btn, const int x) { // Looping backwards ensures that we get the innermost area first - for (int i = astack.pos; i >= 0; i--) { - area_t *a = &astack.slot[i]; + for (int i = area_stack.at; i >= 0; i--) { + area_t *a = &area_stack.area[i]; if (a->window == win && a->button == btn && x >= a->begin && x < a->end) return a; @@ -342,8 +343,8 @@ area_shift (xcb_window_t win, const int align, int delta) if (align == ALIGN_C) delta /= 2; - for (int i = 0; i < astack.pos; i++) { - area_t *a = &astack.slot[i]; + for (int i = 0; i < area_stack.at; i++) { + area_t *a = &area_stack.area[i]; if (a->window == win && a->align == align && !a->active) { a->begin -= delta; a->end -= delta; @@ -363,9 +364,9 @@ area_add (char *str, const char *optend, char **end, monitor_t *mon, const int x *end = str; // Find most recent unclosed area. - for (i = astack.pos - 1; i >= 0 && !astack.slot[i].active; i--) + for (i = area_stack.at - 1; i >= 0 && !area_stack.area[i].active; i--) ; - a = &astack.slot[i]; + a = &area_stack.area[i]; // Basic safety checks if (!a->cmd || a->align != align || a->window != mon->window) @@ -392,11 +393,12 @@ area_add (char *str, const char *optend, char **end, monitor_t *mon, const int x return true; } - if (astack.pos >= N) { - fprintf(stderr, "astack overflow!\n"); + if (area_stack.at + 1 > area_stack.max) { + fprintf(stderr, "Cannot add any more clickable areas (used %d/%d)\n", + area_stack.at, area_stack.max); return false; } - a = &astack.slot[astack.pos++]; + a = &area_stack.area[area_stack.at++]; // Found the closing : and check if it's just an escaped one for (trail = strchr(++str, ':'); trail && trail[-1] == '\\'; trail = strchr(trail + 1, ':')) @@ -474,7 +476,8 @@ parse (char *text) align = ALIGN_L; cur_mon = monhead; - memset(&astack, 0, sizeof(area_stack_t)); + // Reset the stack position + area_stack.at = 0; for (monitor_t *m = monhead; m != NULL; m = m->next) fill_rect(m->pixmap, gc[GC_CLEAR], 0, 0, m->width, bh); @@ -510,7 +513,8 @@ parse (char *text) // The range is 1-5 if (isdigit(*p) && (*p > '0' && *p < '6')) button = *p++ - '0'; - area_add(p, block_end, &p, cur_mon, pos_x, align, button); + if (!area_add(p, block_end, &p, cur_mon, pos_x, align, button)) + return; break; case 'B': bgc = parse_color(p, &p, dbgc); update_gc(); break; @@ -1170,6 +1174,8 @@ init (void) void cleanup (void) { + free(area_stack.area); + for (int i = 0; i < font_count; i++) { xcb_close_font(c, font_list[i]->ptr); free(font_list[i]->width_lut); @@ -1216,22 +1222,22 @@ main (int argc, char **argv) char input[4096] = {0, }; bool permanent = false; int geom_v[4] = { -1, -1, 0, 0 }; - int ch; + int ch, areas; // Install the parachute! atexit(cleanup); signal(SIGINT, sighandle); signal(SIGTERM, sighandle); - // Connect to the Xserver and initialize scr - xconn(); - // B/W combo - dbgc = bgc = (rgba_t)scr->black_pixel; - dfgc = fgc = (rgba_t)scr->white_pixel; + dbgc = bgc = (rgba_t)0x00000000U; + dfgc = fgc = (rgba_t)0xffffffffU; ugc = fgc; + // A safe default + areas = 10; + while ((ch = getopt(argc, argv, "hg:bdf:a:pu:B:F:")) != -1) { switch (ch) { case 'h': @@ -1242,6 +1248,7 @@ main (int argc, char **argv) "\t-b Put the bar at the bottom of the screen\n" "\t-d Force docking (use this if your WM isn't EWMH compliant)\n" "\t-f Set the font name to use\n" + "\t-a Number of clickable areas available (default is 10)\n" "\t-p Don't close after the data ends\n" "\t-u Set the underline/overline height in pixels\n" "\t-B Set background color in #AARRGGBB\n" @@ -1253,17 +1260,36 @@ main (int argc, char **argv) case 'd': dock = true; break; case 'f': font_load(optarg); break; case 'u': bu = strtoul(optarg, NULL, 10); break; - case 'B': dbgc = bgc = parse_color(optarg, NULL, (rgba_t)scr->black_pixel); break; - case 'F': dfgc = fgc = parse_color(optarg, NULL, (rgba_t)scr->white_pixel); break; + case 'B': dbgc = bgc = parse_color(optarg, NULL, (rgba_t)0x00000000U); break; + case 'F': dfgc = fgc = parse_color(optarg, NULL, (rgba_t)0xffffffffU); break; + case 'a': areas = strtoul(optarg, NULL, 10); break; } } + // Initialize the stack holding the clickable areas + area_stack.at = 0; + area_stack.max = areas; + if (areas) { + area_stack.area = calloc(areas, sizeof(area_t)); + + if (!area_stack.area) { + fprintf(stderr, "Could not allocate enough memory for %d clickable areas, try lowering the number\n", areas); + return EXIT_FAILURE; + } + } + else + area_stack.area = NULL; + + // Copy the geometry values in place bw = geom_v[0]; bh = geom_v[1]; bx = geom_v[2]; by = geom_v[3]; + // Connect to the Xserver and initialize scr + xconn(); + // Do the heavy lifting init(); // Get the fd to Xserver From 6580e2d4f760fc3b69a03aa3179ce80b2b3d9d55 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Sat, 24 Oct 2015 14:35:36 +0200 Subject: [PATCH 3/6] Warn the user when the area geometry is invalid --- lemonbar.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lemonbar.c b/lemonbar.c index ef3b3cf..73d3ff1 100644 --- a/lemonbar.c +++ b/lemonbar.c @@ -29,7 +29,7 @@ typedef struct font_t { } font_t; typedef struct monitor_t { - int x:16, y:16, width:16; + int x, y, width; xcb_window_t window; xcb_pixmap_t pixmap; struct monitor_t *prev, *next; @@ -369,8 +369,10 @@ area_add (char *str, const char *optend, char **end, monitor_t *mon, const int x a = &area_stack.area[i]; // Basic safety checks - if (!a->cmd || a->align != align || a->window != mon->window) + if (!a->cmd || a->align != align || a->window != mon->window) { + fprintf(stderr, "Invalid geometry for the clickable area\n"); return false; + } const int size = x - a->begin; From 531c575575e04fd7d6bd34b8347f420497e017a4 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Sat, 24 Oct 2015 14:44:41 +0200 Subject: [PATCH 4/6] Allow the user to set the WM_NAME atom value --- README.pod | 6 +++++- lemonbar.c | 16 ++++++++++++---- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/README.pod b/README.pod index a38a7df..ea7eb7f 100644 --- a/README.pod +++ b/README.pod @@ -6,7 +6,7 @@ lemonbar - Featherweight lemon-scented bar =head1 SYNOPSIS -I [-h | -g IBIB<+>IB<+>I | -b | -d | -f I | -p | -u I | -B I | -F I] +I [-h | -g IBIB<+>IB<+>I | -b | -d | -f I | -p | -n | -u I | -B I | -F I] =head1 DESCRIPTION @@ -41,6 +41,10 @@ changing the MAX_FONT_COUNT parameter in the source code). Make the bar permanent, don't exit after the standard input is closed. +=item B<-n> + +Set the WM_NAME atom value for the bar. + =item B<-u> I Sets the underline width in pixels. The default is 1. diff --git a/lemonbar.c b/lemonbar.c index 73d3ff1..6582160 100644 --- a/lemonbar.c +++ b/lemonbar.c @@ -1080,7 +1080,7 @@ xconn (void) } void -init (void) +init (char *wm_name) { // Try to load a default font if (!font_count) @@ -1168,6 +1168,10 @@ init (void) // 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 }); + + // Set the WM_NAME atom to the user specified value + if (wm_name) + xcb_change_property(c, XCB_PROP_MODE_REPLACE, monhead->window, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8 ,strlen(wm_name), wm_name); } xcb_flush(c); @@ -1225,6 +1229,7 @@ main (int argc, char **argv) bool permanent = false; int geom_v[4] = { -1, -1, 0, 0 }; int ch, areas; + char *wm_name; // Install the parachute! atexit(cleanup); @@ -1239,12 +1244,13 @@ main (int argc, char **argv) // A safe default areas = 10; + wm_name = NULL; - while ((ch = getopt(argc, argv, "hg:bdf:a:pu:B:F:")) != -1) { + while ((ch = getopt(argc, argv, "hg:bdf:a:pu:B:F:n:")) != -1) { switch (ch) { case 'h': printf ("lemonbar version %s\n", VERSION); - printf ("usage: %s [-h | -g | -b | -d | -f | -a | -p | -u | -B | -F]\n" + printf ("usage: %s [-h | -g | -b | -d | -f | -a | -p | -n | -u | -B | -F]\n" "\t-h Show this help\n" "\t-g Set the bar geometry {width}x{height}+{xoffset}+{yoffset}\n" "\t-b Put the bar at the bottom of the screen\n" @@ -1252,12 +1258,14 @@ main (int argc, char **argv) "\t-f Set the font name to use\n" "\t-a Number of clickable areas available (default is 10)\n" "\t-p Don't close after the data ends\n" + "\t-n Set the WM_NAME atom to the specified value for this bar\n" "\t-u Set the underline/overline height in pixels\n" "\t-B Set background color in #AARRGGBB\n" "\t-F Set foreground color in #AARRGGBB\n", argv[0]); exit (EXIT_SUCCESS); case 'g': (void)parse_geometry_string(optarg, geom_v); break; case 'p': permanent = true; break; + case 'n': wm_name = optarg; break; case 'b': topbar = false; break; case 'd': dock = true; break; case 'f': font_load(optarg); break; @@ -1293,7 +1301,7 @@ main (int argc, char **argv) xconn(); // Do the heavy lifting - init(); + init(wm_name); // Get the fd to Xserver pollin[1].fd = xcb_get_file_descriptor(c); From 3b5baa93b172a28179c4de0557e438daf0f6619a Mon Sep 17 00:00:00 2001 From: Bastien Dejean Date: Wed, 28 Oct 2015 11:48:15 +0100 Subject: [PATCH 5/6] The WM_NAME option takes an argument --- README.pod | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.pod b/README.pod index ea7eb7f..d0adc1b 100644 --- a/README.pod +++ b/README.pod @@ -6,7 +6,7 @@ lemonbar - Featherweight lemon-scented bar =head1 SYNOPSIS -I [-h | -g IBIB<+>IB<+>I | -b | -d | -f I | -p | -n | -u I | -B I | -F I] +I [-h | -g IBIB<+>IB<+>I | -b | -d | -f I | -p | -n I | -u I | -B I | -F I] =head1 DESCRIPTION @@ -41,7 +41,7 @@ changing the MAX_FONT_COUNT parameter in the source code). Make the bar permanent, don't exit after the standard input is closed. -=item B<-n> +=item B<-n> I Set the WM_NAME atom value for the bar. From 2c0f57c5846e34b3576c58808a296266e9f8a30b Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Wed, 28 Oct 2015 22:41:15 +0100 Subject: [PATCH 6/6] Prevent load_font calls before connecting to X --- lemonbar.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lemonbar.c b/lemonbar.c index 6582160..11aabb3 100644 --- a/lemonbar.c +++ b/lemonbar.c @@ -1246,6 +1246,9 @@ main (int argc, char **argv) areas = 10; wm_name = NULL; + // Connect to the Xserver and initialize scr + xconn(); + while ((ch = getopt(argc, argv, "hg:bdf:a:pu:B:F:n:")) != -1) { switch (ch) { case 'h': @@ -1297,9 +1300,6 @@ main (int argc, char **argv) bx = geom_v[2]; by = geom_v[3]; - // Connect to the Xserver and initialize scr - xconn(); - // Do the heavy lifting init(wm_name); // Get the fd to Xserver