resolve conflicts, update to latest.

This commit is contained in:
Nathan Isom 2016-02-23 00:52:38 -06:00
commit 7a1efbb759
4 changed files with 195 additions and 166 deletions

3
.gitignore vendored
View File

@ -1,5 +1,4 @@
config.h
bar
lemonbar
*.o
*.swp
*~

View File

@ -3,5 +3,6 @@ compiler:
- clang
- gcc
before_install:
- sudo apt-get install -qq libx11-xcb-dev libxcb-randr0-dev libxcb-xinerama0-dev
- sudo apt-get update -qq
- sudo apt-get install -y libx11-xcb-dev libxcb-randr0-dev libxcb-xinerama0-dev
script: make

View File

@ -6,7 +6,7 @@ lemonbar - Featherweight lemon-scented bar
=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>]
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> | -U I<color>]
=head1 DESCRIPTION
@ -34,29 +34,42 @@ Force docking without asking the window manager. This is needed if the window ma
=item B<-f> I<font>
Comma separated list of fonts, lemonbar supports a maximum of five fonts (the limit can be tweaked by changing the MAX_FONT_COUNT parameter in the source).
Define the font to load into one of the five slots (the number of slots is hardcoded and can be tweaked by
changing the MAX_FONT_COUNT parameter in the source code).
=item B<-a> I<number>
Set number of clickable areas (default is 10)
=item B<-p>
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>
Sets the underline width in pixels. The default is 1.
=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>
Set the foreground color of the bar. Accepts the same color formats as B<-B>.
=item B<-U> I<color>
Set the underline color of the bar. Accepts the same color formats as B<-B>.
=back
=head1 FORMATTING
lemonbar provides a screenrc-inspired formatting syntax to allow full customization at runtime. Every formatting block is opened with B<%{> and closed by B<}> and accepts the following commands, the parser tries it's best to handle malformed input.
lemonbar provides a screenrc-inspired formatting syntax to allow full customization at runtime. Every formatting block is opened with C<%{> and closed by C<}> and accepts the following commands, the parser tries it's best to handle malformed input. Use C<%%> to get a literal percent sign (C<%>).
=over
@ -86,7 +99,7 @@ Set the text foreground color. The parameter I<color> can be I<-> or a color in
=item B<T>I<index>
Set the font used to draw the following text. The parameter I<index> is a 1-based index of the font list supplied to bar. Any other value (for example I<->) resets the 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, lemonbar will fall back to normal behaviour for that character.
Set the font used to draw the following text. The parameter I<index> can either be I<-> or the 1-based index of the slot which contains the desired font. If the parameter is I<-> lemonbar resets to the normal behavior (matching the first font that can be used for the character). If the selected font can't be used to draw a character, lemonbar will fall back to normal behavior for that character
=item B<U>I<color>
@ -94,7 +107,7 @@ Set the text underline color. The parameter I<color> can be I<-> or a color in o
=item B<A>I<button>:I<command>:
Create a clickable area starting from the current position, when the area is clicked I<command> is executed. The area is closed when a B<A> token, not followed by : is encountered.
Create a clickable area starting from the current position, when the area is clicked I<command> is printed on stdout. The area is closed when a B<A> token, not followed by : is encountered.
Eg. I<%{A:reboot:} Click here to reboot %{A}>

View File

