Robust geometry string parsing. Support for x offset. Underline/overline width is now configurable (fixes #43)
This commit is contained in:
parent
66e8b40be1
commit
b70edf4c4b
10
README.pod
10
README.pod
|
@ -4,7 +4,7 @@ bar - bar ain't recursive
|
||||||
|
|
||||||
=head1 SYNOPSIS
|
=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
|
=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.
|
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>
|
=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.
|
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>
|
=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).
|
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
177
bar.c
|
@ -7,6 +7,7 @@
|
||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
#include <xcb/xcb.h>
|
#include <xcb/xcb.h>
|
||||||
#include <xcb/xinerama.h>
|
#include <xcb/xinerama.h>
|
||||||
#include <xcb/randr.h>
|
#include <xcb/randr.h>
|
||||||
|
@ -51,10 +52,11 @@ static xcb_gcontext_t gc[3];
|
||||||
static monitor_t *monhead, *montail;
|
static monitor_t *monhead, *montail;
|
||||||
static font_t *main_font, *alt_font;
|
static font_t *main_font, *alt_font;
|
||||||
static uint32_t attrs = 0;
|
static uint32_t attrs = 0;
|
||||||
static float ba = 1.0f;
|
static float ba = 1.0f; /* bar alpha */
|
||||||
static bool dock = false;
|
static bool dock = false;
|
||||||
static bool topbar = true;
|
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 char *mfont, *afont;
|
||||||
static uint32_t fgc, bgc, ugc;
|
static uint32_t fgc, bgc, ugc;
|
||||||
static uint32_t dfgc, dbgc;
|
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);
|
ch = (ch >> 8) | (ch << 8);
|
||||||
|
|
||||||
/* String baseline coordinates */
|
/* 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_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_char2b_t *)&ch);
|
|
||||||
|
|
||||||
/* We can render both at the same time */
|
/* We can render both at the same time */
|
||||||
if (attrs & ATTR_OVERL)
|
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)
|
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;
|
return ch_width;
|
||||||
}
|
}
|
||||||
|
@ -433,6 +434,40 @@ rect_sort_cb (const void *p1, const void *p2)
|
||||||
return 0;
|
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
|
void
|
||||||
get_randr_monitors (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_get_screen_resources_current_reply_t *rres_reply;
|
||||||
xcb_randr_output_t *outputs;
|
xcb_randr_output_t *outputs;
|
||||||
int num, valid = 0;
|
int num, valid = 0;
|
||||||
int width = bw;
|
|
||||||
|
|
||||||
rres_reply = xcb_randr_get_screen_resources_current_reply(c,
|
rres_reply = xcb_randr_get_screen_resources_current_reply(c,
|
||||||
xcb_randr_get_screen_resources_current(c, scr->root), NULL);
|
xcb_randr_get_screen_resources_current(c, scr->root), NULL);
|
||||||
|
@ -521,26 +555,7 @@ get_randr_monitors (void)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Sort before use */
|
monitor_create_chain(rects, num);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -567,27 +582,9 @@ get_xinerama_monitors (void)
|
||||||
xcb_xinerama_screen_info_next(&iter);
|
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);
|
free(xqs_reply);
|
||||||
|
|
||||||
|
monitor_create_chain(rects, screens);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -725,29 +722,55 @@ sighandle (int signal)
|
||||||
exit(EXIT_SUCCESS);
|
exit(EXIT_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Parse an urxvt-like geometry string {width}x{height}, both the fields are
|
/* Parse an X-styled geometry string, we don't support signed offsets tho. */
|
||||||
* optional. A width of -1 means that the bar spawns the whole screen. */
|
bool
|
||||||
void
|
parse_geometry_string (char *str, int *tmp)
|
||||||
parse_geometry_string (char *str)
|
|
||||||
{
|
{
|
||||||
char *p, *q;
|
char *p = str;
|
||||||
int tmp;
|
int i = 0, j;
|
||||||
|
|
||||||
if (!str)
|
if (!str || !str[0])
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
p = str;
|
/* The leading = is optional */
|
||||||
|
if (*p == '=')
|
||||||
|
p++;
|
||||||
|
|
||||||
tmp = strtoul(p, &q, 10);
|
while (*p) {
|
||||||
if (p != q)
|
/* A geometry string has only 4 fields */
|
||||||
bw = tmp;
|
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 */
|
return true;
|
||||||
p = q + 1;
|
|
||||||
|
|
||||||
tmp = strtoul(p, &q, 10);
|
|
||||||
if (p != q)
|
|
||||||
bh = tmp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -771,14 +794,15 @@ parse_font_list (char *str)
|
||||||
int
|
int
|
||||||
main (int argc, char **argv)
|
main (int argc, char **argv)
|
||||||
{
|
{
|
||||||
char input[2048] = {0, };
|
|
||||||
struct pollfd pollin[2] = {
|
struct pollfd pollin[2] = {
|
||||||
{ .fd = STDIN_FILENO, .events = POLLIN },
|
{ .fd = STDIN_FILENO, .events = POLLIN },
|
||||||
{ .fd = -1 , .events = POLLIN },
|
{ .fd = -1 , .events = POLLIN },
|
||||||
};
|
};
|
||||||
xcb_generic_event_t *ev;
|
xcb_generic_event_t *ev;
|
||||||
xcb_expose_event_t *expose_ev;
|
xcb_expose_event_t *expose_ev;
|
||||||
|
char input[2048] = {0, };
|
||||||
bool permanent = false;
|
bool permanent = false;
|
||||||
|
int geom_v[4] = { -1, -1, 0, 0 };
|
||||||
|
|
||||||
/* Install the parachute! */
|
/* Install the parachute! */
|
||||||
atexit(cleanup);
|
atexit(cleanup);
|
||||||
|
@ -793,10 +817,10 @@ main (int argc, char **argv)
|
||||||
dfgc = fgc = scr->white_pixel;
|
dfgc = fgc = scr->white_pixel;
|
||||||
|
|
||||||
char ch;
|
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) {
|
switch (ch) {
|
||||||
case 'h':
|
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-h Show this help\n"
|
||||||
"\t-g Set the bar geometry {width}x{height})\n"
|
"\t-g Set the bar geometry {width}x{height})\n"
|
||||||
"\t-b Put bar at the bottom of the screen\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-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-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-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-B Set background color in #RRGGBB\n"
|
||||||
"\t-F Set foreground color in #RRGGBB\n", argv[0]);
|
"\t-F Set foreground color in #RRGGBB\n", argv[0]);
|
||||||
exit (EXIT_SUCCESS);
|
exit (EXIT_SUCCESS);
|
||||||
case 'a': ba = strtof(optarg, NULL); break;
|
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 'p': permanent = true; break;
|
||||||
case 'b': topbar = false; break;
|
case 'b': topbar = false; break;
|
||||||
case 'd': dock = true; break;
|
case 'd': dock = true; break;
|
||||||
case 'f': parse_font_list(optarg); 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 'B': dbgc = bgc = parse_color(optarg, NULL, scr->black_pixel); break;
|
||||||
case 'F': dfgc = fgc = parse_color(optarg, NULL, scr->white_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 */
|
/* 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)
|
if (ba > 1.0f)
|
||||||
ba = 1.0f;
|
ba = 1.0f;
|
||||||
if (ba < 0.0f)
|
if (ba < 0.0f)
|
||||||
|
@ -870,5 +907,5 @@ main (int argc, char **argv)
|
||||||
xcb_flush(c);
|
xcb_flush(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user