3 Commits
v1.1 ... xbm

Author SHA1 Message Date
LemonBoy
a116c04ed9 p 2015-02-28 17:29:37 +01:00
LemonBoy
9dd806b9fc pppp 2015-02-15 23:49:33 +01:00
LemonBoy
fa9cfcbbd7 pew 2015-02-14 16:43:45 +01:00
4 changed files with 232 additions and 83 deletions

View File

@@ -1,19 +1,11 @@
# This snippet has been shmelessly stol^Hborrowed from thestinger's repose Makefile
VERSION = 1.1
GIT_DESC=$(shell test -d .git && git describe --always 2>/dev/null)
ifneq "$(GIT_DESC)" ""
VERSION=$(GIT_DESC)
endif
CC ?= gcc CC ?= gcc
CFLAGS += -Wall -std=c99 -Os -DVERSION="\"$(VERSION)\"" CFLAGS = -g -Wall -std=c99 -Os
LDFLAGS += -lxcb -lxcb-xinerama -lxcb-randr LDFLAGS = -lxcb -lxcb-xinerama -lxcb-randr
CFDEBUG = -g3 -pedantic -Wall -Wunused-parameter -Wlong-long \ CFDEBUG = -g3 -pedantic -Wall -Wunused-parameter -Wlong-long\
-Wsign-conversion -Wconversion -Wimplicit-function-declaration -Wsign-conversion -Wconversion -Wimplicit-function-declaration
EXEC = lemonbar EXEC = bar
SRCS = lemonbar.c SRCS = bar.c
OBJS = ${SRCS:.c=.o} OBJS = ${SRCS:.c=.o}
PREFIX?=/usr PREFIX?=/usr
@@ -22,7 +14,7 @@ BINDIR=${PREFIX}/bin
all: ${EXEC} all: ${EXEC}
doc: README.pod doc: README.pod
pod2man --section=1 --center="lemonbar Manual" --name "lemonbar" --release="lemonbar $(VERSION)" README.pod > lemonbar.1 pod2man --section=1 --center="bar Manual" --name "bar" --release="bar $(shell git describe --always)" README.pod > bar.1
.c.o: .c.o:
${CC} ${CFLAGS} -o $@ -c $< ${CC} ${CFLAGS} -o $@ -c $<
@@ -37,12 +29,12 @@ clean:
rm -f ./*.o ./*.1 rm -f ./*.o ./*.1
rm -f ./${EXEC} rm -f ./${EXEC}
install: lemonbar doc install: bar doc
install -D -m 755 lemonbar ${DESTDIR}${BINDIR}/lemonbar install -D -m 755 bar ${DESTDIR}${BINDIR}/bar
install -D -m 644 lemonbar.1 ${DESTDIR}${PREFIX}/share/man/man1/lemonbar.1 install -D -m 644 bar.1 ${DESTDIR}${PREFIX}/share/man/man1/bar.1
uninstall: uninstall:
rm -f ${DESTDIR}${BINDIR}/lemonbar rm -f ${DESTDIR}${BINDIR}/bar
rm -f $(DESTDIR)$(PREFIX)/share/man/man1/lemonbar.1 rm -f $(DESTDIR)$(PREFIX)/share/man/man1/bar.1
.PHONY: all debug clean install .PHONY: all debug clean install

View File

@@ -1,16 +1,16 @@
=head1 NAME =head1 NAME
lemonbar - Featherweight lemon-scented bar bar - bar ain't recursive
=for HTML <a href="https://travis-ci.org/LemonBoy/bar"><img src="https://travis-ci.org/LemonBoy/bar.svg?branch=master"></a> =for HTML <a href="https://travis-ci.org/LemonBoy/bar"><img src="https://travis-ci.org/LemonBoy/bar.svg?branch=master"></a>
=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>] I<bar> [-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>]
=head1 DESCRIPTION =head1 DESCRIPTION
B<lemonbar> (formerly known as B<bar>) 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. B<bar> 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 =head1 OPTIONS
@@ -34,11 +34,11 @@ Force docking without asking the window manager. This is needed if the window ma
=item B<-f> I<font> =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). Comma separated list of fonts, bar supports a maximum of five fonts (the limit can be tweaked by changing the MAX_FONT_COUNT parameter in the source).
=item B<-p> =item B<-p>
Make the 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> =item B<-u> I<pixel>
@@ -56,7 +56,7 @@ Set the foreground color of the bar. Accepts the same color formats as B<-B>.
=head1 FORMATTING =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. 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 =over
@@ -86,7 +86,7 @@ Set the text foreground color. The parameter I<color> can be I<-> or a color in
=item B<T>I<index> =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> is a 1-based index of the font list supplied to bar. Any other value (for example I<->) resets 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, bar will fall back to normal behaviour for that character.
=item B<U>I<color> =item B<U>I<color>
@@ -100,13 +100,9 @@ Eg. I<%{A:reboot:} Click here to reboot %{A}>
The I<button> field is optional, it defaults to the left button, and it's a number ranging from 1 to 5 which maps to the left, middle, right, scroll up and scroll down movements. Your mileage may vary. The I<button> field is optional, it defaults to the left button, and it's a number ranging from 1 to 5 which maps to the left, middle, right, scroll up and scroll down movements. Your mileage may vary.
Nested clickable areas can trigger different commands.
Eg. I<%{A:reboot:}%{A3:halt:} Left click to reboot, right click to shutdown %{A}%{A}>
=item B<S>I<dir> =item B<S>I<dir>
Change the monitor the bar is rendered to. I<dir> can be either Change the monitor bar is rendering to. I<dir> can be either
=over =over
@@ -160,7 +156,7 @@ Draw a line under the text.
=head1 OUTPUT =head1 OUTPUT
Clicking on an area makes lemonbar output the command to stdout, followed by a newline, allowing the user to pipe it into a script, execute it or simply ignore it. Simple and powerful, that's it. Clicking on an area makes bar output the command to stdout, followed by a newline, allowing the user to pipe it into a script, execute it or simply ignore it. Simple and powerful, that's it.
=head1 WWW =head1 WWW
@@ -168,7 +164,7 @@ L<git repository|https://github.com/LemonBoy/bar>
=head1 AUTHOR =head1 AUTHOR
2012-2015 (C) The Lemon Man 2012-2014 (C) The Lemon Man
Xinerama support was kindly contributed by Stebalien Xinerama support was kindly contributed by Stebalien

View File

@@ -44,9 +44,9 @@ typedef struct area_t {
typedef union rgba_t { typedef union rgba_t {
struct { struct {
uint8_t b;
uint8_t g;
uint8_t r; uint8_t r;
uint8_t g;
uint8_t b;
uint8_t a; uint8_t a;
}; };
uint32_t v; uint32_t v;
@@ -147,10 +147,10 @@ xcb_void_cookie_t xcb_poly_text_16_simple(xcb_connection_t * c,
uint32_t len, const uint16_t *str) uint32_t len, const uint16_t *str)
{ {
static const xcb_protocol_request_t xcb_req = { static const xcb_protocol_request_t xcb_req = {
5, // count 5, // count
0, // ext 0, // ext
XCB_POLY_TEXT_16, // opcode XCB_POLY_TEXT_16, // opcode
1 // isvoid 1 // isvoid
}; };
struct iovec xcb_parts[7]; struct iovec xcb_parts[7];
uint8_t xcb_lendelta[2]; uint8_t xcb_lendelta[2];
@@ -183,30 +183,141 @@ xcb_void_cookie_t xcb_poly_text_16_simple(xcb_connection_t * c,
return xcb_ret; return xcb_ret;
} }
#define pad_to(x, to) (((x) + (to) - 1) & ~((to) - 1))
typedef struct xbm_t {
int width;
int height;
uint8_t *bits;
size_t bits_size;
} xbm_t;
xbm_t xbm_cache[20];
int xbm_cache_elem = 0;
int
xbm_parse (char *path)
{
FILE *fp;
char line[4096];
int xbm_height, xcb_width;
fp = fopen(path, "r");
if (!fp)
return 0;
while (fgets(line, sizeof(line), fp)) {
}
}
xbm_t *
xbm_load (uint8_t *bits, int width, int height)
{
const xcb_setup_t *setup = xcb_get_setup(c);
uint8_t *buf;
int width_b, line;
xbm_t *ico = &xbm_cache[xbm_cache_elem];
// XCB_IMAGE_ORDER_LSB_FIRST
// bitmap_format_bit_order
width_b = (width + 7) / 8;
line = pad_to(width_b, setup->bitmap_format_scanline_pad >> 3);
buf = malloc(height * line);
if (!buf)
return NULL;
// Despite the naming, this is actually a stencil
for (int i = 0; i < height * width_b; i++)
bits[i] = ~bits[i];
for (int i = 0; i < height; i++)
memcpy(buf + (i * line),
bits + (i * width_b),
width_b);
ico->width = width;
ico->height = height;
ico->bits = buf;
ico->bits_size = height * line;
xbm_cache_elem++;
return ico;
}
int
pixmap_shift (monitor_t *mon, int x, int align, int w)
{
switch (align) {
case ALIGN_L:
return x;
case ALIGN_C:
xcb_copy_area(c, mon->pixmap, mon->pixmap, gc[GC_DRAW],
mon->width / 2 - x / 2, 0,
mon->width / 2 - (x + w) / 2, 0,
x, bh);
return mon->width / 2 - (x + w) / 2 + x;
case ALIGN_R:
xcb_copy_area(c, mon->pixmap, mon->pixmap, gc[GC_DRAW],
mon->width - x, 0,
mon->width - x - w, 0,
x, bh);
return mon->width - w;
}
return 0;
}
int
draw_icon (monitor_t *mon, int x, xbm_t *icon, int align)
{
xcb_gcontext_t tmp_gc;
xcb_pixmap_t mask;
x = pixmap_shift(mon, x, align, icon->width);
mask = xcb_generate_id(c);
xcb_create_pixmap(c, 1, mask, mon->pixmap, icon->width, icon->height);
tmp_gc = xcb_generate_id(c);
xcb_create_gc(c, tmp_gc, mask, 0, NULL);
xcb_put_image(
c,
XCB_IMAGE_FORMAT_XY_BITMAP,
mask,
tmp_gc,
icon->width, icon->height,
0, 0,
0, 1,
icon->bits_size, icon->bits);
xcb_free_gc(c, tmp_gc);
xcb_change_gc(c, gc[GC_DRAW], XCB_GC_CLIP_MASK, (const uint32_t []){ mask });
xcb_change_gc(c, gc[GC_DRAW], XCB_GC_CLIP_ORIGIN_X | XCB_GC_CLIP_ORIGIN_Y,
(const uint32_t []){ x, bh / 2 - icon->height / 2 });
xcb_poly_fill_rectangle(c, mon->pixmap, gc[GC_DRAW], 1,
(const xcb_rectangle_t []){ { 0, 0, bw, bh } });
xcb_change_gc(c, gc[GC_DRAW], XCB_GC_CLIP_MASK, (const uint32_t []){ XCB_NONE });
xcb_free_pixmap(c, mask);
return icon->width;
}
int int
draw_char (monitor_t *mon, font_t *cur_font, int x, int align, uint16_t ch) draw_char (monitor_t *mon, font_t *cur_font, int x, int align, uint16_t ch)
{ {
int ch_width = cur_font->width_lut[ch - cur_font->char_min].character_width; int ch_width = cur_font->width_lut[ch - cur_font->char_min].character_width;
switch (align) { x = pixmap_shift(mon, x, align, ch_width);
case ALIGN_C:
xcb_copy_area(c, mon->pixmap, mon->pixmap, gc[GC_DRAW],
mon->width / 2 - x / 2, 0,
mon->width / 2 - (x + ch_width) / 2, 0,
x, bh);
x = mon->width / 2 - (x + ch_width) / 2 + x;
break;
case ALIGN_R:
xcb_copy_area(c, mon->pixmap, mon->pixmap, gc[GC_DRAW],
mon->width - x, 0,
mon->width - x - ch_width, 0,
x, bh);
x = mon->width - ch_width;
break;
}
// Draw the background first // Draw the background first
fill_rect(mon->pixmap, gc[GC_CLEAR], x, 0, ch_width, bh); fill_rect(mon->pixmap, gc[GC_CLEAR], x, by, ch_width, bh);
// xcb accepts string in UCS-2 BE, so swap // xcb accepts string in UCS-2 BE, so swap
ch = (ch >> 8) | (ch << 8); ch = (ch >> 8) | (ch << 8);
@@ -225,6 +336,14 @@ draw_char (monitor_t *mon, font_t *cur_font, int x, int align, uint16_t ch)
return ch_width; return ch_width;
} }
static unsigned char fox_bits[] = {
0x70, 0x88, 0xC4, 0xE2, 0x7E, 0x32, 0x09, 0x07
0x81, 0xC3, 0xBD, 0xFF, 0x99, 0xDB, 0x7E, 0x18,
};
int xbm_w = 8;
int xbm_h = 8;
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)
{ {
@@ -285,7 +404,7 @@ parse_color (const char *str, char **end, const rgba_t def)
nc_reply = xcb_alloc_named_color_reply(c, xcb_alloc_named_color(c, colormap, string_len, str), NULL); nc_reply = xcb_alloc_named_color_reply(c, xcb_alloc_named_color(c, colormap, string_len, str), NULL);
if (!nc_reply) if (!nc_reply)
fprintf(stderr, "Could not allocate the color \"%.*s\"\n", string_len, str); fprintf(stderr, "Could not alloc color \"%.*s\"\n", string_len, str);
ret = nc_reply? ret = nc_reply?
(rgba_t)nc_reply->pixel: (rgba_t)nc_reply->pixel:
@@ -340,10 +459,9 @@ area_shift (xcb_window_t win, const int align, int delta)
delta /= 2; delta /= 2;
for (int i = 0; i < astack.pos; i++) { for (int i = 0; i < astack.pos; i++) {
area_t *a = &astack.slot[i]; if (astack.slot[i].window == win && astack.slot[i].align == align) {
if (a->window == win && a->align == align && !a->active) { astack.slot[i].begin -= delta;
a->begin -= delta; astack.slot[i].end -= delta;
a->end -= delta;
} }
} }
} }
@@ -446,7 +564,7 @@ select_drawable_font (const uint16_t c)
if (font_index != -1 && font_has_glyph(font_list[font_index - 1], c)) if (font_index != -1 && font_has_glyph(font_list[font_index - 1], c))
return font_list[font_index - 1]; return font_list[font_index - 1];
// If the end is reached without finding an appropriate font, return NULL. // If the end is reached without finding an apropriate font, return NULL.
// If the font can draw the character, return it. // If the font can draw the character, return it.
for (int i = 0; i < font_count; i++) { for (int i = 0; i < font_count; i++) {
if (font_has_glyph(font_list[i], c)) if (font_has_glyph(font_list[i], c))
@@ -461,7 +579,7 @@ parse (char *text)
font_t *cur_font; font_t *cur_font;
monitor_t *cur_mon; monitor_t *cur_mon;
int pos_x, align, button; int pos_x, align, button;
char *p = text, *block_end, *ep; char *p = text, *end;
rgba_t tmp; rgba_t tmp;
pos_x = 0; pos_x = 0;
@@ -477,8 +595,8 @@ parse (char *text)
if (*p == '\0' || *p == '\n') if (*p == '\0' || *p == '\n')
return; return;
if (*p == '%' && p++ && *p == '{' && (block_end = strchr(p++, '}'))) { if (*p == '%' && p++ && *p == '{' && (end = strchr(p++, '}'))) {
while (p < block_end) { while (p < end) {
while (isspace(*p)) while (isspace(*p))
p++; p++;
@@ -503,7 +621,7 @@ 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); area_add(p, end, &p, cur_mon, pos_x, align, button);
break; break;
case 'B': bgc = parse_color(p, &p, dbgc); update_gc(); break; case 'B': bgc = parse_color(p, &p, dbgc); update_gc(); break;
@@ -532,17 +650,28 @@ parse (char *text)
break; break;
case 'T': case 'T':
font_index = (int)strtoul(p, &ep, 10); font_index = (int)strtoul(p, NULL, 10);
// User-specified 'font_index' ∊ (0,font_count] // User-specified 'font_index' ∊ (0,font_count]
// Otherwise just fallback to the automatic font selection // Otherwise just fallback to the automatic font selection
if (!font_index || font_index > font_count) if (!font_index || font_index > font_count)
font_index = -1; font_index = -1;
p = ep; p = end;
break;
case 'i':
;
int icon_index = (int)strtoul(p, NULL, 10);
if (icon_index < xbm_cache_elem) {
int w = draw_icon(cur_mon, pos_x, &xbm_cache[0], align);
pos_x += w;
area_shift(cur_mon->window, align, w);
}
p = end;
break; break;
// In case of error keep parsing after the closing } // In case of error keep parsing after the closing }
default: default:
p = block_end; p = end;
} }
} }
// Eat the trailing } // Eat the trailing }
@@ -594,8 +723,8 @@ parse (char *text)
xcb_change_gc(c, gc[GC_DRAW] , XCB_GC_FONT, (const uint32_t []){ cur_font->ptr }); xcb_change_gc(c, gc[GC_DRAW] , XCB_GC_FONT, (const uint32_t []){ cur_font->ptr });
int w = draw_char(cur_mon, cur_font, pos_x, align, ucs); int w = draw_char(cur_mon, cur_font, pos_x, align, ucs);
pos_x += w; pos_x += w;
area_shift(cur_mon->window, align, w); area_shift(cur_mon->window, align, w);
} }
} }
@@ -812,9 +941,6 @@ monitor_create_chain (xcb_rectangle_t *rects, const int num)
min(width, rects[i].width - left), min(width, rects[i].width - left),
rects[i].height); rects[i].height);
if (!mon)
break;
monitor_add(mon); monitor_add(mon);
width -= rects[i].width - left; width -= rects[i].width - left;
@@ -899,7 +1025,7 @@ get_randr_monitors (void)
continue; continue;
for (j = 0; j < num; j++) { for (j = 0; j < num; j++) {
// Does I contain J ? // Does I countain J ?
if (i != j && rects[j].width) { if (i != j && rects[j].width) {
if (rects[j].x >= rects[i].x && rects[j].x + rects[j].width <= rects[i].x + rects[i].width && if (rects[j].x >= rects[i].x && rects[j].x + rects[j].width <= rects[i].x + rects[i].width &&
@@ -975,7 +1101,7 @@ get_visual (void)
return scr->root_visual; return scr->root_visual;
} }
// Parse an X-styled geometry string, we don't support signed offsets though. // Parse an X-styled geometry string, we don't support signed offsets tho.
bool bool
parse_geometry_string (char *str, int *tmp) parse_geometry_string (char *str, int *tmp)
{ {
@@ -1066,8 +1192,6 @@ parse_font_list (char *str)
} }
} }
void void
xconn (void) xconn (void)
{ {
@@ -1115,7 +1239,7 @@ init (void)
// Generate a list of screens // Generate a list of screens
const xcb_query_extension_reply_t *qe_reply; const xcb_query_extension_reply_t *qe_reply;
// Initialize monitor list head and tail // Initialiaze monitor list head and tail
monhead = montail = NULL; monhead = montail = NULL;
// Check if RandR is present // Check if RandR is present
@@ -1254,11 +1378,10 @@ main (int argc, char **argv)
while ((ch = getopt(argc, argv, "hg:bdf:a:pu:B:F:")) != -1) { while ((ch = getopt(argc, argv, "hg:bdf:a:pu:B:F:")) != -1) {
switch (ch) { switch (ch) {
case 'h': 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 | -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 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 Bar font list, comma separated\n" "\t-f Bar font list, comma separated\n"
"\t-p Don't close after the data ends\n" "\t-p Don't close after the data ends\n"
@@ -1288,6 +1411,8 @@ main (int argc, char **argv)
// 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);
xbm_load (fox_bits, xbm_w, xbm_h);
for (;;) { for (;;) {
bool redraw = false; bool redraw = false;
@@ -1307,7 +1432,7 @@ main (int argc, char **argv)
parse(input); parse(input);
redraw = true; redraw = true;
} }
if (pollin[1].revents & POLLIN) { // The event comes from the Xorg server 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; expose_ev = (xcb_expose_event_t *)ev;

36
palette.pl Executable file
View File

@@ -0,0 +1,36 @@
#!/usr/bin/env perl
#
# palette.pl
#
# Converts your .Xdefault/.Xresources colors into a ready to paste palette
# for bar. It takes your foreground/background settings into account and if
# it cant find them it leaves COLOR0/COLOR1 undefined.
#
use strict;
use warnings;
open (F, "<".$ARGV[0]) || die "Can't open!";
our %vars = ();
while (<F>) {
# Don't match comments
if ($_ !~ m/^\s*!/) {
# It's a define!
if ($_ =~ m/^\s*#define\s+(\w+)\s+#([0-9A-Fa-f]{1,6})/) {
$vars{"$1"} = hex($2);
}
elsif ($_ =~ m/^\s*\w*\*(background|foreground|color\d)\s*:\s*([\w\d#]+)/) {
my ($name, $value) = (uc $1, $2);
# Check if it's a color
if (substr($value, 0, 1) eq '#') {
$value = hex(substr($value, 1));
} else {
$value = $vars{"$value"};
}
printf "#define $name 0x%06x\n", $value;
}
}
}