Compare commits

..

3 Commits
master ... 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
6 changed files with 377 additions and 307 deletions

3
.gitignore vendored
View File

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

View File

@ -3,8 +3,5 @@ compiler:
- clang - clang
- gcc - gcc
before_install: before_install:
- sudo apt-get update -qq - sudo apt-get install -qq libx11-xcb-dev libxcb-randr0-dev libxcb-xinerama0-dev
- sudo apt-get install -y libx11-xcb-dev libxcb-randr0-dev libxcb-xinerama0-dev
env:
- CFLAGS='-DWITH_XINERAMA=1'
script: make script: make

View File

@ -1,19 +1,11 @@
# This snippet has been shmelessly stol^Hborrowed from thestinger's repose Makefile
VERSION = 1.3
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 | -n I<name> | -u I<pixel> | -B I<color> | -F I<color> | -U 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,20 +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>
Define the font to load into one of the five slots (the number of slots is hardcoded and can be tweaked by 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).
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> =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<-n> I<name>
Set the WM_NAME atom value for the bar.
=item B<-u> I<pixel> =item B<-u> I<pixel>
@ -55,21 +46,17 @@ 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> 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. 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.
=item B<-F> I<color> =item B<-F> I<color>
Set the foreground color of the bar. Accepts the same color formats as B<-B>. 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 =back
=head1 FORMATTING =head1 FORMATTING
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 its best to handle malformed input. Use C<%%> to get a literal percent sign (C<%>). 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
@ -89,10 +76,6 @@ Aligns the following text to the center of the screen.
Aligns the following text to the right side of the screen. Aligns the following text to the right side of the screen.
=item B<O>I<width>
Offset the current position by I<width> pixels in the alignment direction.
=item B<B>I<color> =item B<B>I<color>
Set the text background color. The parameter I<color> can be I<-> or a color in one of the formats mentioned before. The special value I<-> resets the color to the default one. Set the text background color. The parameter I<color> can be I<-> or a color in one of the formats mentioned before. The special value I<-> resets the color to the default one.
@ -103,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> 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 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>
@ -111,19 +94,15 @@ Set the text underline color. The parameter I<color> can be I<-> or a color in o
=item B<A>I<button>:I<command>: =item B<A>I<button>:I<command>:
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. 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.
Eg. I<%{A:reboot:} Click here to reboot %{A}> 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
@ -177,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
@ -185,7 +164,7 @@ L<git repository|https://github.com/LemonBoy/bar>
=head1 AUTHOR =head1 AUTHOR
2012-2017 (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

@ -1,5 +1,4 @@
// vim:sw=4:ts=4:et: // vim:sw=4:ts=4:et:
#define _POSIX_C_SOURCE 200809L
#include <stdbool.h> #include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@ -12,9 +11,7 @@
#include <errno.h> #include <errno.h>
#include <xcb/xcb.h> #include <xcb/xcb.h>
#include <xcb/xcbext.h> #include <xcb/xcbext.h>
#if WITH_XINERAMA
#include <xcb/xinerama.h> #include <xcb/xinerama.h>
#endif
#include <xcb/randr.h> #include <xcb/randr.h>
// Here be dragons // Here be dragons
@ -25,7 +22,7 @@
typedef struct font_t { typedef struct font_t {
xcb_font_t ptr; xcb_font_t ptr;
int descent, height, width; int descent, height;
uint16_t char_max; uint16_t char_max;
uint16_t char_min; uint16_t char_min;
xcb_charinfo_t *width_lut; xcb_charinfo_t *width_lut;
@ -39,28 +36,27 @@ typedef struct monitor_t {
} monitor_t; } monitor_t;
typedef struct area_t { typedef struct area_t {
unsigned int begin:16; bool active;
unsigned int end:16; int begin, end, align, button;
bool active:1;
int align:3;
unsigned int button:3;
xcb_window_t window; xcb_window_t window;
char *cmd; char *cmd;
} area_t; } 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;
} rgba_t; } rgba_t;
#define N 20
typedef struct area_stack_t { typedef struct area_stack_t {
int at, max; int pos;
area_t *area; area_t slot[N];
} area_stack_t; } area_stack_t;
enum { enum {
@ -98,8 +94,8 @@ static bool topbar = true;
static int bw = -1, bh = -1, bx = 0, by = 0; static int bw = -1, bh = -1, bx = 0, by = 0;
static int bu = 1; // Underline height static int bu = 1; // Underline height
static rgba_t fgc, bgc, ugc; static rgba_t fgc, bgc, ugc;
static rgba_t dfgc, dbgc, dugc; static rgba_t dfgc, dbgc;
static area_stack_t area_stack; static area_stack_t astack;
void void
update_gc (void) update_gc (void)
@ -151,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];
@ -184,60 +180,144 @@ 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_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); xcb_ret.sequence = xcb_send_request(c, 0, xcb_parts + 2, &xcb_req);
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 int
shift (monitor_t *mon, int x, int align, int ch_width) 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) { switch (align) {
case ALIGN_L:
return x;
case ALIGN_C: case ALIGN_C:
xcb_copy_area(c, mon->pixmap, mon->pixmap, gc[GC_DRAW], xcb_copy_area(c, mon->pixmap, mon->pixmap, gc[GC_DRAW],
mon->width / 2 - x / 2, 0, mon->width / 2 - x / 2, 0,
mon->width / 2 - (x + ch_width) / 2, 0, mon->width / 2 - (x + w) / 2, 0,
x, bh); x, bh);
x = mon->width / 2 - (x + ch_width) / 2 + x; return mon->width / 2 - (x + w) / 2 + x;
break;
case ALIGN_R: case ALIGN_R:
xcb_copy_area(c, mon->pixmap, mon->pixmap, gc[GC_DRAW], xcb_copy_area(c, mon->pixmap, mon->pixmap, gc[GC_DRAW],
mon->width - x, 0, mon->width - x, 0,
mon->width - x - ch_width, 0, mon->width - x - w, 0,
x, bh); x, bh);
x = mon->width - ch_width; return mon->width - w;
break;
} }
// Draw the background first return 0;
fill_rect(mon->pixmap, gc[GC_CLEAR], x, 0, ch_width, bh);
return x;
} }
void int
draw_lines (monitor_t *mon, int x, int w) draw_icon (monitor_t *mon, int x, xbm_t *icon, int align)
{ {
/* We can render both at the same time */ xcb_gcontext_t tmp_gc;
if (attrs & ATTR_OVERL) xcb_pixmap_t mask;
fill_rect(mon->pixmap, gc[GC_ATTR], x, 0, w, bu);
if (attrs & ATTR_UNDERL)
fill_rect(mon->pixmap, gc[GC_ATTR], x, bh - bu, w, bu);
}
void x = pixmap_shift(mon, x, align, icon->width);
draw_shift (monitor_t *mon, int x, int align, int w)
{ mask = xcb_generate_id(c);
x = shift(mon, x, align, w); xcb_create_pixmap(c, 1, mask, mon->pixmap, icon->width, icon->height);
draw_lines(mon, x, w);
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) ? int ch_width = cur_font->width_lut[ch - cur_font->char_min].character_width;
cur_font->width_lut[ch - cur_font->char_min].character_width:
cur_font->width;
x = shift(mon, x, align, ch_width); x = pixmap_shift(mon, x, align, ch_width);
// Draw the background first
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);
@ -247,15 +327,29 @@ draw_char (monitor_t *mon, font_t *cur_font, int x, int align, uint16_t ch)
x, bh / 2 + cur_font->height / 2 - cur_font->descent, x, bh / 2 + cur_font->height / 2 - cur_font->descent,
1, &ch); 1, &ch);
draw_lines(mon, x, ch_width); // We can render both at the same time
if (attrs & ATTR_OVERL)
fill_rect(mon->pixmap, gc[GC_ATTR], x, 0, ch_width, bu);
if (attrs & ATTR_UNDERL)
fill_rect(mon->pixmap, gc[GC_ATTR], x, bh - bu, ch_width, bu);
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)
{ {
xcb_alloc_named_color_reply_t *nc_reply;
int string_len; int string_len;
rgba_t ret;
char *ep; char *ep;
if (!str) if (!str)
@ -270,61 +364,61 @@ parse_color (const char *str, char **end, const rgba_t def)
} }
// Hex representation // Hex representation
if (str[0] != '#') { if (str[0] == '#') {
errno = 0;
rgba_t tmp = (rgba_t)(uint32_t)strtoul(str + 1, &ep, 16);
if (end) if (end)
*end = (char *)str; *end = ep;
fprintf(stderr, "Invalid color specified\n"); // Some error checking is definitely good
return def; if (errno) {
}
errno = 0;
rgba_t tmp = (rgba_t)(uint32_t)strtoul(str + 1, &ep, 16);
if (end)
*end = ep;
// 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"); fprintf(stderr, "Invalid color specified\n");
return def; 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;
} }
// Premultiply the alpha in // Actual color name, resolve it
if (tmp.a) { for (string_len = 0; isalpha(str[string_len]); string_len++)
// 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; 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 alloc color \"%.*s\"\n", string_len, str);
ret = nc_reply?
(rgba_t)nc_reply->pixel:
def;
free(nc_reply);
if (end)
*end = (char *)str + string_len;
return ret;
} }
void void
set_attribute (const char modifier, const char attribute) set_attribute (const char modifier, const char attribute)
{ {
@ -347,9 +441,10 @@ area_t *
area_get (xcb_window_t win, const int btn, const int x) area_get (xcb_window_t win, const int btn, const int x)
{ {
// Looping backwards ensures that we get the innermost area first // Looping backwards ensures that we get the innermost area first
for (int i = area_stack.at - 1; i >= 0; i--) { for (int i = astack.pos; i >= 0; i--) {
area_t *a = &area_stack.area[i]; area_t *a = &astack.slot[i];
if (a->window == win && a->button == btn && x >= a->begin && x < a->end) if (a->window == win && a->button == btn
&& x >= a->begin && x < a->end)
return a; return a;
} }
return NULL; return NULL;
@ -363,11 +458,10 @@ area_shift (xcb_window_t win, const int align, int delta)
if (align == ALIGN_C) if (align == ALIGN_C)
delta /= 2; delta /= 2;
for (int i = 0; i < area_stack.at; i++) { for (int i = 0; i < astack.pos; i++) {
area_t *a = &area_stack.area[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;
} }
} }
} }
@ -384,15 +478,13 @@ area_add (char *str, const char *optend, char **end, monitor_t *mon, const int x
*end = str; *end = str;
// Find most recent unclosed area. // Find most recent unclosed area.
for (i = area_stack.at - 1; i >= 0 && !area_stack.area[i].active; i--) for (i = astack.pos - 1; i >= 0 && !astack.slot[i].active; i--)
; ;
a = &area_stack.area[i]; a = &astack.slot[i];
// Basic safety checks // 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; return false;
}
const int size = x - a->begin; const int size = x - a->begin;
@ -415,12 +507,11 @@ area_add (char *str, const char *optend, char **end, monitor_t *mon, const int x
return true; return true;
} }
if (area_stack.at + 1 > area_stack.max) { if (astack.pos >= N) {
fprintf(stderr, "Cannot add any more clickable areas (used %d/%d)\n", fprintf(stderr, "astack overflow!\n");
area_stack.at, area_stack.max);
return false; return false;
} }
a = &area_stack.area[area_stack.at++]; a = &astack.slot[astack.pos++];
// Found the closing : and check if it's just an escaped one // Found the closing : and check if it's just an escaped one
for (trail = strchr(++str, ':'); trail && trail[-1] == '\\'; trail = strchr(trail + 1, ':')) for (trail = strchr(++str, ':'); trail && trail[-1] == '\\'; trail = strchr(trail + 1, ':'))
@ -459,13 +550,10 @@ area_add (char *str, const char *optend, char **end, monitor_t *mon, const int x
bool bool
font_has_glyph (font_t *font, const uint16_t c) font_has_glyph (font_t *font, const uint16_t c)
{ {
if (c < font->char_min || c > font->char_max) return (c >= font->char_min &&
return false; c <= font->char_max &&
font->width_lut &&
if (font->width_lut && font->width_lut[c - font->char_min].character_width == 0) font->width_lut[c - font->char_min].character_width);
return false;
return true;
} }
// returns NULL if character cannot be printed // returns NULL if character cannot be printed
@ -476,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))
@ -491,15 +579,14 @@ 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;
align = ALIGN_L; align = ALIGN_L;
cur_mon = monhead; cur_mon = monhead;
// Reset the stack position memset(&astack, 0, sizeof(area_stack_t));
area_stack.at = 0;
for (monitor_t *m = monhead; m != NULL; m = m->next) for (monitor_t *m = monhead; m != NULL; m = m->next)
fill_rect(m->pixmap, gc[GC_CLEAR], 0, 0, m->width, bh); fill_rect(m->pixmap, gc[GC_CLEAR], 0, 0, m->width, bh);
@ -508,10 +595,8 @@ parse (char *text)
if (*p == '\0' || *p == '\n') if (*p == '\0' || *p == '\n')
return; return;
if (p[0] == '%' && p[1] == '{' && (block_end = strchr(p++, '}'))) { if (*p == '%' && p++ && *p == '{' && (end = strchr(p++, '}'))) {
p++; while (p < end) {
while (p < block_end) {
int w;
while (isspace(*p)) while (isspace(*p))
p++; p++;
@ -536,13 +621,12 @@ 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';
if (!area_add(p, block_end, &p, cur_mon, pos_x, align, button)) area_add(p, end, &p, cur_mon, pos_x, align, button);
return;
break; break;
case 'B': bgc = parse_color(p, &p, dbgc); update_gc(); break; case 'B': bgc = parse_color(p, &p, dbgc); update_gc(); break;
case 'F': fgc = parse_color(p, &p, dfgc); update_gc(); break; case 'F': fgc = parse_color(p, &p, dfgc); update_gc(); break;
case 'U': ugc = parse_color(p, &p, dugc); update_gc(); break; case 'U': ugc = parse_color(p, &p, dbgc); update_gc(); break;
case 'S': case 'S':
if (*p == '+' && cur_mon->next) if (*p == '+' && cur_mon->next)
@ -564,48 +648,35 @@ parse (char *text)
p++; p++;
pos_x = 0; pos_x = 0;
break; break;
case 'O':
errno = 0;
w = (int) strtoul(p, &p, 10);
if (errno)
continue;
draw_shift(cur_mon, pos_x, align, w);
pos_x += w;
area_shift(cur_mon->window, align, w);
break;
case 'T': case 'T':
if (*p == '-') { //Reset to automatic font selection font_index = (int)strtoul(p, NULL, 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; font_index = -1;
p++; p = end;
break; break;
} else if (isdigit(*p)) {
font_index = (int)strtoul(p, &ep, 10); case 'i':
// User-specified 'font_index' ∊ (0,font_count] ;
// Otherwise just fallback to the automatic font selection int icon_index = (int)strtoul(p, NULL, 10);
if (!font_index || font_index > font_count) if (icon_index < xbm_cache_elem) {
font_index = -1; int w = draw_icon(cur_mon, pos_x, &xbm_cache[0], align);
p = ep; pos_x += w;
break; area_shift(cur_mon->window, align, w);
} else {
fprintf(stderr, "Invalid font slot \"%c\"\n", *p++); //Swallow the token
break;
} }
p = end;
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 }
p++; p++;
} else { // utf-8 -> ucs-2 } else { // utf-8 -> ucs-2
// Escaped % symbol, eat the first one
if (p[0] == '%' && p[1] == '%')
p++;
uint8_t *utf = (uint8_t *)p; uint8_t *utf = (uint8_t *)p;
uint16_t ucs; uint16_t ucs;
@ -634,7 +705,7 @@ parse (char *text)
ucs = 0xfffd; ucs = 0xfffd;
p += 5; p += 5;
} }
// Six byte utf8 sequence // Siz byte utf8 sequence
else if ((utf[0] & 0xfe) == 0xfc) { else if ((utf[0] & 0xfe) == 0xfc) {
ucs = 0xfffd; ucs = 0xfffd;
p += 6; p += 6;
@ -652,21 +723,16 @@ 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);
} }
} }
} }
void font_t *
font_load (const char *pattern) font_load (const char *str)
{ {
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_cookie_t queryreq;
xcb_query_font_reply_t *font_info; xcb_query_font_reply_t *font_info;
xcb_void_cookie_t cookie; xcb_void_cookie_t cookie;
@ -674,16 +740,16 @@ font_load (const char *pattern)
font = xcb_generate_id(c); font = xcb_generate_id(c);
cookie = xcb_open_font_checked(c, font, strlen(pattern), pattern); cookie = xcb_open_font_checked(c, font, strlen(str), str);
if (xcb_request_check (c, cookie)) { if (xcb_request_check (c, cookie)) {
fprintf(stderr, "Could not load font \"%s\"\n", pattern); fprintf(stderr, "Could not load font \"%s\"\n", str);
return; return NULL;
} }
font_t *ret = calloc(1, sizeof(font_t)); font_t *ret = calloc(1, sizeof(font_t));
if (!ret) if (!ret)
return; return NULL;
queryreq = xcb_query_font(c, font); queryreq = xcb_query_font(c, font);
font_info = xcb_query_font_reply(c, queryreq, NULL); font_info = xcb_query_font_reply(c, queryreq, NULL);
@ -691,7 +757,6 @@ font_load (const char *pattern)
ret->ptr = font; ret->ptr = font;
ret->descent = font_info->font_descent; ret->descent = font_info->font_descent;
ret->height = font_info->font_ascent + 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_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->char_min = font_info->min_byte1 << 8 | font_info->min_char_or_byte2;
@ -704,7 +769,7 @@ font_load (const char *pattern)
free(font_info); free(font_info);
font_list[font_count++] = ret; return ret;
} }
enum { enum {
@ -756,11 +821,11 @@ set_ewmh_atoms (void)
if (topbar) { if (topbar) {
strut[2] = bh; strut[2] = bh;
strut[8] = mon->x; strut[8] = mon->x;
strut[9] = mon->x + mon->width - 1; strut[9] = mon->x + mon->width;
} else { } else {
strut[3] = bh; strut[3] = bh;
strut[10] = mon->x; strut[10] = mon->x;
strut[11] = mon->x + mon->width - 1; strut[11] = mon->x + mon->width;
} }
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_REPLACE, mon->window, atom_list[NET_WM_WINDOW_TYPE], XCB_ATOM_ATOM, 32, 1, &atom_list[NET_WM_WINDOW_TYPE_DOCK]);
@ -824,15 +889,10 @@ rect_sort_cb (const void *p1, const void *p2)
const xcb_rectangle_t *r1 = (xcb_rectangle_t *)p1; const xcb_rectangle_t *r1 = (xcb_rectangle_t *)p1;
const xcb_rectangle_t *r2 = (xcb_rectangle_t *)p2; const xcb_rectangle_t *r2 = (xcb_rectangle_t *)p2;
if (r1->x < r2->x || r1->y + r1->height <= r2->y) if (r1->x < r2->x || r1->y < r2->y)
{
return -1; 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; return 0;
} }
@ -881,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;
@ -968,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 &&
@ -994,7 +1051,6 @@ get_randr_monitors (void)
monitor_create_chain(r, valid); monitor_create_chain(r, valid);
} }
#ifdef WITH_XINERAMA
void void
get_xinerama_monitors (void) get_xinerama_monitors (void)
{ {
@ -1023,7 +1079,6 @@ get_xinerama_monitors (void)
monitor_create_chain(rects, screens); monitor_create_chain(rects, screens);
} }
#endif
xcb_visualid_t xcb_visualid_t
get_visual (void) get_visual (void)
@ -1046,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)
{ {
@ -1097,6 +1152,46 @@ parse_geometry_string (char *str, int *tmp)
return true; 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 void
xconn (void) xconn (void)
{ {
@ -1118,11 +1213,15 @@ xconn (void)
} }
void void
init (char *wm_name) init (void)
{ {
// 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 // Try to load a default font
if (!font_count) if (!font_count)
font_load("fixed"); parse_font_list(fallback_font);
// We tried and failed hard, there's something wrong // We tried and failed hard, there's something wrong
if (!font_count) if (!font_count)
@ -1140,7 +1239,7 @@ init (char *wm_name)
// 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
@ -1148,9 +1247,7 @@ init (char *wm_name)
if (qe_reply && qe_reply->present) { if (qe_reply && qe_reply->present) {
get_randr_monitors(); get_randr_monitors();
} } else {
#if WITH_XINERAMA
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 // Check if Xinerama extension is present and active
@ -1164,7 +1261,6 @@ init (char *wm_name)
free(xia_reply); free(xia_reply);
} }
} }
#endif
if (!monhead) { if (!monhead) {
// If I fits I sits // If I fits I sits
@ -1209,10 +1305,6 @@ init (char *wm_name)
// Make sure that the window really gets in the place it's supposed to be // Make sure that the window really gets in the place it's supposed to be
// Some WM such as Openbox need this // 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 }); 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, mon->window, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8 ,strlen(wm_name), wm_name);
} }
xcb_flush(c); xcb_flush(c);
@ -1221,8 +1313,6 @@ init (char *wm_name)
void void
cleanup (void) cleanup (void)
{ {
free(area_stack.area);
for (int i = 0; i < font_count; i++) { for (int i = 0; i < font_count; i++) {
xcb_close_font(c, font_list[i]->ptr); xcb_close_font(c, font_list[i]->ptr);
free(font_list[i]->width_lut); free(font_list[i]->width_lut);
@ -1269,72 +1359,47 @@ main (int argc, char **argv)
char input[4096] = {0, }; char input[4096] = {0, };
bool permanent = false; bool permanent = false;
int geom_v[4] = { -1, -1, 0, 0 }; int geom_v[4] = { -1, -1, 0, 0 };
int ch, areas; int ch;
char *wm_name;
// Install the parachute! // Install the parachute!
atexit(cleanup); atexit(cleanup);
signal(SIGINT, sighandle); signal(SIGINT, sighandle);
signal(SIGTERM, 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 // Connect to the Xserver and initialize scr
xconn(); xconn();
while ((ch = getopt(argc, argv, "hg:bdf:a:pu:B:F:U:n:")) != -1) { // 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) {
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 | -n | -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 Set the font name to use\n" "\t-f Bar font list, comma separated\n"
"\t-a Number of clickable areas available (default is 10)\n"
"\t-p Don't close after the data ends\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-u Set the underline/overline height in pixels\n"
"\t-B Set background color in #AARRGGBB\n" "\t-B Set background color in #AARRGGBB\n"
"\t-F Set foreground color in #AARRGGBB\n", argv[0]); "\t-F Set foreground color in #AARRGGBB\n", argv[0]);
exit (EXIT_SUCCESS); exit (EXIT_SUCCESS);
case 'g': (void)parse_geometry_string(optarg, geom_v); break; case 'g': (void)parse_geometry_string(optarg, geom_v); break;
case 'p': permanent = true; break; case 'p': permanent = true; break;
case 'n': wm_name = strdup(optarg); break;
case 'b': topbar = false; break; case 'b': topbar = false; break;
case 'd': dock = true; break; case 'd': dock = true; break;
case 'f': font_load(optarg); break; case 'f': parse_font_list(optarg); break;
case 'u': bu = strtoul(optarg, NULL, 10); break; case 'u': bu = strtoul(optarg, NULL, 10); break;
case 'B': dbgc = bgc = parse_color(optarg, NULL, (rgba_t)0x00000000U); 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)0xffffffffU); break; case 'F': dfgc = fgc = parse_color(optarg, NULL, (rgba_t)scr->white_pixel); 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 // Copy the geometry values in place
bw = geom_v[0]; bw = geom_v[0];
bh = geom_v[1]; bh = geom_v[1];
@ -1342,12 +1407,12 @@ main (int argc, char **argv)
by = geom_v[3]; by = geom_v[3];
// Do the heavy lifting // Do the heavy lifting
init(wm_name); init();
// The string is strdup'd when the command line arguments are parsed
free(wm_name);
// 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;
@ -1367,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;
@ -1382,8 +1447,8 @@ main (int argc, char **argv)
area_t *area = area_get(press_ev->event, press_ev->detail, press_ev->event_x); area_t *area = area_get(press_ev->event, press_ev->detail, press_ev->event_x);
// Respond to the click // Respond to the click
if (area) { if (area) {
(void)write(STDOUT_FILENO, area->cmd, strlen(area->cmd)); write(STDOUT_FILENO, area->cmd, strlen(area->cmd));
(void)write(STDOUT_FILENO, "\n", 1); write(STDOUT_FILENO, "\n", 1);
} }
} }
break; break;

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;
}
}
}