This commit is contained in:
Thomas Adam 2017-06-08 10:56:27 +00:00 committed by GitHub
commit c35d3eb7d3
3 changed files with 411 additions and 153 deletions

View File

@ -7,8 +7,8 @@ ifneq "$(GIT_DESC)" ""
endif endif
CC ?= gcc CC ?= gcc
CFLAGS += -Wall -std=c99 -Os -DVERSION="\"$(VERSION)\"" CFLAGS += -Wall -std=c99 -Os -DVERSION="\"$(VERSION)\"" -I/usr/include/freetype2
LDFLAGS += -lxcb -lxcb-xinerama -lxcb-randr LDFLAGS += -lxcb -lxcb-xinerama -lxcb-randr -lX11 -lX11-xcb -lXft -lfreetype -lz -lfontconfig
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

View File

@ -6,7 +6,7 @@ lemonbar - Featherweight lemon-scented bar
=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<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> | -o I<offset> ]
=head1 DESCRIPTION =head1 DESCRIPTION
@ -24,6 +24,10 @@ Display the help and exit.
Set the window geometry. If a parameter is omitted it's filled with the default value. If the I<y> parameter is specified along with the B<-b> switch then the position is relative to the bottom of the screen. Set the window geometry. If a parameter is omitted it's filled with the default value. If the I<y> parameter is specified along with the B<-b> switch then the position is relative to the bottom of the screen.
=item B<-o> I<name>
Set next output to I<name>. May be used multiple times; order is significant. If any B<-o> options are given, only B<-o> specified monitors will be used. Invalid output names are silently ignored. (only supported on randr configurations at this time)
=item B<-b> =item B<-b>
Dock the bar at the bottom of the screen. Dock the bar at the bottom of the screen.
@ -35,7 +39,7 @@ 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 Define the font to load into one of the five slots (the number of slots is hardcoded and can be tweaked by
changing the MAX_FONT_COUNT parameter in the source code). changing the MAX_FONT_COUNT parameter in the source code). This version supports fontconfig font specifiers and anti-aliased fonts.
=item B<-a> I<number> =item B<-a> I<number>
@ -61,6 +65,10 @@ Set the background color of the bar. I<color> must be specified in the hex forma
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<-o> I<offset>
Add a vertical offset to the text. I<offset> must be a number and can be negative. I<-o -3> will push the text 3 pixels up.
=item B<-U> I<color> =item B<-U> I<color>
Set the underline color of the bar. Accepts the same color formats as B<-B>. Set the underline color of the bar. Accepts the same color formats as B<-B>.
@ -139,6 +147,11 @@ First/last monitor.
Nth monitor. Nth monitor.
=item I<n>B<NAME>
Named monitor.
Eg. I<%{SnHDMI-0} This text will show up on the HDMI-0 output>
=back =back
=back =back

View File

