Robust geometry string parsing. Support for x offset. Underline/overline width is now configurable (fixes #43)

This commit is contained in:
LemonBoy 2014-02-21 11:52:50 +00:00
parent 66e8b40be1
commit b70edf4c4b
2 changed files with 114 additions and 73 deletions

View File

@ -4,7 +4,7 @@ bar - bar ain't recursive
=head1 SYNOPSIS
I<bar> [-h | -g I<width>B<x>I<height> | -b | -d | -f I<font> | -a I<alpha>| -p | -B I<color> | -F I<color>]
I<bar> [-h | -g I<width>B<x>I<height>B<+>I<x> | -b | -d | -f I<font> | -a I<alpha>| -p | -u I<pixel> | -B I<color> | -F I<color>]
=head1 DESCRIPTION
@ -18,9 +18,9 @@ B<bar> is a lightweight bar entirely based on XCB. Provides full UTF-8 support,
Display the help and exit.
=item B<-g> I<width>B<x>I<height>
=item B<-g> I<width>B<x>I<height>B<+>I<x>
Set the window geometry. Both the parameters can be omitted, bar defaults the former to the whole width and the height to the font height + 1 pixel.
Set the window geometry. If a parameter is omitted it's filled with the default value.
=item B<-b>
@ -42,6 +42,10 @@ Set the bar alpha in range 0.0 to 1.0. This requires a compositor manager such a
Make bar permanent, don't exit after the standard input is closed.
=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 (#rrggbb) or in the symbolic name format (eg. white, brightred, darkgray).

177
bar.c
View File

@ -7,6 +7,7 @@
#include <poll.h>
#include <getopt.h>
#include <unistd.h>
#include <errno.h>
#include <xcb/xcb.h>
#include <xcb/xinerama.h>
#include <xcb/randr.h>
@ -51,10 +52,11 @@ static xcb_gcontext_t gc[3];
static monitor_t *monhead, *montail;
static font_t *main_font, *alt_font;
static uint32_t attrs = 0;
static float ba = 1.0f;
static float ba = 1.0f; /* bar alpha */
static bool dock = false;
static bool topbar = true;
static int bw = -1, bh = -1;
static int bw = -1, bh = -1, bx = 0;
static int bu = 1; /* Underline height */
static char *mfont, *afont;
static uint32_t fgc, bgc, ugc;
static uint32_t dfgc, dbgc;
@ -102,14 +104,13 @@ draw_char (monitor_t *mon, font_t *cur_font, int x, int align, uint16_t ch)
ch = (ch >> 8) | (ch << 8);
/* String baseline coordinates */
xcb_image_text_16(c, 1, canvas, gc[0], x + mon->x, bh / 2 + cur_font->height / 2 - cur_font->descent,
(xcb_char2b_t *)&ch);
xcb_image_text_16(c, 1, canvas, gc[0], x + mon->x, bh / 2 + cur_font->height / 2 - cur_font->descent, (xcb_char2b_t *)&ch);
/* We can render both at the same time */
if (attrs & ATTR_OVERL)
fill_rect(gc[2], x + mon->x, 0, ch_width, 1);
fill_rect(gc[2], x + mon->x, 0, ch_width, bu);
if (attrs & ATTR_UNDERL)
fill_rect(gc[2], x + mon->x, bh-1, ch_width, 1);
fill_rect(gc[2], x + mon->x, bh-bu, ch_width, bu);
return ch_width;
}
@ -433,6 +434,40 @@ rect_sort_cb (const void *p1, const void *p2)
return 0;
}
void
monitor_create_chain (xcb_rectangle_t *rects, const int num)
{
int width = bw;
int left = bx;
/* Sort before use */
qsort(rects, num, sizeof(xcb_rectangle_t), rect_sort_cb);
/* Left is a positive number or zero therefore monitors with zero width are excluded */
for (int i = 0; i < num; i++) {
if (rects[i].width > left) {
monitor_t *mon = monitor_new(
rects[i].x + left,
rects[i].y,
min(width, rects[i].width - left),
rects[i].height);
monitor_add(mon);
width -= rects[i].width - left;
/* No need to check for other monitors */
if (width <= 0)
break;
}
left -= rects[i].width;
if (left < 0)
left = 0;
}
}
void
get_randr_monitors (void)
{
@ -440,7 +475,6 @@ get_randr_monitors (void)
xcb_randr_get_screen_resources_current_reply_t *rres_reply;
xcb_randr_output_t *outputs;
int num, valid = 0;
int width = bw;
rres_reply = xcb_randr_get_screen_resources_current_reply(c,
xcb_randr_get_screen_resources_current(c, scr->root), NULL);
@ -521,26 +555,7 @@ get_randr_monitors (void)
return;
}
/* Sort before use */
qsort(rects, num, sizeof(xcb_rectangle_t), rect_sort_cb);
for (int i = 0; i < num; i++) {
if (rects[i].width) {
monitor_t *mon = monitor_new(
rects[i].x,
rects[i].y,
min(width, rects[i].width),
rects[i].height);
monitor_add(mon);
width -= rects[i].width;
/* No need to check for other monitors */
if (width <= 0)
break;
}
}
monitor_create_chain(rects, num);
}
void
@ -567,27 +582,9 @@ get_xinerama_monitors (void)
xcb_xinerama_screen_info_next(&iter);
}
/* Sort before use */
qsort(rects, screens, sizeof(xcb_rectangle_t), rect_sort_cb);
/* The width is consumed across all the screens */
for (int i = 0; i < screens; i++) {
monitor_t *mon = monitor_new(
rects[i].x,
rects[i].y,
min(width, rects[i].width),
rects[i].height);
monitor_add(mon);
width -= rects[i].width;
/* No need to check for other monitors */
if (width <= 0)
break;
}
free(xqs_reply);
monitor_create_chain(rects, screens);
}
void
@ -725,29 +722,55 @@ sighandle (int signal)
exit(EXIT_SUCCESS);
}
/* Parse an urxvt-like geometry string {width}x{height}, both the fields are
* optional. A width of -1 means that the bar spawns the whole screen. */
void
parse_geometry_string (char *str)
/* Parse an X-styled geometry string, we don't support signed offsets tho. */
bool
parse_geometry_string (char *str, int *tmp)
{
char *p, *q;
int tmp;
char *p = str;
int i = 0, j;
if (!str)
return;
if (!str || !str[0])
return false;
p = str;
/* The leading = is optional */
if (*p == '=')
p++;
tmp = strtoul(p, &q, 10);
if (p != q)
bw = tmp;
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;
}
/* P now might point to a NULL char, strtoul takes care of that */
p = q + 1;
tmp = strtoul(p, &q, 10);
if (p != q)
bh = tmp;
return true;
}
void
@ -771,14 +794,15 @@ parse_font_list (char *str)
int
main (int argc, char **argv)
{
char input[2048] = {0, };
struct pollfd pollin[2] = {
{ .fd = STDIN_FILENO, .events = POLLIN },
{ .fd = -1 , .events = POLLIN },
};
xcb_generic_event_t *ev;
xcb_expose_event_t *expose_ev;
char input[2048] = {0, };
bool permanent = false;
int geom_v[4] = { -1, -1, 0, 0 };
/* Install the parachute! */
atexit(cleanup);
@ -793,10 +817,10 @@ main (int argc, char **argv)
dfgc = fgc = scr->white_pixel;
char ch;
while ((ch = getopt(argc, argv, "hg:bdf:a:pB:F:")) != -1) {
while ((ch = getopt(argc, argv, "hg:bdf:a:pu:B:F:")) != -1) {
switch (ch) {
case 'h':
printf ("usage: %s [-h | -g | -b | -d | -f | -a | -p | -B | -F]\n"
printf ("usage: %s [-h | -g | -b | -d | -f | -a | -p | -u | -B | -F]\n"
"\t-h Show this help\n"
"\t-g Set the bar geometry {width}x{height})\n"
"\t-b Put bar at the bottom of the screen\n"
@ -804,21 +828,34 @@ main (int argc, char **argv)
"\t-f Bar font list, comma separated\n"
"\t-a Set the bar alpha ranging from 0.0 to 1.0 (requires a compositor)\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 #RRGGBB\n"
"\t-F Set foreground color in #RRGGBB\n", argv[0]);
exit (EXIT_SUCCESS);
case 'a': ba = strtof(optarg, NULL); break;
case 'g': parse_geometry_string(optarg); break;
case 'g': (void)parse_geometry_string(optarg, geom_v); break;
case 'p': permanent = true; break;
case 'b': topbar = false; break;
case 'd': dock = true; break;
case 'f': parse_font_list(optarg); break;
case 'u': bu = strtoul(optarg, NULL, 10); break;
case 'B': dbgc = bgc = parse_color(optarg, NULL, scr->black_pixel); break;
case 'F': dfgc = fgc = parse_color(optarg, NULL, scr->white_pixel); break;
}
}
/* Copy the geometry values in place */
bw = geom_v[0];
bh = geom_v[1];
bx = geom_v[2];
/* Sanitize the arguments */
if (bx >= scr->width_in_pixels || bx + bw >= scr->width_in_pixels) {
bx = 0;
bw = -1;
}
if (bu >= bh)
bu = 1;
if (ba > 1.0f)
ba = 1.0f;
if (ba < 0.0f)
@ -870,5 +907,5 @@ main (int argc, char **argv)
xcb_flush(c);
}
return 0;
return EXIT_SUCCESS;
}