Merge remote-tracking branch 'upstream/master' into xft-port

* upstream/master:
  Prevent load_font calls before connecting to X
  The WM_NAME option takes an argument
  Allow the user to set the WM_NAME atom value
  Warn the user when the area geometry is invalid
  Make the number of clickable areas configurable.
  Don't parse named colors. Expand #rgb format.
This commit is contained in:
krypt-n 2015-11-02 13:49:43 +01:00
commit 2f66bb1407
2 changed files with 118 additions and 82 deletions

View File

@ -6,7 +6,7 @@ lemonbar - Featherweight lemon-scented bar
=head1 SYNOPSIS =head1 SYNOPSIS
I<lemonbar> [-h | -g I<width>B<x>I<height>B<+>I<x>B<+>I<y> | -b | -d | -f I<font> | -p | -u I<pixel> | -B I<color> | -F I<color> | -o I<offset>] I<lemonbar> [-h | -g I<width>B<x>I<height>B<+>I<x>B<+>I<y> | -b | -d | -f I<font> | -p | -n I<name> | -u I<pixel> | -B I<color> | -F I<color> | -o I<offset>]
=head1 DESCRIPTION =head1 DESCRIPTION
@ -41,13 +41,17 @@ changing the MAX_FONT_COUNT parameter in the source code).
Make the bar permanent, don't exit after the standard input is closed. Make the bar permanent, don't exit after the standard input is closed.
=item B<-n> I<name>
Set the WM_NAME atom value for the bar.
=item B<-u> I<pixel> =item B<-u> I<pixel>
Sets the underline width in pixels. The default is 1. Sets the underline width in pixels. The default is 1.
=item B<-B> I<color> =item B<-B> I<color>
Set the background color of the bar. I<color> 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<color> 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<color> =item B<-F> I<color>

View File