@ -1,6 +1,7 @@
// vim:sw=4:ts=4:et: // vim:sw=4:ts=4:et:
#define _POSIX_C_SOURCE 200809L #define _POSIX_C_SOURCE 200809L
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -17,7 +18,10 @@
#endif #endif
#include <xcb/randr.h> #include <xcb/randr.h>
// Here be dragons #include <X11/Xft/Xft.h>
#include <X11/Xlib-xcb.h>
// Here bet dragons
#define max(a,b) ((a) > (b) ? (a) : (b)) #define max(a,b) ((a) > (b) ? (a) : (b))
#define min(a,b) ((a) < (b) ? (a) : (b)) #define min(a,b) ((a) < (b) ? (a) : (b))
@ -25,14 +29,20 @@
typedef struct font_t { typedef struct font_t {
xcb_font_t ptr; xcb_font_t ptr;
xcb_charinfo_t *width_lut;
XftFont *xft_ft;
int ascent;
int descent, height, width; int descent, height, width;
uint16_t char_max; uint16_t char_max;
uint16_t char_min; uint16_t char_min;
xcb_charinfo_t *width_lut;
} font_t; } font_t;
typedef struct monitor_t { typedef struct monitor_t {
int x, y, width; char *name;
int x, y, width, height;
xcb_window_t window; xcb_window_t window;
xcb_pixmap_t pixmap; xcb_pixmap_t pixmap;
struct monitor_t *prev, *next; struct monitor_t *prev, *next;
@ -68,11 +78,10 @@ enum {
ATTR_UNDERL = (1<<1), ATTR_UNDERL = (1<<1),
}; };
enum { enum { ALIGN_L = 0,
ALIGN_L = 0, ALIGN_C,
ALIGN_C, ALIGN_R
ALIGN_R };
};
enum { enum {
GC_DRAW = 0, GC_DRAW = 0,
@ -83,15 +92,26 @@ enum {
#define MAX_FONT_COUNT 5 #define MAX_FONT_COUNT 5
static Display *dpy;
static xcb_connection_t *c; static xcb_connection_t *c;
static xcb_screen_t *scr; static xcb_screen_t *scr;
static int scr_nbr = 0;
static xcb_gcontext_t gc[GC_MAX]; static xcb_gcontext_t gc[GC_MAX];
static xcb_visualid_t visual; static xcb_visualid_t visual;
static Visual *visual_ptr;
static xcb_colormap_t colormap; static xcb_colormap_t colormap;
static monitor_t *monhead, *montail; static monitor_t *monhead, *montail;
static font_t *font_list[MAX_FONT_COUNT]; static font_t *font_list[MAX_FONT_COUNT];
static int font_count = 0; static int font_count = 0;
static int font_index = -1; static int font_index = -1;
static int offsets_y[MAX_FONT_COUNT];
static int offset_y_count = 0;
static int offset_y_index = 0;
static uint32_t attrs = 0; static uint32_t attrs = 0;
static bool dock = false; static bool dock = false;
static bool topbar = true; static bool topbar = true;
@ -101,12 +121,31 @@ static rgba_t fgc, bgc, ugc;
static rgba_t dfgc, dbgc, dugc; static rgba_t dfgc, dbgc, dugc;
static area_stack_t area_stack; static area_stack_t area_stack;
static XftColor sel_fg;
static XftDraw *xft_draw;
//char width lookuptable
#define MAX_WIDTHS (1 << 16)
static wchar_t xft_char[MAX_WIDTHS];
static char xft_width[MAX_WIDTHS];
static int num_outputs = 0;
static char **output_names = NULL;
static void parse_output_string(char *);
void void
update_gc (void) update_gc (void)
{ {
xcb_change_gc(c, gc[GC_DRAW], XCB_GC_FOREGROUND, (const uint32_t []){ fgc.v }); xcb_change_gc(c, gc[GC_DRAW], XCB_GC_FOREGROUND, (const uint32_t []){ fgc.v });
xcb_change_gc(c, gc[GC_CLEAR], XCB_GC_FOREGROUND, (const uint32_t []){ bgc.v }); xcb_change_gc(c, gc[GC_CLEAR], XCB_GC_FOREGROUND, (const uint32_t []){ bgc.v });
xcb_change_gc(c, gc[GC_ATTR], XCB_GC_FOREGROUND, (const uint32_t []){ ugc.v }); xcb_change_gc(c, gc[GC_ATTR], XCB_GC_FOREGROUND, (const uint32_t []){ ugc.v });
XftColorFree(dpy, visual_ptr, colormap , &sel_fg);
char color[] = "#ffffff";
uint32_t nfgc = fgc.v & 0x00ffffff;
snprintf(color, sizeof(color), "#%06X", nfgc);
if (!XftColorAllocName (dpy, visual_ptr, colormap, color, &sel_fg)) {
fprintf(stderr, "Couldn't allocate xft font color '%s'\n", color);
}
} }
void void
@ -188,6 +227,36 @@ xcb_void_cookie_t xcb_poly_text_16_simple(xcb_connection_t * c,
return xcb_ret; return xcb_ret;
} }
int
xft_char_width_slot (uint16_t ch)
{
int slot = ch % MAX_WIDTHS;
while (xft_char[slot] != 0 && xft_char[slot] != ch)
{
slot = (slot + 1) % MAX_WIDTHS;
}
return slot;
}
int xft_char_width (uint16_t ch, font_t *cur_font)
{
int slot = xft_char_width_slot(ch);
if (!xft_char[slot]) {
XGlyphInfo gi;
FT_UInt glyph = XftCharIndex (dpy, cur_font->xft_ft, (FcChar32) ch);
XftFontLoadGlyphs (dpy, cur_font->xft_ft, FcFalse, &glyph, 1);
XftGlyphExtents (dpy, cur_font->xft_ft, &glyph, 1, &gi);
XftFontUnloadGlyphs (dpy, cur_font->xft_ft, &glyph, 1);
xft_char[slot] = ch;
xft_width[slot] = gi.xOff;
return gi.xOff;
} else if (xft_char[slot] == ch)
return xft_width[slot];
else
return 0;
}
int int
shift (monitor_t *mon, int x, int align, int ch_width) shift (monitor_t *mon, int x, int align, int ch_width)
{ {
@ -207,8 +276,8 @@ shift (monitor_t *mon, int x, int align, int ch_width)
x = mon->width - ch_width; x = mon->width - ch_width;
break; 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, 0, ch_width, bh);
return x; return x;
} }
@ -233,19 +302,30 @@ draw_shift (monitor_t *mon, int x, int align, int w)
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; if (cur_font->xft_ft) {
ch_width = xft_char_width(ch, cur_font);
} else {
ch_width = (cur_font->width_lut) ?
cur_font->width_lut[ch - cur_font->char_min].character_width:
cur_font->width;
}
x = shift(mon, x, align, ch_width); x = shift(mon, x, align, ch_width);
// xcb accepts string in UCS-2 BE, so swap int y = bh / 2 + cur_font->height / 2- cur_font->descent + offsets_y[offset_y_index];
ch = (ch >> 8) | (ch << 8); if (cur_font->xft_ft) {
XftDrawString16 (xft_draw, &sel_fg, cur_font->xft_ft, x,y, &ch, 1);
// The coordinates here are those of the baseline } else {
xcb_poly_text_16_simple(c, mon->pixmap, gc[GC_DRAW], /* xcb accepts string in UCS-2 BE, so swap */
x, bh / 2 + cur_font->height / 2 - cur_font->descent, ch = (ch >> 8) | (ch << 8);
// The coordinates here are those of the baseline
xcb_poly_text_16_simple(c, mon->pixmap, gc[GC_DRAW],
x, y,
1, &ch); 1, &ch);
}
draw_lines(mon, x, ch_width); draw_lines(mon, x, ch_width);
@ -336,9 +416,15 @@ set_attribute (const char modifier, const char attribute)
} }
switch (modifier) { switch (modifier) {
case '+': attrs |= (1<<pos); break; case '+':
case '-': attrs &=~(1<<pos); break; attrs |= (1u<<pos);
case '!': attrs ^= (1<<pos); break; break;
case '-':
attrs &=~(1u<<pos);
break;
case '!':
attrs ^= (1u<<pos);
break;
} }
} }
@ -459,6 +545,15 @@ 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 (font->xft_ft) {
if (XftCharExists(dpy, font->xft_ft, (FcChar32) c)) {
return true;
} else {
return false;
}
}
if (c < font->char_min || c > font->char_max) if (c < font->char_min || c > font->char_max)
return false; return false;
@ -468,23 +563,27 @@ font_has_glyph (font_t *font, const uint16_t c)
return true; return true;
} }
// returns NULL if character cannot be printed
font_t * font_t *
select_drawable_font (const uint16_t c) select_drawable_font (const uint16_t c)
{ {
// If the user has specified a font to use, try that first. // If the user has specified a font to use, try that first.
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)) {
offset_y_index = font_index - 1;
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 appropriate 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)) {
offset_y_index = i;
return font_list[i]; return font_list[i];
}
} }
return NULL; return NULL;
} }
void void
parse (char *text) parse (char *text)
{ {
@ -501,12 +600,14 @@ parse (char *text)
// Reset the stack position // Reset the stack position
area_stack.at = 0; area_stack.at = 0;
for (monitor_t *m = monhead; m != NULL; m = m->next) /* Create xft drawable */
fill_rect(m->pixmap, gc[GC_CLEAR], 0, 0, m->width, bh); if (!(xft_draw = XftDrawCreate (dpy, cur_mon->pixmap, visual_ptr , colormap))) {
fprintf(stderr, "Couldn't create xft drawable\n");
}
for (;;) { for (;;) {
if (*p == '\0' || *p == '\n') if (*p == '\0' || *p == '\n')
return; break;
if (p[0] == '%' && p[1] == '{' && (block_end = strchr(p++, '}'))) { if (p[0] == '%' && p[1] == '{' && (block_end = strchr(p++, '}'))) {
p++; p++;
@ -553,6 +654,15 @@ parse (char *text)
{ cur_mon = monhead; } { cur_mon = monhead; }
else if (*p == 'l') else if (*p == 'l')
{ cur_mon = montail ? montail : monhead; } { cur_mon = montail ? montail : monhead; }
else if (*p == 'n')
{ cur_mon = monhead;
while (cur_mon->next) {
if (cur_mon->name && !strncmp(cur_mon->name, p+1, (block_end-p)-1)) {
break;
}
cur_mon = cur_mon->next;
}
}
else if (isdigit(*p)) else if (isdigit(*p))
{ cur_mon = monhead; { cur_mon = monhead;
for (int i = 0; i != *p-'0' && cur_mon->next; i++) for (int i = 0; i != *p-'0' && cur_mon->next; i++)
@ -560,9 +670,14 @@ parse (char *text)
} }
else else
{ p++; continue; } { p++; continue; }
XftDrawDestroy (xft_draw);
if (!(xft_draw = XftDrawCreate (dpy, cur_mon->pixmap, visual_ptr , colormap ))) {
fprintf(stderr, "Couldn't create xft drawable\n");
}
p++; p++;
pos_x = 0; pos_x = 0;
fill_rect(cur_mon->pixmap, gc[GC_CLEAR], 0, 0, cur_mon->width, bh);
break; break;
case 'O': case 'O':
errno = 0; errno = 0;
@ -649,14 +764,17 @@ parse (char *text)
if (!cur_font) if (!cur_font)
continue; continue;
xcb_change_gc(c, gc[GC_DRAW] , XCB_GC_FONT, (const uint32_t []){ cur_font->ptr }); if(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);
} }
} }
XftDrawDestroy (xft_draw);
} }
void void
@ -674,39 +792,60 @@ 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);
if (xcb_request_check (c, cookie)) {
fprintf(stderr, "Could not load font \"%s\"\n", pattern);
return;
}
font_t *ret = calloc(1, sizeof(font_t)); font_t *ret = calloc(1, sizeof(font_t));
if (!ret) if (!ret)
return; return;
queryreq = xcb_query_font(c, font); cookie = xcb_open_font_checked(c, font, strlen(pattern), pattern);
font_info = xcb_query_font_reply(c, queryreq, NULL); if (!xcb_request_check (c, cookie)) {
queryreq = xcb_query_font(c, font);
font_info = xcb_query_font_reply(c, queryreq, NULL);
ret->ptr = font; ret->xft_ft = NULL;
ret->descent = font_info->font_descent; ret->ptr = font;
ret->height = font_info->font_ascent + font_info->font_descent; ret->descent = font_info->font_descent;
ret->width = font_info->max_bounds.character_width; 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->width = font_info->max_bounds.character_width;
ret->char_min = font_info->min_byte1 << 8 | font_info->min_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;
// Copy over the width lut as it's part of font_info // Copy over the width lut as it's part of font_info
int lut_size = sizeof(xcb_charinfo_t) * xcb_query_font_char_infos_length(font_info); int lut_size = sizeof(xcb_charinfo_t) * xcb_query_font_char_infos_length(font_info);
if (lut_size) { if (lut_size) {
ret->width_lut = malloc(lut_size); ret->width_lut = malloc(lut_size);
memcpy(ret->width_lut, xcb_query_font_char_infos(font_info), lut_size); memcpy(ret->width_lut, xcb_query_font_char_infos(font_info), lut_size);
}
free(font_info);
} else if ((ret->xft_ft = XftFontOpenName (dpy, scr_nbr, pattern))) {
ret->ptr = 0;
ret->ascent = ret->xft_ft->ascent;
ret->descent = ret->xft_ft->descent;
ret->height = ret->ascent + ret->descent;
} else {
fprintf(stderr, "Could not load font %s\n", pattern);
free(ret);
return;
} }
free(font_info);
font_list[font_count++] = ret; font_list[font_count++] = ret;
} }
void add_y_offset(int offset) {
if (offset_y_count >= MAX_FONT_COUNT) {
fprintf(stderr, "Max offset count reached. Could not set offset \"%d\"\n", offset);
return;
}
offsets_y[offset_y_count] = strtol(optarg, NULL, 10);
if (offset_y_count == 0) {
for (int i = 1; i < MAX_FONT_COUNT; ++i) {
offsets_y[i] = offsets_y[0];
}
}
++offset_y_count;
}
enum { enum {
NET_WM_WINDOW_TYPE, NET_WM_WINDOW_TYPE,
NET_WM_WINDOW_TYPE_DOCK, NET_WM_WINDOW_TYPE_DOCK,
@ -765,15 +904,18 @@ set_ewmh_atoms (void)
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]);
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_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_DESKTOP], XCB_ATOM_CARDINAL, 32, 1, (const uint32_t []) {
0u - 1u
} );
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_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_STRUT], XCB_ATOM_CARDINAL, 32, 4, strut);
xcb_change_property(c, XCB_PROP_MODE_REPLACE, mon->window, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8, 3, "bar"); xcb_change_property(c, XCB_PROP_MODE_REPLACE, mon->window, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8, 3, "bar");
xcb_change_property(c, XCB_PROP_MODE_REPLACE, mon->window, XCB_ATOM_WM_CLASS, XCB_ATOM_STRING, 8, 12, "lemonbar\0Bar");
} }
} }
monitor_t * monitor_t *
monitor_new (int x, int y, int width, int height) monitor_new (int x, int y, int width, int height, char *name)
{ {
monitor_t *ret; monitor_t *ret;
@ -783,18 +925,20 @@ monitor_new (int x, int y, int width, int height)
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
ret->name = name;
ret->x = x; ret->x = x;
ret->y = (topbar ? by : height - bh - by) + y; ret->y = (topbar ? by : height - bh - by) + y;
ret->width = width; ret->width = width;
ret->next = ret->prev = NULL; ret->next = ret->prev = NULL;
ret->window = xcb_generate_id(c); ret->window = xcb_generate_id(c);
int depth = (visual == scr->root_visual) ? XCB_COPY_FROM_PARENT : 32; int depth = (visual == scr->root_visual) ? XCB_COPY_FROM_PARENT : 32;
xcb_create_window(c, depth, ret->window, scr->root, xcb_create_window(c, depth, ret->window, scr->root,
ret->x, ret->y, width, bh, 0, ret->x, ret->y, width, bh, 0,
XCB_WINDOW_CLASS_INPUT_OUTPUT, visual, XCB_WINDOW_CLASS_INPUT_OUTPUT, visual,
XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL | XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK | XCB_CW_COLORMAP, XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL | XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK | XCB_CW_COLORMAP,
(const uint32_t []){ bgc.v, bgc.v, dock, XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_PRESS, colormap }); (const uint32_t []) {
bgc.v, bgc.v, dock, XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_PRESS, colormap
});
ret->pixmap = xcb_generate_id(c); ret->pixmap = xcb_generate_id(c);
xcb_create_pixmap(c, depth, ret->pixmap, ret->window, width, bh); xcb_create_pixmap(c, depth, ret->pixmap, ret->window, width, bh);
@ -819,17 +963,17 @@ monitor_add (monitor_t *mon)
} }
int int
rect_sort_cb (const void *p1, const void *p2) mon_sort_cb (const void *p1, const void *p2)
{ {
const xcb_rectangle_t *r1 = (xcb_rectangle_t *)p1; const monitor_t *m1 = (monitor_t *)p1;
const xcb_rectangle_t *r2 = (xcb_rectangle_t *)p2; const monitor_t *m2 = (monitor_t *)p2;
if (r1->x < r2->x || r1->y + r1->height <= r2->y) if (m1->x < m2->x || m1->y + m1->height <= m2->y)
{ {
return -1; return -1;
} }
if (r1->x > r2->x || r1->y + r1->height > r2->y) if (m1->x > m2->x || m1->y + m1->height > m2->y)
{ {
return 1; return 1;
} }
@ -838,22 +982,23 @@ rect_sort_cb (const void *p1, const void *p2)
} }
void void
monitor_create_chain (xcb_rectangle_t *rects, const int num) monitor_create_chain (monitor_t *mons, const int num)
{ {
int i; int i;
int width = 0, height = 0; int width = 0, height = 0;
int left = bx; int left = bx;
// Sort before use // Sort before use
qsort(rects, num, sizeof(xcb_rectangle_t), rect_sort_cb); if (!num_outputs)
qsort(mons, num, sizeof(monitor_t), mon_sort_cb);
for (i = 0; i < num; i++) { for (i = 0; i < num; i++) {
int h = rects[i].y + rects[i].height; int h = mons[i].y + mons[i].height;
// Accumulated width of all monitors // Accumulated width of all monitors
width += rects[i].width; width += mons[i].width;
// Get height of screen from y_offset + height of lowest monitor // Get height of screen from y_offset + height of lowest monitor
if (h >= height) if (h >= height)
height = h; height = h;
} }
if (bw < 0) if (bw < 0)
@ -872,28 +1017,29 @@ monitor_create_chain (xcb_rectangle_t *rects, const int num)
// Left is a positive number or zero therefore monitors with zero width are excluded // Left is a positive number or zero therefore monitors with zero width are excluded
width = bw; width = bw;
for (i = 0; i < num; i++) { for (i = 0; i < num; i++) {
if (rects[i].y + rects[i].height < by) if (mons[i].y + mons[i].height < by)
continue; continue;
if (rects[i].width > left) { if (mons[i].width > left) {
monitor_t *mon = monitor_new( monitor_t *mon = monitor_new(
rects[i].x + left, mons[i].x + left,
rects[i].y, mons[i].y,
min(width, rects[i].width - left), min(width, mons[i].width - left),
rects[i].height); mons[i].height,
mons[i].name);
if (!mon) if (!mon)
break; break;
monitor_add(mon); monitor_add(mon);
width -= rects[i].width - left; width -= mons[i].width - left;
// No need to check for other monitors // No need to check for other monitors
if (width <= 0) if (width <= 0)
break; break;
} }
left -= rects[i].width; left -= mons[i].width;
if (left < 0) if (left < 0)
left = 0; left = 0;
@ -908,7 +1054,7 @@ get_randr_monitors (void)
int i, j, num, valid = 0; int i, j, num, valid = 0;
rres_reply = xcb_randr_get_screen_resources_current_reply(c, rres_reply = xcb_randr_get_screen_resources_current_reply(c,
xcb_randr_get_screen_resources_current(c, scr->root), NULL); xcb_randr_get_screen_resources_current(c, scr->root), NULL);
if (!rres_reply) { if (!rres_reply) {
fprintf(stderr, "Failed to get current randr screen resources\n"); fprintf(stderr, "Failed to get current randr screen resources\n");
@ -925,26 +1071,27 @@ get_randr_monitors (void)
return; return;
} }
xcb_rectangle_t rects[num]; monitor_t mons[num];
// Get all outputs // Get all outputs
for (i = 0; i < num; i++) { for (i = 0; i < num; i++) {
xcb_randr_get_output_info_reply_t *oi_reply; xcb_randr_get_output_info_reply_t *oi_reply;
xcb_randr_get_crtc_info_reply_t *ci_reply; xcb_randr_get_crtc_info_reply_t *ci_reply;
int namelen;
uint8_t *str;
char *name;
oi_reply = xcb_randr_get_output_info_reply(c, xcb_randr_get_output_info(c, outputs[i], XCB_CURRENT_TIME), NULL); 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 ? // Output disconnected or not attached to any CRTC ?
if (!oi_reply || oi_reply->crtc == XCB_NONE || oi_reply->connection != XCB_RANDR_CONNECTION_CONNECTED) { if (!oi_reply || oi_reply->crtc == XCB_NONE || oi_reply->connection != XCB_RANDR_CONNECTION_CONNECTED) {
free(oi_reply); free(oi_reply);
rects[i].width = 0; mons[i].width = 0;
continue; continue;
} }
ci_reply = xcb_randr_get_crtc_info_reply(c, ci_reply = xcb_randr_get_crtc_info_reply(c,
xcb_randr_get_crtc_info(c, oi_reply->crtc, XCB_CURRENT_TIME), NULL); xcb_randr_get_crtc_info(c, oi_reply->crtc, XCB_CURRENT_TIME), NULL);
free(oi_reply);
if (!ci_reply) { if (!ci_reply) {
fprintf(stderr, "Failed to get RandR ctrc info\n"); fprintf(stderr, "Failed to get RandR ctrc info\n");
@ -952,9 +1099,45 @@ get_randr_monitors (void)
return; return;
} }
// There's no need to handle rotated screens here (see #69) namelen = xcb_randr_get_output_info_name_length(oi_reply);
rects[i] = (xcb_rectangle_t){ ci_reply->x, ci_reply->y, ci_reply->width, ci_reply->height }; name = malloc(namelen+1);
if (!name) {
fprintf(stderr, "Failed to allocate randr output name\n");
exit(EXIT_FAILURE);
}
str = xcb_randr_get_output_info_name(oi_reply);
memcpy(name, str, namelen);
name[namelen] = '\0';
if (!name || name[0] == '\0')
continue;
parse_output_string(name);
if (num_outputs) {
for (j = 0; j < num_outputs; j++) {
// if this output name has been allocated, skip it
if (!output_names[j])
continue;
if (!memcmp(output_names[j], name, namelen)) {
mons[j] = (monitor_t){ name, ci_reply->x, ci_reply->y, ci_reply->width, ci_reply->height, 0, 0, NULL, NULL };
output_names[j] = NULL;
break;
}
else
free(name);
}
}
else {
// There's no need to handle rotated screens here (see #69)
mons[i] = (monitor_t){ NULL, ci_reply->x, ci_reply->y, ci_reply->width, ci_reply->height, 0, 0, NULL, NULL };
}
free(oi_reply);
free(ci_reply); free(ci_reply);
valid++; valid++;
@ -962,36 +1145,15 @@ get_randr_monitors (void)
free(rres_reply); free(rres_reply);
// Check for clones and inactive outputs if (num_outputs)
for (i = 0; i < num; i++) { free(output_names);
if (rects[i].width == 0)
continue;
for (j = 0; j < num; j++) { if (valid == 0) {
// Does I contain J ?
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 &&
rects[j].y >= rects[i].y && rects[j].y + rects[j].height <= rects[i].y + rects[i].height) {
rects[j].width = 0;
valid--;
}
}
}
}
if (valid < 1) {
fprintf(stderr, "No usable RandR output found\n"); fprintf(stderr, "No usable RandR output found\n");
return; return;
} }
xcb_rectangle_t r[valid]; monitor_create_chain(mons, valid);
for (i = j = 0; i < num && j < valid; i++)
if (rects[i].width != 0)
r[j++] = rects[i];
monitor_create_chain(r, valid);
} }
#ifdef WITH_XINERAMA #ifdef WITH_XINERAMA
@ -1002,48 +1164,53 @@ get_xinerama_monitors (void)
xcb_xinerama_screen_info_iterator_t iter; xcb_xinerama_screen_info_iterator_t iter;
int screens; int screens;
if (num_outputs) {
fprintf(stderr, "Using output names with Xinerama is not yet supported\n");
return;
}
xqs_reply = xcb_xinerama_query_screens_reply(c, xqs_reply = xcb_xinerama_query_screens_reply(c,
xcb_xinerama_query_screens_unchecked(c), NULL); 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; screens = iter.rem;
xcb_rectangle_t rects[screens]; monitor_t mons[screens];
// Fetch all the screens first // Fetch all the screens first
for (int i = 0; iter.rem; i++) { for (int i = 0; iter.rem; i++) {
rects[i].x = iter.data->x_org; mons[i].name = NULL;
rects[i].y = iter.data->y_org; mons[i].x = iter.data->x_org;
rects[i].width = iter.data->width; mons[i].y = iter.data->y_org;
rects[i].height = iter.data->height; mons[i].width = iter.data->width;
mons[i].height = iter.data->height;
xcb_xinerama_screen_info_next(&iter); xcb_xinerama_screen_info_next(&iter);
} }
free(xqs_reply); free(xqs_reply);
monitor_create_chain(rects, screens); monitor_create_chain(mons, screens);
} }
#endif #endif
xcb_visualid_t xcb_visualid_t
get_visual (void) get_visual (void)
{ {
xcb_depth_iterator_t iter;
iter = xcb_screen_allowed_depths_iterator(scr); XVisualInfo xv;
xv.depth = 32;
int result = 0;
XVisualInfo* result_ptr = NULL;
result_ptr = XGetVisualInfo(dpy, VisualDepthMask, &xv, &result);
// Try to find a RGBA visual if (result > 0) {
while (iter.rem) { visual_ptr = result_ptr->visual;
xcb_visualtype_t *vis = xcb_depth_visuals(iter.data); return result_ptr->visualid;
if (iter.data->depth == 32)
return vis->visual_id;
xcb_depth_next(&iter);
} }
// Fallback to the default one //Fallback
return scr->root_visual; visual_ptr = DefaultVisual(dpy, scr_nbr);
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 though.
@ -1097,28 +1264,50 @@ parse_geometry_string (char *str, int *tmp)
return true; return true;
} }
void
parse_output_string(char *str)
{
if (!str || !*str)
return;
num_outputs++;
output_names = realloc(output_names, num_outputs * sizeof(char *));
if (!output_names) {
fprintf(stderr, "failed to allocate output name\n");
exit(EXIT_FAILURE);
}
output_names[num_outputs-1] = str;
}
void void
xconn (void) xconn (void)
{ {
// Connect to X if ((dpy = XOpenDisplay(0)) == NULL) {
c = xcb_connect (NULL, NULL); fprintf (stderr, "Couldnt open display\n");
}
if ((c = XGetXCBConnection(dpy)) == NULL) {
fprintf (stderr, "Couldnt connect to X\n");
exit (EXIT_FAILURE);
}
XSetEventQueueOwner(dpy, XCBOwnsEventQueue);
if (xcb_connection_has_error(c)) { if (xcb_connection_has_error(c)) {
fprintf(stderr, "Couldn't connect to X\n"); fprintf(stderr, "Couldn't connect to X\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
// Grab infos from the first screen /* Grab infos from the first screen */
scr = xcb_setup_roots_iterator(xcb_get_setup(c)).data; scr = xcb_setup_roots_iterator(xcb_get_setup(c)).data;
// Try to get a RGBA visual and build the colormap for that /* Try to get a RGBA visual and build the colormap for that */
visual = get_visual(); visual = get_visual();
colormap = xcb_generate_id(c); colormap = xcb_generate_id(c);
xcb_create_colormap(c, XCB_COLORMAP_ALLOC_NONE, colormap, scr->root, visual); xcb_create_colormap(c, XCB_COLORMAP_ALLOC_NONE, colormap, scr->root, visual);
} }
void void
init (char *wm_name) init (char *wm_name, char *wm_instance)
{ {
// Try to load a default font // Try to load a default font
if (!font_count) if (!font_count)
@ -1167,6 +1356,10 @@ init (char *wm_name)
#endif #endif
if (!monhead) { if (!monhead) {
if (num_outputs) {
fprintf(stderr, "Failed to find any specified outputs\n");
exit(EXIT_FAILURE);
}
// If I fits I sits // If I fits I sits
if (bw < 0) if (bw < 0)
bw = scr->width_in_pixels - bx; bw = scr->width_in_pixels - bx;
@ -1182,7 +1375,7 @@ init (char *wm_name)
} }
// If no RandR outputs or Xinerama screens, fall back to using whole screen // If no RandR outputs or Xinerama screens, fall back to using whole screen
monhead = monitor_new(0, 0, bw, scr->height_in_pixels); monhead = monitor_new(0, 0, bw, scr->height_in_pixels, NULL);
} }
if (!monhead) if (!monhead)
@ -1213,8 +1406,33 @@ init (char *wm_name)
// Set the WM_NAME atom to the user specified value // Set the WM_NAME atom to the user specified value
if (wm_name) 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_change_property(c, XCB_PROP_MODE_REPLACE, mon->window, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8 ,strlen(wm_name), wm_name);
// set the WM_CLASS atom instance to the executable name
if (wm_instance) {
char *wm_class;
int wm_class_offset, wm_class_len;
// WM_CLASS is nullbyte seperated: wm_instance + "\0Bar\0"
wm_class_offset = strlen(wm_instance) + 1;
wm_class_len = wm_class_offset + 4;
wm_class = calloc(1, wm_class_len + 1);
strcpy(wm_class, wm_instance);
strcpy(wm_class+wm_class_offset, "Bar");
xcb_change_property(c, XCB_PROP_MODE_REPLACE, mon->window, XCB_ATOM_WM_CLASS, XCB_ATOM_STRING, 8, wm_class_len, wm_class);
free(wm_class);
}
} }
char color[] = "#ffffff";
uint32_t nfgc = fgc.v & 0x00ffffff;
snprintf(color, sizeof(color), "#%06X", nfgc);
if (!XftColorAllocName (dpy, visual_ptr, colormap, color, &sel_fg)) {
fprintf(stderr, "Couldn't allocate xft font color '%s'\n", color);
}
xcb_flush(c); xcb_flush(c);
} }
@ -1222,10 +1440,14 @@ void
cleanup (void) cleanup (void)
{ {
free(area_stack.area); free(area_stack.area);
for (int i = 0; font_list[i]; i++) {
for (int i = 0; i < font_count; i++) { if (font_list[i]->xft_ft) {
xcb_close_font(c, font_list[i]->ptr); XftFontClose (dpy, font_list[i]->xft_ft);
free(font_list[i]->width_lut); }
else {
xcb_close_font(c, font_list[i]->ptr);
free(font_list[i]->width_lut);
}
free(font_list[i]); free(font_list[i]);
} }
@ -1237,7 +1459,7 @@ cleanup (void)
monhead = next; monhead = next;
} }
xcb_free_colormap(c, colormap); XftColorFree(dpy, visual_ptr, colormap, &sel_fg);
if (gc[GC_DRAW]) if (gc[GC_DRAW])
xcb_free_gc(c, gc[GC_DRAW]); xcb_free_gc(c, gc[GC_DRAW]);
@ -1249,6 +1471,21 @@ cleanup (void)
xcb_disconnect(c); xcb_disconnect(c);
} }
char*
strip_path(char *path)
{
char *slash;
if (path == NULL || *path == '\0')
return strdup("lemonbar");
slash = strrchr(path, '/');
if (slash != NULL)
return strndup(slash + 1, 31);
return strndup(path, 31);
}
void void
sighandle (int signal) sighandle (int signal)
{ {
@ -1256,6 +1493,7 @@ sighandle (int signal)
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
} }
int int
main (int argc, char **argv) main (int argc, char **argv)
{ {
@ -1271,6 +1509,7 @@ main (int argc, char **argv)
int geom_v[4] = { -1, -1, 0, 0 }; int geom_v[4] = { -1, -1, 0, 0 };
int ch, areas; int ch, areas;
char *wm_name; char *wm_name;
char *instance_name;
// Install the parachute! // Install the parachute!
atexit(cleanup); atexit(cleanup);
@ -1280,20 +1519,23 @@ main (int argc, char **argv)
// B/W combo // B/W combo
dbgc = bgc = (rgba_t)0x00000000U; dbgc = bgc = (rgba_t)0x00000000U;
dfgc = fgc = (rgba_t)0xffffffffU; dfgc = fgc = (rgba_t)0xffffffffU;
dugc = ugc = fgc; dugc = ugc = fgc;
// A safe default // A safe default
areas = 10; areas = 10;
wm_name = NULL; wm_name = NULL;
instance_name = strip_path(argv[0]);
// 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) { while ((ch = getopt(argc, argv, "hg:o:bdf:a:pu:B:F:U:n:")) != -1) {
switch (ch) { switch (ch) {
case 'h': case 'h':
printf ("lemonbar version %s\n", VERSION); printf ("lemonbar version %s\n", VERSION);
printf ("usage: %s [-h | -g | -b | -d | -f | -a | -p | -n | -u | -B | -F]\n" printf ("usage: %s [-h | -o | -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 the bar at the bottom of the screen\n"
@ -1304,7 +1546,8 @@ main (int argc, char **argv)
"\t-n Set the WM_NAME atom to the specified value for this bar\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"
"\t-o Add a vertical offset to the text, it can be negative\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;
@ -1313,6 +1556,7 @@ main (int argc, char **argv)
case 'd': dock = true; break; case 'd': dock = true; break;
case 'f': font_load(optarg); break; case 'f': font_load(optarg); break;
case 'u': bu = strtoul(optarg, NULL, 10); break; case 'u': bu = strtoul(optarg, NULL, 10); break;
case 'o': add_y_offset(strtol(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)0x00000000U); break;
case 'F': dfgc = fgc = parse_color(optarg, NULL, (rgba_t)0xffffffffU); break; case 'F': dfgc = fgc = parse_color(optarg, NULL, (rgba_t)0xffffffffU); break;
case 'U': dugc = ugc = parse_color(optarg, NULL, fgc); break; case 'U': dugc = ugc = parse_color(optarg, NULL, fgc); break;
@ -1342,12 +1586,13 @@ 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(wm_name, instance_name);
// The string is strdup'd when the command line arguments are parsed // The string is strdup'd when the command line arguments are parsed
free(wm_name); free(wm_name);
// The string is strdup'd when stripping argv[0]
free(instance_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);
for (;;) { for (;;) {
bool redraw = false; bool redraw = false;
@ -1386,7 +1631,7 @@ main (int argc, char **argv)
(void)write(STDOUT_FILENO, "\n", 1); (void)write(STDOUT_FILENO, "\n", 1);
} }
} }
break; break;
} }
free(ev); free(ev);