@ -22,7 +22,7 @@
typedef struct font_t {
xcb_font_t ptr;
int descent, height;
int descent, height, width;
uint16_t char_max;
uint16_t char_min;
xcb_charinfo_t *width_lut;
@ -36,8 +36,11 @@ typedef struct monitor_t {
} 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;
unsigned 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 {
@ -94,8 +95,8 @@ static bool topbar = true;
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 rgba_t dfgc, dbgc, dugc;
static area_stack_t area_stack;
static int slant;
static int slant_width;
static int full_slant_width;
@ -184,13 +185,18 @@ xcb_void_cookie_t xcb_poly_text_16_simple(xcb_connection_t * c,
xcb_parts[6].iov_len = -(xcb_parts[4].iov_len + xcb_parts[5].iov_len) & 3;
xcb_ret.sequence = xcb_send_request(c, 0, xcb_parts + 2, &xcb_req);
return xcb_ret;
}
int
draw_char (monitor_t *mon, font_t *cur_font, int x, int align, uint16_t ch, int slant)
{
int ch_width = cur_font->width_lut[ch - cur_font->char_min].character_width;
int ch_width;
ch_width = (cur_font->width_lut) ?
cur_font->width_lut[ch - cur_font->char_min].character_width:
cur_font->width;
switch (align) {
case ALIGN_C:
@ -277,9 +283,7 @@ draw_char (monitor_t *mon, font_t *cur_font, int x, int align, uint16_t ch, int
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)
@ -294,61 +298,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)
{
@ -371,10 +375,9 @@ 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];
if (a->window == win && a->button == btn
&& x >= a->begin && x < a->end)
for (int i = area_stack.at - 1; 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;
}
return NULL;
@ -388,8 +391,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;
@ -409,13 +412,15 @@ 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)
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;
@ -438,11 +443,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, ':'))
@ -481,10 +487,13 @@ 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 &&
font->width_lut[c - font->char_min].character_width);
if (c < font->char_min || c > font->char_max)
return false;
if (font->width_lut && font->width_lut[c - font->char_min].character_width == 0)
return false;
return true;
}
// returns NULL if character cannot be printed
@ -517,7 +526,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);
@ -526,7 +536,8 @@ parse (char *text)
if (*p == '\0' || *p == '\n')
return;
if (*p == '%' && p++ && *p == '{' && (block_end = strchr(p++, '}'))) {
if (p[0] == '%' && p[1] == '{' && (block_end = strchr(p++, '}'))) {
p++;
while (p < block_end) {
while (isspace(*p))
p++;
@ -552,12 +563,13 @@ 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;
case 'F': fgc = parse_color(p, &p, dfgc); update_gc(); break;
case 'U': ugc = parse_color(p, &p, dbgc); update_gc(); break;
case 'U': ugc = parse_color(p, &p, dugc); update_gc(); break;
case 'E':
@ -592,13 +604,22 @@ parse (char *text)
break;
case 'T':
font_index = (int)strtoul(p, &ep, 10);
// User-specified 'font_index' ∊ (0,font_count]
// Otherwise just fallback to the automatic font selection
if (!font_index || font_index > font_count)
if (*p == '-') { //Reset to automatic font selection
font_index = -1;
p = ep;
break;
p++;
break;
} else if (isdigit(*p)) {
font_index = (int)strtoul(p, &ep, 10);
// 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 = ep;
break;
} else {
fprintf(stderr, "Invalid font slot \"%c\"\n", *p++); //Swallow the token
break;
}
// In case of error keep parsing after the closing }
default:
@ -636,7 +657,7 @@ parse (char *text)
ucs = 0xfffd;
p += 5;
}
// Siz byte utf8 sequence
// Six byte utf8 sequence
else if ((utf[0] & 0xfe) == 0xfc) {
ucs = 0xfffd;
p += 6;
@ -661,9 +682,14 @@ parse (char *text)
}
}
font_t *
font_load (const char *str)
void
font_load (const char *pattern)
{
if (font_count >= MAX_FONT_COUNT) {
fprintf(stderr, "Max font count reached. Could not load font \"%s\"\n", pattern);
return;
}
xcb_query_font_cookie_t queryreq;
xcb_query_font_reply_t *font_info;
xcb_void_cookie_t cookie;
@ -671,16 +697,16 @@ font_load (const char *str)
font = xcb_generate_id(c);
cookie = xcb_open_font_checked(c, font, strlen(str), str);
cookie = xcb_open_font_checked(c, font, strlen(pattern), pattern);
if (xcb_request_check (c, cookie)) {
fprintf(stderr, "Could not load font \"%s\"\n", str);
return NULL;
fprintf(stderr, "Could not load font \"%s\"\n", pattern);
return;
}
font_t *ret = calloc(1, sizeof(font_t));
if (!ret)
return NULL;
return;
queryreq = xcb_query_font(c, font);
font_info = xcb_query_font_reply(c, queryreq, NULL);
@ -688,6 +714,7 @@ font_load (const char *str)
ret->ptr = font;
ret->descent = font_info->font_descent;
ret->height = font_info->font_ascent + font_info->font_descent;
ret->width = font_info->max_bounds.character_width;
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;
@ -700,7 +727,7 @@ font_load (const char *str)
free(font_info);
return ret;
font_list[font_count++] = ret;
}
enum {
@ -820,10 +847,15 @@ rect_sort_cb (const void *p1, const void *p2)
const xcb_rectangle_t *r1 = (xcb_rectangle_t *)p1;
const xcb_rectangle_t *r2 = (xcb_rectangle_t *)p2;
if (r1->x < r2->x || r1->y < r2->y)
if (r1->x < r2->x || r1->y + r1->height <= r2->y)
{
return -1;
if (r1->x > r2->x || r1->y > r2->y)
return 1;
}
if (r1->x > r2->x || r1->y + r1->height > r2->y)
{
return 1;
}
return 0;
}
@ -1086,48 +1118,6 @@ parse_geometry_string (char *str, int *tmp)
return true;
}
void
parse_font_list (char *str)
{
char *tok, *end;
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;
}
// Strip the leading and trailing whitespaces
while (isspace(*tok) || iscntrl(*tok))
tok++;
end = tok + strlen(tok) - 1;
while ((end > tok && isspace(*end)) || iscntrl(*end))
end--;
*(end + 1) = '\0';
if (tok[0]) {
// Load the selected font
font_t *font = font_load(tok);
if (font)
font_list[font_count++] = font;
}
else
fprintf(stderr, "Empty font name, skipping...\n");
tok = strtok(NULL, ",");
}
}
void
xconn (void)
{
@ -1149,15 +1139,11 @@ xconn (void)
}
void
init (void)
init (char *wm_name)
{
// 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";
// Try to load a default font
if (!font_count)
parse_font_list(fallback_font);
font_load("fixed");
// We tried and failed hard, there's something wrong
if (!font_count)
@ -1241,6 +1227,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);
@ -1249,6 +1239,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);
@ -1295,48 +1287,72 @@ 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;
char *wm_name;
// Install the parachute!
atexit(cleanup);
signal(SIGINT, sighandle);
signal(SIGTERM, sighandle);
// B/W combo
dbgc = bgc = (rgba_t)0x00000000U;
dfgc = fgc = (rgba_t)0xffffffffU;
dugc = ugc = fgc;
// A safe default
areas = 10;
wm_name = NULL;
// Connect to the Xserver and initialize scr
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);
ugc = fgc;
while ((ch = getopt(argc, argv, "hg:bdf:a:pu:B:F:")) != -1) {
while ((ch = getopt(argc, argv, "hg:bdf:a:pu:B:F:U: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"
"\t-d Force docking (use this if your WM isn't EWMH compliant)\n"
"\t-f Bar font list, comma separated\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-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': parse_font_list(optarg); 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 'U': dugc = ugc = parse_color(optarg, NULL, fgc); 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];
@ -1344,7 +1360,7 @@ main (int argc, char **argv)
by = geom_v[3];
// Do the heavy lifting
init();
init(wm_name);
// Get the fd to Xserver
pollin[1].fd = xcb_get_file_descriptor(c);