@ -45,8 +45,11 @@ typedef struct monitor_t {
} monitor_t; } monitor_t;
typedef struct area_t { typedef struct area_t {
bool active; unsigned int begin:16;
int begin, end, align, button; unsigned int end:16;
bool active:1;
int align:3;
int button:3;
xcb_window_t window; xcb_window_t window;
char *cmd; char *cmd;
} area_t; } area_t;
@ -61,11 +64,9 @@ typedef union rgba_t {
uint32_t v; uint32_t v;
} rgba_t; } rgba_t;
#define N 20
typedef struct area_stack_t { typedef struct area_stack_t {
int pos; int at, max;
area_t slot[N]; area_t *area;
} area_stack_t; } area_stack_t;
enum { enum {
@ -114,7 +115,7 @@ static int bw = -1, bh = -1, bx = 0, by = 0;
static int bu = 1; // Underline height static int bu = 1; // Underline height
static rgba_t fgc, bgc, ugc; static rgba_t fgc, bgc, ugc;
static rgba_t dfgc, dbgc; static rgba_t dfgc, dbgc;
static area_stack_t astack; static area_stack_t area_stack;
static XftColor sel_fg; static XftColor sel_fg;
static XftDraw *xft_draw; static XftDraw *xft_draw;
@ -305,9 +306,7 @@ draw_char (monitor_t *mon, font_t *cur_font, int x, int align, uint16_t ch)
rgba_t rgba_t
parse_color (const char *str, char **end, const rgba_t def) parse_color (const char *str, char **end, const rgba_t def)
{ {
xcb_alloc_named_color_reply_t *nc_reply;
int string_len; int string_len;
rgba_t ret;
char *ep; char *ep;
if (!str) if (!str)
@ -322,61 +321,61 @@ parse_color (const char *str, char **end, const rgba_t def)
} }
// Hex representation // Hex representation
if (str[0] == '#') { if (str[0] != '#') {
errno = 0;
rgba_t tmp = (rgba_t)(uint32_t)strtoul(str + 1, &ep, 16);
if (end) if (end)
*end = ep; *end = (char *)str;
// Some error checking is definitely good fprintf(stderr, "Invalid color specified\n");
if (errno) { return def;
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;
} }
// Actual color name, resolve it errno = 0;
for (string_len = 0; isalpha(str[string_len]); string_len++) rgba_t tmp = (rgba_t)(uint32_t)strtoul(str + 1, &ep, 16);
;
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);
if (end) 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 void
set_attribute (const char modifier, const char attribute) set_attribute (const char modifier, const char attribute)
{ {
@ -405,8 +404,8 @@ area_t *
area_get (xcb_window_t win, const int btn, const int x) 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--) { for (int i = area_stack.at; i >= 0; i--) {
area_t *a = &astack.slot[i]; area_t *a = &area_stack.area[i];
if (a->window == win && a->button == btn if (a->window == win && a->button == btn
&& x >= a->begin && x < a->end) && x >= a->begin && x < a->end)
return a; return a;
@ -422,8 +421,8 @@ area_shift (xcb_window_t win, const int align, int delta)
if (align == ALIGN_C) if (align == ALIGN_C)
delta /= 2; delta /= 2;
for (int i = 0; i < astack.pos; i++) { for (int i = 0; i < area_stack.at; i++) {
area_t *a = &astack.slot[i]; area_t *a = &area_stack.area[i];
if (a->window == win && a->align == align && !a->active) { if (a->window == win && a->align == align && !a->active) {
a->begin -= delta; a->begin -= delta;
a->end -= delta; a->end -= delta;
@ -443,13 +442,15 @@ area_add (char *str, const char *optend, char **end, monitor_t *mon, const int x
*end = 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--) 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 // 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; return false;
}
const int size = x - a->begin; const int size = x - a->begin;
@ -472,11 +473,12 @@ area_add (char *str, const char *optend, char **end, monitor_t *mon, const int x
return true; return true;
} }
if (astack.pos >= N) { if (area_stack.at + 1 > area_stack.max) {
fprintf(stderr, "astack overflow!\n"); fprintf(stderr, "Cannot add any more clickable areas (used %d/%d)\n",
area_stack.at, area_stack.max);
return false; 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 // Found the closing : and check if it's just an escaped one
for (trail = strchr(++str, ':'); trail && trail[-1] == '\\'; trail = strchr(trail + 1, ':')) for (trail = strchr(++str, ':'); trail && trail[-1] == '\\'; trail = strchr(trail + 1, ':'))
@ -567,7 +569,8 @@ parse (char *text)
align = ALIGN_L; align = ALIGN_L;
cur_mon = monhead; 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) for (monitor_t *m = monhead; m != NULL; m = m->next)
fill_rect(m->pixmap, gc[GC_CLEAR], 0, 0, m->width, bh); fill_rect(m->pixmap, gc[GC_CLEAR], 0, 0, m->width, bh);
@ -608,7 +611,8 @@ parse (char *text)
// The range is 1-5 // The range is 1-5
if (isdigit(*p) && (*p > '0' && *p < '6')) if (isdigit(*p) && (*p > '0' && *p < '6'))
button = *p++ - '0'; 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; break;
case 'B': bgc = parse_color(p, &p, dbgc); update_gc(); break; case 'B': bgc = parse_color(p, &p, dbgc); update_gc(); break;
@ -1208,7 +1212,7 @@ xconn (void)
} }
void void
init (void) init (char *wm_name)
{ {
// Try to load a default font // Try to load a default font
if (!font_count) if (!font_count)
@ -1296,6 +1300,10 @@ init (void)
// Make sure that the window really gets in the place it's supposed to be // Make sure that the window really gets in the place it's supposed to be
// Some WM such as Openbox need this // 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_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);
} }
char color[] = "#ffffff"; char color[] = "#ffffff";
@ -1311,6 +1319,7 @@ init (void)
void void
cleanup (void) cleanup (void)
{ {
free(area_stack.area);
for (int i = 0; font_list[i]; i++) { for (int i = 0; font_list[i]; i++) {
if (font_list[i]->xft_ft) { if (font_list[i]->xft_ft) {
XftFontClose (dpy, font_list[i]->xft_ft); XftFontClose (dpy, font_list[i]->xft_ft);
@ -1363,33 +1372,39 @@ main (int argc, char **argv)
char input[4096] = {0, }; char input[4096] = {0, };
bool permanent = false; bool permanent = false;
int geom_v[4] = { -1, -1, 0, 0 }; int geom_v[4] = { -1, -1, 0, 0 };
int ch; int ch, areas;
char *wm_name;
// Install the parachute! // Install the parachute!
atexit(cleanup); atexit(cleanup);
signal(SIGINT, sighandle); signal(SIGINT, sighandle);
signal(SIGTERM, sighandle); signal(SIGTERM, sighandle);
// B/W combo
dbgc = bgc = (rgba_t)0x00000000U;
dfgc = fgc = (rgba_t)0xffffffffU;
ugc = fgc;
// A safe default
areas = 10;
wm_name = NULL;
// Connect to the Xserver and initialize scr // Connect to the Xserver and initialize scr
xconn(); xconn();
// B/W combo while ((ch = getopt(argc, argv, "hg:bdf:a:pu:B:F:n:o:")) != -1) {
dbgc = bgc = parse_color("black", NULL, (rgba_t)scr->black_pixel);
dfgc = fgc = parse_color("white", NULL, (rgba_t)scr->white_pixel);
ugc = fgc;
while ((ch = getopt(argc, argv, "hg:bdf:a:pu:o:B:F:")) != -1) {
switch (ch) { switch (ch) {
case 'h': case 'h':
printf ("lemonbar version %s\n", VERSION); printf ("lemonbar version %s\n", VERSION);
printf ("usage: %s [-h | -g | -b | -d | -f | -a | -p | -u | -B | -F | -o]\n" printf ("usage: %s [-h | -g | -b | -d | -f | -a | -p | -n | -u | -B | -F]\n"
"\t-h Show this help\n" "\t-h Show this help\n"
"\t-g Set the bar geometry {width}x{height}+{xoffset}+{yoffset}\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" "\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-d Force docking (use this if your WM isn't EWMH compliant)\n"
"\t-f Set the font name to use\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-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-u Set the underline/overline height in pixels\n"
"\t-B Set background color in #AARRGGBB\n" "\t-B Set background color in #AARRGGBB\n"
"\t-F Set foreground color in #AARRGGBB\n" "\t-F Set foreground color in #AARRGGBB\n"
@ -1397,16 +1412,33 @@ main (int argc, char **argv)
exit (EXIT_SUCCESS); exit (EXIT_SUCCESS);
case 'g': (void)parse_geometry_string(optarg, geom_v); break; case 'g': (void)parse_geometry_string(optarg, geom_v); break;
case 'p': permanent = true; break; case 'p': permanent = true; break;
case 'n': wm_name = optarg; break;
case 'b': topbar = false; break; case 'b': topbar = false; break;
case 'd': dock = true; break; case 'd': dock = true; break;
case 'f': font_load(optarg); break; case 'f': font_load(optarg); break;
case 'u': bu = strtoul(optarg, NULL, 10); break; case 'u': bu = strtoul(optarg, NULL, 10); break;
case 'o': add_y_offset(strtol(optarg, NULL, 10)); break; case 'o': add_y_offset(strtol(optarg, NULL, 10)); break;
case 'B': dbgc = bgc = parse_color(optarg, NULL, (rgba_t)scr->black_pixel); break; case 'B': dbgc = bgc = parse_color(optarg, NULL, (rgba_t)0x00000000U); break;
case 'F': dfgc = fgc = parse_color(optarg, NULL, (rgba_t)scr->white_pixel); 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 // Copy the geometry values in place
bw = geom_v[0]; bw = geom_v[0];
bh = geom_v[1]; bh = geom_v[1];
@ -1414,7 +1446,7 @@ main (int argc, char **argv)
by = geom_v[3]; by = geom_v[3];
// Do the heavy lifting // Do the heavy lifting
init(); init(wm_name);
// Get the fd to Xserver // Get the fd to Xserver
pollin[1].fd = xcb_get_file_descriptor(c); pollin[1].fd = xcb_get_file_descriptor(c);
for (;;) { for (;;) {