diff --git a/Makefile b/Makefile index 6ccd461..ff620a8 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ CC ?= gcc STRIP ?= strip -CFLAGS = -std=c99 -O2 +CFLAGS = -std=c99 -Os LDFLAGS = -lxcb -lxcb-xinerama -lxcb-randr CFDEBUG = -g3 -pedantic -Wall -Wunused-parameter -Wlong-long\ -Wsign-conversion -Wconversion -Wimplicit-function-declaration @@ -14,15 +14,12 @@ BINDIR=${PREFIX}/bin all: ${EXEC} +doc: README.pod + pod2man --section=1 --center="bar Manual" --name "bar" --release="bar $(shell git describe --always)" README.pod > bar.1 + .c.o: ${CC} ${CFLAGS} -o $@ -c $< -${OBJS}: config.h - -config.h: - @echo creating $@ from config.def.h - @cp config.def.h $@ - ${EXEC}: ${OBJS} ${CC} -o ${EXEC} ${OBJS} ${LDFLAGS} ${STRIP} -s ${EXEC} @@ -31,14 +28,15 @@ debug: ${EXEC} debug: CC += ${CFDEBUG} clean: - rm -f ./*.o + rm -f ./*.o ./*.1 rm -f ./${EXEC} -install: bar - test -d ${DESTDIR}${BINDIR} || mkdir -p ${DESTDIR}${BINDIR} - install -m755 bar ${DESTDIR}${BINDIR}/bar +install: bar doc + install -D -m 755 bar ${DESTDIR}${BINDIR}/bar + install -D -m 644 bar.1 ${DESTDIR}${PREFIX}/share/man/man1/bar.1 uninstall: rm -f ${DESTDIR}${BINDIR}/bar + rm -f $(DESTDIR)$(PREFIX)/share/man/man1/bar.1 .PHONY: all debug clean install diff --git a/README.md b/README.md deleted file mode 100644 index 0414cbb..0000000 --- a/README.md +++ /dev/null @@ -1,70 +0,0 @@ -b(ar) a(in't) r(ecursive) -========================= -2012-2013 (C) The Lemon Man - -A lightweight bar based on XCB (yay). Provides foreground/background color -switching along with text alignment (screw you dzen!), full utf8 support -and reduced memory footprint. It also supports transparency when using a -compositor such as compton. Nothing less and nothing more. - -Xinerama support ----------------- -Thanks to @Stebalien now bar is Xinerama compliant, just compile it with -XINERAMA=1 and you're good to go! - -Options -------- -bar accepts a couple of command line switches. - -``` --h Show the help and bail out. --p Make the bar permanent. --f Force docking (use this if your WM isn't EWMH compliant) --b Show the bar at the bottom of the screen. -``` - -Configuration -------------- -Change the config.h file and you're good to go! -The text background and foreground are respectively the first and the second -entries in the palette (COLOR0 and COLOR1). - -Colors ------- -Attached there's palette.pl, an handy tool that extracts a palette from your -X colors and returns it ready to be pasted in the configuration file. - -``` -palette.pl <.Xresources / .Xdefaults path> -``` - -If you keep your colors in a separate file just feed that file and you're good -to go. - -Text formatting ---------------- -All the format commands are preceded by a backslash (\\). -To draw a backslash just backslash escape it (\\\\). - -``` -f<0-9> Selects the text foreground color from the palette. -b<0-9> Selects the text background color from the palette. -u<0-9> Selects the underline color from the palette. - To reset the bg/fg/underline color just pass 'r' as the color index. - -l Aligns the text to the left. -c Aligns the text to the center. -r Aligns the text to the right. -``` - - -The options below are valid only if compiled with Xinerama support. - - -``` -s<0-9> Switches to screen 0-9 -sn Switches to next screen -sp Switches to previous screen -sr Switches to the rightmost screen (the latest) -sl Switches to the leftmost screen (the first) -``` diff --git a/README.pod b/README.pod new file mode 100644 index 0000000..5837bd1 --- /dev/null +++ b/README.pod @@ -0,0 +1,153 @@ +=head1 NAME + +bar - bar ain't recursive + +=head1 SYNOPSIS + +I [-h | -g IBI | -b | -d | -f I | -a I| -p | -B I | -F I] + +=head1 DESCRIPTION + +B is a lightweight bar entirely based on XCB. Provides full UTF-8 support, basic formatting, RandR and Xinerama support and EWMH compliance without wasting your precious memory. + +=head1 OPTIONS + +=over + +=item B<-h> + +Display the help and exit. + +=item B<-g> IBI + +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. + +=item B<-b> + +Dock the bar at the bottom of the screen. + +=item B<-d> + +Force docking without asking the window manager. This is needed if the window manager isn't EWMH compliant. + +=item B<-f> I + +Comma separated list of fonts, bar supports a maximum of two fonts. + +=item B<-a> I + +Set the bar alpha in range 0.0 to 1.0. This requires a compositor manager such as Compton. + +=item B<-p> + +Make bar permanent, don't exit after the standard input is closed. + +=item B<-B> I + +Set the background color of the bar. I might be either in hex format (#rrggbb) or in the symbolic name format (eg. white, brightred, darkgray). + +=item B<-F> I + +Set the foreground color of the bar. Accepts the same color formats as B<-B>. + +=back + +=head1 FORMATTING + +bar 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. + +=over + +=item B + +Swap the current background and foreground colors. + +=item B + +Aligns the following text to the left side of the screen. + +=item B + +Aligns the following text to the center of the screen. + +=item B + +Aligns the following text to the right side of the screen. + +=item BI + +Set the text background color. The parameter I can be I<-> or a color in one of the formats mentioned before. The special value I<-> resets the color to the default one. + +=item BI + +Set the text foreground color. The parameter I can be I<-> or a color in one of the formats mentioned before. The special value I<-> resets the color to the default one. + +=item BI + +Set the text underline color. The parameter I can be I<-> or a color in one of the formats mentioned before. The special value I<-> resets the color to the default one. + +=item BI + +Change the monitor bar is rendering to. I can be either + +=over + +=item B<+>/B<-> + +Next/previous monitor. + +=item B/B + +First/last monitor. + +=item I<0-9> + +Nth monitor. + +=back + +=back + +B + +=over + +=item B<+>I + +Set the attribute I for the following text. + +=item B<->I + +Unset the attribute I for the following text. + +=item BI + +Toggle the attribute I for the following text. + +=back + +Where I is one of the following + +=over + +=item B + +Draw a line over the text. + +=item B + +Draw a line under the text. + +=back + +=head1 WWW + +L + +=head1 AUTHOR + +2012-2014 (C) The Lemon Man + +Xinerama support was kindly contributed by Stebalien + +RandR support was kindly contributed by jvvv diff --git a/bar.c b/bar.c index 5566234..a524937 100644 --- a/bar.c +++ b/bar.c @@ -11,12 +11,11 @@ #include #include -#include "config.h" - // Here be dragons -#define MAX(a,b) ((a) > (b) ? (a) : (b)) -#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define max(a,b) ((a) > (b) ? (a) : (b)) +#define min(a,b) ((a) < (b) ? (a) : (b)) +#define indexof(c,s) (strchr(s,c)-s) typedef struct font_t { xcb_font_t ptr; @@ -34,17 +33,9 @@ typedef struct monitor_t { struct monitor_t *prev, *next; } monitor_t; -static struct config_t { - int place_bottom; - int force_docking; - int permanent; - int width; - int height; - float alpha; - char main_font[64]; - char alt_font[64]; -} cfg = { - 0, 0, 0, -1, 18, 1.0f, "fixed", "fixed", +enum { + ATTR_OVERL = (1<<0), + ATTR_UNDERL = (1<<1), }; enum { @@ -54,45 +45,36 @@ enum { }; static xcb_connection_t *c; -static xcb_screen_t *scr; - -static xcb_drawable_t canvas; -static xcb_gcontext_t draw_gc, clear_gc, underl_gc; - +static xcb_screen_t *scr; +static xcb_drawable_t canvas; +static xcb_gcontext_t gc[3]; static monitor_t *monhead, *montail; static font_t *main_font, *alt_font; - -static const uint32_t palette[] = { - COLOR0,COLOR1,COLOR2,COLOR3,COLOR4,COLOR5,COLOR6,COLOR7,COLOR8,COLOR9,BACKGROUND,FOREGROUND -}; +static uint32_t attrs = 0; +static float ba = 1.0f; +static bool dock = false; +static bool topbar = true; +static int bw = -1, bh = -1; +static char *mfont, *afont; +static uint32_t fgc, bgc, ugc; +static uint32_t dfgc, dbgc; void -set_bg (int i) +update_gc (void) { - xcb_change_gc (c, draw_gc , XCB_GC_BACKGROUND, (const unsigned []){ palette[i] }); - xcb_change_gc (c, clear_gc , XCB_GC_FOREGROUND, (const unsigned []){ palette[i] }); -} - -void -set_fg (int i) -{ - xcb_change_gc (c, draw_gc , XCB_GC_FOREGROUND, (const uint32_t []){ palette[i] }); -} - -void -set_ud (int i) -{ - xcb_change_gc (c, underl_gc, XCB_GC_FOREGROUND, (const uint32_t []){ palette[i] }); + xcb_change_gc(c, gc[0], XCB_GC_BACKGROUND | XCB_GC_FOREGROUND, (const uint32_t []){ fgc, bgc }); + xcb_change_gc(c, gc[1], XCB_GC_FOREGROUND, (const uint32_t []){ bgc }); + xcb_change_gc(c, gc[2], XCB_GC_FOREGROUND, (const uint32_t []){ ugc }); } void fill_rect (xcb_gcontext_t gc, int x, int y, int width, int height) { - xcb_poly_fill_rectangle (c, canvas, gc, 1, (const xcb_rectangle_t []){ { x, y, width, height } }); + xcb_poly_fill_rectangle(c, canvas, gc, 1, (const xcb_rectangle_t []){ { x, y, width, height } }); } int -draw_char (monitor_t *mon, font_t *cur_font, int x, int align, int underline, uint16_t ch) +draw_char (monitor_t *mon, font_t *cur_font, int x, int align, uint16_t ch) { /* In the unlikely case that the font doesn't have the glyph wanted just do nothing */ if (ch < cur_font->char_min || ch > cur_font->char_max) @@ -102,119 +84,165 @@ draw_char (monitor_t *mon, font_t *cur_font, int x, int align, int underline, ui switch (align) { case ALIGN_C: - xcb_copy_area (c, canvas, canvas, draw_gc, mon->width / 2 - x / 2 + mon->x, 0, - mon->width / 2 - (x + ch_width) / 2 + mon->x, 0, x, cfg.height); + xcb_copy_area(c, canvas, canvas, gc[0], mon->width / 2 - x / 2 + mon->x, 0, + mon->width / 2 - (x + ch_width) / 2 + mon->x, 0, x, bh); x = mon->width / 2 - (x + ch_width) / 2 + x; break; case ALIGN_R: - xcb_copy_area (c, canvas, canvas, draw_gc, mon->width - x + mon->x, 0, - mon->width - x - ch_width + mon->x, 0, x, cfg.height); + xcb_copy_area(c, canvas, canvas, gc[0], mon->width - x + mon->x, 0, + mon->width - x - ch_width + mon->x, 0, x, bh); x = mon->width - ch_width; break; } /* Draw the background first */ - fill_rect (clear_gc, x + mon->x, 0, ch_width, cfg.height); + fill_rect(gc[1], x + mon->x, 0, ch_width, bh); /* xcb accepts string in UCS-2 BE, so swap */ ch = (ch >> 8) | (ch << 8); /* String baseline coordinates */ - xcb_image_text_16 (c, 1, canvas, draw_gc, x + mon->x, cfg.height / 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); - /* Draw the underline if -1, an overline if 1 */ - if (BAR_UNDERLINE_HEIGHT && underline) - fill_rect (underl_gc, x + mon->x, (underline < 0)*(cfg.height-BAR_UNDERLINE_HEIGHT), ch_width, BAR_UNDERLINE_HEIGHT); + /* We can render both at the same time */ + if (attrs & ATTR_OVERL) + fill_rect(gc[2], x + mon->x, 0, ch_width, 1); + if (attrs & ATTR_UNDERL) + fill_rect(gc[2], x + mon->x, bh-1, ch_width, 1); return ch_width; } +uint32_t +parse_color (const char *str, char **end, const uint32_t def) +{ + xcb_alloc_named_color_reply_t *nc_reply; + int str_len; + uint32_t ret; + + if (!str) + return def; + + /* Reset */ + if (str[0] == '-') { + if (end) + *end = (char *)str + 1; + return def; + } + + /* Hex rapresentation */ + if (str[0] == '#') + return strtoul(str + 1, end, 16); + + /* Actual color name, resolve it */ + str_len = 0; + while (isalpha(str[str_len])) + str_len++; + + nc_reply = xcb_alloc_named_color_reply(c, xcb_alloc_named_color(c, scr->default_colormap, str_len, str), NULL); + + if (!nc_reply) + fprintf(stderr, "Could not alloc color \"%.*s\"\n", str_len, str); + ret = (nc_reply) ? nc_reply->pixel : def; + free(nc_reply); + + printf("k?\n"); + + if (end) + *end = (char *)str + str_len; + + return ret; +} + + +void +set_attribute (const char modifier, const char attribute) +{ + int pos = indexof(attribute, "ou"); + + if (pos < 0) { + fprintf(stderr, "Invalid attribute \"%c\" found\n", attribute); + return; + } + + switch (modifier) { + case '+': attrs |= (1<next) - cur_mon = cur_mon->next; - } else if (*p == 'p') { - if (cur_mon->prev) - cur_mon = cur_mon->prev; - } else if (isdigit(*p)) { - cur_mon = monhead; - /* Start at 0 */ + switch (*p++) { + case '+': set_attribute('+', *p++); break; + case '-': set_attribute('-', *p++); break; + case '!': set_attribute('!', *p++); break; + + case 'R': + tmp = fgc; + fgc = bgc; + bgc = tmp; + update_gc(); + break; + + case 'l': pos_x = 0; align = ALIGN_L; break; + case 'c': pos_x = 0; align = ALIGN_C; break; + case 'r': pos_x = 0; align = ALIGN_R; 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 'S': + if (*p == '+' && cur_mon->next) + { cur_mon = cur_mon->next; pos_x = 0; } + if (*p == '-' && cur_mon->prev) + { cur_mon = cur_mon->prev; pos_x = 0; } + if (*p == 'f') + { cur_mon = monhead; pos_x = 0; } + if (*p == 'l') + { cur_mon = montail ? montail : monhead; pos_x = 0; } + if (isdigit(*p)) + { cur_mon = monhead; for (int i = 0; i != *p-'0' && cur_mon->next; i++) cur_mon = cur_mon->next; - } - } else { - p++; - break; - } + } + p++; + break; - /* Consume the argument */ - p++; - - align = ALIGN_L; - pos_x = 0; - break; - case 'l': - align = ALIGN_L; - pos_x = 0; - break; - case 'c': - align = ALIGN_C; - pos_x = 0; - break; - case 'r': - align = ALIGN_R; - pos_x = 0; - break; + /* In case of error keep parsing after the closing } */ + default: + p = end; } + } + /* Eat the trailing } */ + p++; } else { /* utf-8 -> ucs-2 */ uint8_t *utf = (uint8_t *)p; uint16_t ucs; @@ -239,26 +267,26 @@ parse (char *text) /* If the character is outside the main font charset use the alternate font */ cur_font = (ucs < main_font->char_min || ucs > main_font->char_max) ? alt_font : main_font; - xcb_change_gc (c, draw_gc , XCB_GC_FONT, (const uint32_t []){ cur_font->ptr }); + xcb_change_gc(c, gc[0] , XCB_GC_FONT, (const uint32_t []){ cur_font->ptr }); - pos_x += draw_char (cur_mon, cur_font, pos_x, align, underline_flag, ucs); + pos_x += draw_char(cur_mon, cur_font, pos_x, align, ucs); } } } font_t * -font_load (char *font_string) +font_load (const char *str) { xcb_query_font_cookie_t queryreq; xcb_query_font_reply_t *font_info; xcb_void_cookie_t cookie; xcb_font_t font; - font = xcb_generate_id (c); + font = xcb_generate_id(c); - cookie = xcb_open_font_checked (c, font, strlen (font_string), font_string); + cookie = xcb_open_font_checked(c, font, strlen(str), str); if (xcb_request_check (c, cookie)) { - fprintf (stderr, "Could not load font %s\n", font_string); + fprintf(stderr, "Could not load font %s\n", str); return NULL; } @@ -267,15 +295,15 @@ font_load (char *font_string) if (!ret) return NULL; - queryreq = xcb_query_font (c, font); - font_info = xcb_query_font_reply (c, queryreq, NULL); + queryreq = xcb_query_font(c, font); + font_info = xcb_query_font_reply(c, queryreq, NULL); ret->ptr = font; - ret->width_lut = xcb_query_font_char_infos (font_info); ret->descent = font_info->font_descent; + ret->height = font_info->font_ascent + font_info->font_descent; 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; - ret->height = font_info->font_ascent + font_info->font_descent; + ret->width_lut = xcb_query_font_char_infos(font_info); return ret; } @@ -328,22 +356,22 @@ set_ewmh_atoms (void) /* Prepare the strut array */ for (monitor_t *mon = monhead; mon; mon = mon->next) { int strut[12] = {0}; - if (cfg.place_bottom) { - strut[3] = cfg.height; - strut[10] = mon->x; - strut[11] = mon->x + mon->width; - } else { - strut[2] = cfg.height; + if (topbar) { + strut[2] = bh; strut[8] = mon->x; strut[9] = mon->x + mon->width; + } else { + strut[3] = bh; + strut[10] = mon->x; + strut[11] = mon->x + mon->width; } - xcb_change_property (c, XCB_PROP_MODE_REPLACE, mon->window, atom_list[NET_WM_WINDOW_OPACITY], XCB_ATOM_CARDINAL, 32, 1, (const uint32_t []){ (uint32_t)(cfg.alpha * 0xffffffff) } ); - xcb_change_property (c, XCB_PROP_MODE_REPLACE, mon->window, atom_list[NET_WM_WINDOW_TYPE], XCB_ATOM_ATOM, 32, 1, &atom_list[NET_WM_WINDOW_TYPE_DOCK]); - xcb_change_property (c, XCB_PROP_MODE_APPEND, mon->window, atom_list[NET_WM_STATE], XCB_ATOM_ATOM, 32, 2, &atom_list[NET_WM_STATE_STICKY]); - xcb_change_property (c, XCB_PROP_MODE_REPLACE, mon->window, atom_list[NET_WM_DESKTOP], XCB_ATOM_CARDINAL, 32, 1, (const uint32_t []){ -1 } ); - xcb_change_property (c, XCB_PROP_MODE_REPLACE, mon->window, atom_list[NET_WM_STRUT_PARTIAL], XCB_ATOM_CARDINAL, 32, 12, strut); - xcb_change_property (c, XCB_PROP_MODE_REPLACE, mon->window, atom_list[NET_WM_STRUT], XCB_ATOM_CARDINAL, 32, 4, strut); + xcb_change_property(c, XCB_PROP_MODE_REPLACE, mon->window, atom_list[NET_WM_WINDOW_OPACITY], XCB_ATOM_CARDINAL, 32, 1, (const uint32_t []){ (uint32_t)(ba * 0xffffffff) } ); + xcb_change_property(c, XCB_PROP_MODE_REPLACE, mon->window, atom_list[NET_WM_WINDOW_TYPE], XCB_ATOM_ATOM, 32, 1, &atom_list[NET_WM_WINDOW_TYPE_DOCK]); + xcb_change_property(c, XCB_PROP_MODE_APPEND, mon->window, atom_list[NET_WM_STATE], XCB_ATOM_ATOM, 32, 2, &atom_list[NET_WM_STATE_STICKY]); + xcb_change_property(c, XCB_PROP_MODE_REPLACE, mon->window, atom_list[NET_WM_DESKTOP], XCB_ATOM_CARDINAL, 32, 1, (const uint32_t []){ -1 } ); + xcb_change_property(c, XCB_PROP_MODE_REPLACE, mon->window, atom_list[NET_WM_STRUT_PARTIAL], XCB_ATOM_CARDINAL, 32, 12, strut); + xcb_change_property(c, XCB_PROP_MODE_REPLACE, mon->window, atom_list[NET_WM_STRUT], XCB_ATOM_CARDINAL, 32, 4, strut); } } @@ -362,17 +390,17 @@ monitor_new (int x, int y, int width, int height) ret->width = width; ret->next = ret->prev = NULL; - int win_y = (cfg.place_bottom ? (height - cfg.height) : 0) + y; + int win_y = (topbar ? 0 : height - bh) + y; ret->window = xcb_generate_id(c); xcb_create_window(c, XCB_COPY_FROM_PARENT, ret->window, scr->root, - x, win_y, width, cfg.height, 0, + x, win_y, width, bh, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, scr->root_visual, - XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK, - (const uint32_t []){ palette[10], XCB_EVENT_MASK_EXPOSURE }); + XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK, + (const uint32_t []){ bgc, XCB_EVENT_MASK_EXPOSURE }); - xcb_change_window_attributes (c, ret->window, XCB_CW_OVERRIDE_REDIRECT, - (const uint32_t []){ cfg.force_docking }); + xcb_change_window_attributes(c, ret->window, XCB_CW_OVERRIDE_REDIRECT, + (const uint32_t []){ dock }); return ret; } @@ -408,158 +436,146 @@ rect_sort_cb (const void *p1, const void *p2) } void -get_randr_outputs(void) +get_randr_monitors (void) { xcb_generic_error_t *err; - xcb_randr_get_screen_resources_current_cookie_t rres_query; xcb_randr_get_screen_resources_current_reply_t *rres_reply; xcb_randr_output_t *outputs; - xcb_timestamp_t config_timestamp; - int i, j, num, cnt = 0; - int width = cfg.width; + int num, valid = 0; + int width = bw; - rres_query = xcb_randr_get_screen_resources_current(c, scr->root); - rres_reply = xcb_randr_get_screen_resources_current_reply(c, rres_query, &err); - if (rres_reply == NULL || err != NULL) { + rres_reply = xcb_randr_get_screen_resources_current_reply(c, + xcb_randr_get_screen_resources_current(c, scr->root), NULL); + + if (!rres_reply) { fprintf(stderr, "Failed to get current randr screen resources\n"); - free(rres_reply); return; } num = xcb_randr_get_screen_resources_current_outputs_length(rres_reply); outputs = xcb_randr_get_screen_resources_current_outputs(rres_reply); - config_timestamp = rres_reply->config_timestamp; - - if (num < 1) { - fprintf(stderr, "Failed to get current randr outputs\n"); - free(rres_reply); - return; - } - - xcb_rectangle_t rects[num]; - - /* get all outputs */ - for (i = 0; i < num; i++) { - xcb_randr_get_output_info_cookie_t output_query; - xcb_randr_get_output_info_reply_t *output_reply; - xcb_randr_get_crtc_info_cookie_t crtc_query; - xcb_randr_get_crtc_info_reply_t *crtc_reply; - - output_query = xcb_randr_get_output_info(c, outputs[i], config_timestamp); - output_reply = xcb_randr_get_output_info_reply(c, output_query, &err); - if (err != NULL || output_reply == NULL || output_reply->crtc == XCB_NONE) { - rects[i].width = 0; - continue; - } - crtc_query = xcb_randr_get_crtc_info(c, output_reply->crtc, config_timestamp); - crtc_reply = xcb_randr_get_crtc_info_reply(c, crtc_query, &err); - if (err != NULL | crtc_reply == NULL) { - fprintf(stderr, "Failed to get randr ctrc info\n"); - rects[i].width = 0; - free(output_reply); - continue; - } - - if (crtc_reply->rotation&(XCB_RANDR_ROTATION_ROTATE_90|XCB_RANDR_ROTATION_ROTATE_270)) - rects[i] = (xcb_rectangle_t){ crtc_reply->x, crtc_reply->y, crtc_reply->height, crtc_reply->width }; - else - rects[i] = (xcb_rectangle_t){ crtc_reply->x, crtc_reply->y, crtc_reply->width, crtc_reply->height }; - - free(crtc_reply); - free(output_reply); - cnt++; - } free(rres_reply); - if (cnt < 1) { - fprintf(stderr, "No usable randr outputs\n"); + /* There should be at least one output */ + if (num < 1) return; + + xcb_rectangle_t rects[num]; + + /* Get all outputs */ + for (int i = 0; i < num; i++) { + xcb_randr_get_output_info_reply_t *oi_reply; + xcb_randr_get_crtc_info_reply_t *ci_reply; + + oi_reply = xcb_randr_get_output_info_reply(c, xcb_randr_get_output_info(c, outputs[i], XCB_CURRENT_TIME), NULL); + + /* Output disconnected or not attached to any CRTC ? */ + if (!oi_reply || oi_reply->crtc == XCB_NONE) { + rects[i].width = 0; + continue; + } + + ci_reply = xcb_randr_get_crtc_info_reply(c, + xcb_randr_get_crtc_info(c, oi_reply->crtc, XCB_CURRENT_TIME), NULL); + + free(oi_reply); + + if (!ci_reply) { + fprintf(stderr, "Failed to get RandR ctrc info\n"); + return; + } + + if (ci_reply->rotation & (XCB_RANDR_ROTATION_ROTATE_90|XCB_RANDR_ROTATION_ROTATE_270)) + rects[i] = (xcb_rectangle_t){ ci_reply->x, ci_reply->y, ci_reply->height, ci_reply->width }; + else + rects[i] = (xcb_rectangle_t){ ci_reply->x, ci_reply->y, ci_reply->width, ci_reply->height }; + + free(ci_reply); + + valid++; } - /* check for clones and inactive outputs */ - for (i = 0; i < num; i++) { + /* Check for clones and inactive outputs */ + for (int i = 0; i < num; i++) { if (rects[i].width == 0) continue; - for (j = 0; j < num; j++) { + for (int j = 0; j < num; j++) { /* Does I countain J ? */ if (i != j && rects[j].width) { if (rects[j].x >= rects[i].x && rects[j].x <= rects[i].x + rects[j].width && rects[j].y >= rects[i].y && rects[j].y <= rects[i].y + rects[i].height) { rects[j].width = 0; - cnt--; + valid--; } } } } - if (cnt < 1) { - fprintf(stderr, "No usable randr outputs\n"); + if (valid < 1) { + fprintf(stderr, "No usable RandR output found\n"); return; } - /* Sort by X */ - qsort(rects, cnt, sizeof(xcb_rectangle_t), rect_sort_cb); + /* Sort before use */ + qsort(rects, num, sizeof(xcb_rectangle_t), rect_sort_cb); - for (i = j = 0; i < num && j < cnt; i++) { + for (int i = 0; i < num; i++) { if (rects[i].width) { - monitor_t *mon = monitor_new ( + monitor_t *mon = monitor_new( rects[i].x, rects[i].y, - MIN(width, rects[i].width), + min(width, rects[i].width), rects[i].height); - monitor_add (mon); + monitor_add(mon); width -= rects[i].width; /* No need to check for other monitors */ if (width <= 0) break; - j++; } } } void -get_xinerama_screens (void) +get_xinerama_monitors (void) { xcb_xinerama_query_screens_reply_t *xqs_reply; xcb_xinerama_screen_info_iterator_t iter; - int width = cfg.width; - int i, screens; + int screens, width = bw; - xqs_reply = xcb_xinerama_query_screens_reply (c, - xcb_xinerama_query_screens_unchecked (c), NULL); + xqs_reply = xcb_xinerama_query_screens_reply(c, + xcb_xinerama_query_screens_unchecked(c), NULL); - iter = xcb_xinerama_query_screens_screen_info_iterator (xqs_reply); + iter = xcb_xinerama_query_screens_screen_info_iterator(xqs_reply); + screens = iter.rem; - xcb_rectangle_t rects[iter.rem]; + xcb_rectangle_t rects[screens]; /* Fetch all the screens first */ - for (i = 0; iter.rem; i++) { + for (int i = 0; iter.rem; i++) { rects[i].x = iter.data->x_org; rects[i].y = iter.data->y_org; rects[i].width = iter.data->width; rects[i].height = iter.data->height; - xcb_xinerama_screen_info_next (&iter); + xcb_xinerama_screen_info_next(&iter); } - screens = i; - - /* Sort by X */ + /* Sort before use */ qsort(rects, screens, sizeof(xcb_rectangle_t), rect_sort_cb); /* The width is consumed across all the screens */ - for (i = 0; i < screens; i++) { - monitor_t *mon = monitor_new ( + for (int i = 0; i < screens; i++) { + monitor_t *mon = monitor_new( rects[i].x, rects[i].y, - MIN(width, rects[i].width), + min(width, rects[i].width), rects[i].height); - monitor_add (mon); + monitor_add(mon); width -= rects[i].width; @@ -568,94 +584,104 @@ get_xinerama_screens (void) break; } - free (xqs_reply); + free(xqs_reply); +} + +void +xconn (void) +{ + /* Connect to X */ + c = xcb_connect (NULL, NULL); + if (xcb_connection_has_error(c)) { + fprintf(stderr, "Couldn't connect to X\n"); + exit(EXIT_FAILURE); + } + + /* Grab infos from the first screen */ + scr = xcb_setup_roots_iterator(xcb_get_setup(c)).data; } void init (void) { - /* Connect to X */ - c = xcb_connect (NULL, NULL); - if (xcb_connection_has_error (c)) { - fprintf (stderr, "Couldn't connect to X\n"); - exit (EXIT_FAILURE); - } - - /* Grab infos from the first screen */ - scr = xcb_setup_roots_iterator (xcb_get_setup (c)).data; - /* If I fits I sits */ - if (cfg.width < 0 || cfg.width > scr->width_in_pixels) - cfg.width = scr->width_in_pixels; + if (bw < 0 || bw > scr->width_in_pixels) + bw = scr->width_in_pixels; /* Load the fonts */ - main_font = font_load((char *)cfg.main_font); + main_font = font_load(mfont ? mfont : "fixed"); if (!main_font) exit(EXIT_FAILURE); - alt_font = font_load((char *)cfg.alt_font); + alt_font = font_load(afont ? afont : "fixed"); if (!alt_font) exit(EXIT_FAILURE); /* To make the alignment uniform */ - main_font->height = alt_font->height = MAX(main_font->height, alt_font->height); + main_font->height = alt_font->height = max(main_font->height, alt_font->height); + + /* Adjust the height */ + if (bh < 0 || bh > scr->height_in_pixels) + bh = main_font->height + 1; /* Generate a list of screens */ const xcb_query_extension_reply_t *qe_reply; - int width = cfg.width; /* Initialiaze monitor list head and tail */ monhead = montail = NULL; /* Check if RandR is present */ - qe_reply = xcb_get_extension_data (c, &xcb_randr_id); + qe_reply = xcb_get_extension_data(c, &xcb_randr_id); if (qe_reply && qe_reply->present) { - get_randr_outputs (); + get_randr_monitors(); } else { - qe_reply = xcb_get_extension_data (c, &xcb_xinerama_id); + qe_reply = xcb_get_extension_data(c, &xcb_xinerama_id); /* Check if Xinerama extension is present and active */ if (qe_reply && qe_reply->present) { xcb_xinerama_is_active_reply_t *xia_reply; - xia_reply = xcb_xinerama_is_active_reply (c, xcb_xinerama_is_active (c), NULL); + xia_reply = xcb_xinerama_is_active_reply(c, xcb_xinerama_is_active(c), NULL); if (xia_reply && xia_reply->state) - get_xinerama_screens (); + get_xinerama_monitors(); - free (xia_reply); + free(xia_reply); } } if (!monhead) /* If no RandR outputs or Xinerama screens, fall back to using whole screen */ - monhead = monitor_new (0, 0, width, scr->height_in_pixels); + monhead = monitor_new(0, 0, bw, scr->height_in_pixels); - if (!monhead) + if (!monhead) exit(EXIT_FAILURE); /* For WM that support EWMH atoms */ set_ewmh_atoms(); /* Create a temporary canvas */ - canvas = xcb_generate_id (c); - xcb_create_pixmap (c, scr->root_depth, canvas, scr->root, cfg.width, cfg.height); + canvas = xcb_generate_id(c); + xcb_create_pixmap(c, scr->root_depth, canvas, scr->root, bw, bh); + + /* Default to the classic B/W combo */ + ugc = fgc; /* Create the gc for drawing */ - draw_gc = xcb_generate_id (c); - xcb_create_gc (c, draw_gc, scr->root, XCB_GC_FOREGROUND | XCB_GC_BACKGROUND, (const uint32_t []){ palette[11], palette[10] }); + gc[0] = xcb_generate_id(c); + xcb_create_gc(c, gc[0], scr->root, XCB_GC_FOREGROUND | XCB_GC_BACKGROUND, (const uint32_t []){ fgc, bgc }); - clear_gc = xcb_generate_id (c); - xcb_create_gc (c, clear_gc, scr->root, XCB_GC_FOREGROUND, (const uint32_t []){ palette[10] }); + gc[1] = xcb_generate_id(c); + xcb_create_gc(c, gc[1], scr->root, XCB_GC_FOREGROUND, (const uint32_t []){ bgc }); - underl_gc = xcb_generate_id (c); - xcb_create_gc (c, underl_gc, scr->root, XCB_GC_FOREGROUND, (const uint32_t []){ palette[10] }); + gc[2] = xcb_generate_id(c); + xcb_create_gc(c, gc[2], scr->root, XCB_GC_FOREGROUND, (const uint32_t []){ bgc }); /* Make the bar visible */ for (monitor_t *mon = monhead; mon; mon = mon->next) xcb_map_window(c, mon->window); - xcb_flush (c); + xcb_flush(c); } void @@ -678,22 +704,22 @@ cleanup (void) } if (canvas) - xcb_free_pixmap (c, canvas); - if (draw_gc) - xcb_free_gc (c, draw_gc); - if (clear_gc) - xcb_free_gc (c, clear_gc); - if (underl_gc) - xcb_free_gc (c, underl_gc); + xcb_free_pixmap(c, canvas); + if (gc[0]) + xcb_free_gc(c, gc[0]); + if (gc[1]) + xcb_free_gc(c, gc[1]); + if (gc[2]) + xcb_free_gc(c, gc[2]); if (c) - xcb_disconnect (c); + xcb_disconnect(c); } void sighandle (int signal) { - if (signal == SIGINT || signal == SIGTERM) - exit (EXIT_SUCCESS); + if (signal == SIGINT || signal == SIGTERM) + exit(EXIT_SUCCESS); } /* Parse an urxvt-like geometry string {width}x{height}, both the fields are @@ -704,21 +730,21 @@ parse_geometry_string (char *str) char *p, *q; int tmp; - if (!str) + if (!str) return; p = str; tmp = strtoul(p, &q, 10); if (p != q) - cfg.width = tmp; + bw = tmp; /* P now might point to a NULL char, strtoul takes care of that */ p = q + 1; tmp = strtoul(p, &q, 10); if (p != q) - cfg.height = tmp; + bh = tmp; } void @@ -730,102 +756,115 @@ parse_font_list (char *str) return; tok = strtok(str, ","); - if (tok) - strncpy(cfg.main_font, tok, sizeof(cfg.main_font)); + if (tok) + mfont = tok; tok = strtok(NULL, ","); - if (tok) - strncpy(cfg.alt_font, tok, sizeof(cfg.alt_font)); + if (tok) + afont = tok; return; } -int +int main (int argc, char **argv) { - char input[1024] = {0, }; - struct pollfd pollin[2] = { - { .fd = STDIN_FILENO, .events = POLLIN }, - { .fd = -1 , .events = POLLIN }, + 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; + bool permanent = false; + + /* Install the parachute! */ + atexit(cleanup); + signal(SIGINT, sighandle); + signal(SIGTERM, sighandle); + + /* Connect to the Xserver and initialize scr */ + xconn(); + + /* B/W combo */ + dbgc = bgc = scr->black_pixel; + dfgc = fgc = scr->white_pixel; char ch; - while ((ch = getopt (argc, argv, "hg:bdf:a:o:p")) != -1) { + while ((ch = getopt(argc, argv, "hg:bdf:a:pB:F:")) != -1) { switch (ch) { - case 'h': - printf ("usage: %s [-h | -g | -b | -d | -f | -a | -o | -p ]\n" + case 'h': + printf ("usage: %s [-h | -g | -b | -d | -f | -a | -p | -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" "\t-d Force docking (use this if your WM isn't EWMH compliant)\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-p Don't close after the data ends\n", argv[0]); + "\t-p Don't close after the data ends\n" + "\t-B Set background color in #RRGGBB\n" + "\t-F Set foreground color in #RRGGBB\n", argv[0]); exit (EXIT_SUCCESS); - case 'a': cfg.alpha = strtof(optarg, NULL); break; + case 'a': ba = strtof(optarg, NULL); break; case 'g': parse_geometry_string(optarg); break; - case 'p': cfg.permanent = 1; break; - case 'b': cfg.place_bottom = 1; break; - case 'd': cfg.force_docking = 1; break; + case 'p': permanent = true; break; + case 'b': topbar = false; break; + case 'd': dock = true; break; case 'f': parse_font_list(optarg); 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; } } /* Sanitize the arguments */ - if (cfg.alpha > 1.0f) - cfg.alpha = 1.0f; - if (cfg.alpha < 0.0f) - cfg.alpha = 0.0f; - - atexit (cleanup); - signal (SIGINT, sighandle); - signal (SIGTERM, sighandle); - init (); + if (ba > 1.0f) + ba = 1.0f; + if (ba < 0.0f) + ba = 0.0f; + /* Do the heavy lifting */ + init(); /* Get the fd to Xserver */ - pollin[1].fd = xcb_get_file_descriptor (c); - - fill_rect (clear_gc, 0, 0, cfg.width, cfg.height); + pollin[1].fd = xcb_get_file_descriptor(c); + /* Clear the bar */ + fill_rect(gc[1], 0, 0, bw, bh); for (;;) { - int redraw = 0; + bool redraw = false; - if (poll (pollin, 2, -1) > 0) { - if (pollin[0].revents & POLLHUP) { /* No more data... */ - if (cfg.permanent) pollin[0].fd = -1; /* ...null the fd and continue polling :D */ - else break; /* ...bail out */ + if (poll(pollin, 2, -1) > 0) { + if (pollin[0].revents & POLLHUP) { /* No more data... */ + if (permanent) pollin[0].fd = -1; /* ...null the fd and continue polling :D */ + else break; /* ...bail out */ } if (pollin[0].revents & POLLIN) { /* New input, process it */ - if (fgets (input, sizeof(input), stdin) == NULL) + if (fgets(input, sizeof(input), stdin) == NULL) break; /* EOF received */ - parse (input); - redraw = 1; + parse(input); + redraw = true; } if (pollin[1].revents & POLLIN) { /* Xserver broadcasted an event */ - while ((ev = xcb_poll_for_event (c))) { + while ((ev = xcb_poll_for_event(c))) { expose_ev = (xcb_expose_event_t *)ev; switch (ev->response_type & 0x7F) { - case XCB_EXPOSE: - if (expose_ev->count == 0) redraw = 1; + case XCB_EXPOSE: + if (expose_ev->count == 0) redraw = true; break; } - free (ev); + free(ev); } } } if (redraw) { /* Copy our temporary pixmap onto the window */ for (monitor_t *mon = monhead; mon; mon = mon->next) { - xcb_copy_area (c, canvas, mon->window, draw_gc, mon->x, 0, 0, 0, mon->width, cfg.height); + xcb_copy_area(c, canvas, mon->window, gc[0], mon->x, 0, 0, 0, mon->width, bh); } } - xcb_flush (c); + xcb_flush(c); } return 0; diff --git a/config.def.h b/config.def.h deleted file mode 100644 index b3edf4d..0000000 --- a/config.def.h +++ /dev/null @@ -1,15 +0,0 @@ -/* The thickness of the underline (in pixels). Set to 0 to disable. */ -#define BAR_UNDERLINE_HEIGHT 2 -/* Color palette */ -#define BACKGROUND 0x232c31 -#define COLOR0 0x2d3c46 -#define COLOR1 0xac4142 -#define COLOR2 0x90a959 -#define COLOR3 0xde935f -#define COLOR4 0x6a9fb5 -#define COLOR5 0xaa759f -#define COLOR6 0x75b5aa -#define COLOR7 0x6c7a80 -#define COLOR8 0x425059 -#define COLOR9 0xcc6666 -#define FOREGROUND 0xc5c8c6