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
CC ?= gcc
CFLAGS += -Wall -std=c99 -Os -DVERSION="\"$(VERSION)\""
LDFLAGS += -lxcb -lxcb-xinerama -lxcb-randr
CFLAGS += -Wall -std=c99 -Os -DVERSION="\"$(VERSION)\"" -I/usr/include/freetype2
LDFLAGS += -lxcb -lxcb-xinerama -lxcb-randr -lX11 -lX11-xcb -lXft -lfreetype -lz -lfontconfig
CFDEBUG = -g3 -pedantic -Wall -Wunused-parameter -Wlong-long \
-Wsign-conversion -Wconversion -Wimplicit-function-declaration

View File

@ -6,7 +6,7 @@ lemonbar - Featherweight lemon-scented bar
=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
@ -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.
=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>
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>
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>
@ -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>.
=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>
Set the underline color of the bar. Accepts the same color formats as B<-B>.
@ -139,6 +147,11 @@ First/last 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

View File

@ -1,6 +1,7 @@
// vim:sw=4:ts=4:et:
#define _POSIX_C_SOURCE 200809L
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -17,7 +18,10 @@
#endif
#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 min(a,b) ((a) < (b) ? (a) : (b))
@ -25,14 +29,20 @@
typedef struct font_t {
xcb_font_t ptr;
xcb_charinfo_t *width_lut;
XftFont *xft_ft;
int ascent;
int descent, height, width;
uint16_t char_max;
uint16_t char_min;
xcb_charinfo_t *width_lut;
} font_t;
typedef struct monitor_t {
int x, y, width;
char *name;
int x, y, width, height;
xcb_window_t window;
xcb_pixmap_t pixmap;
struct monitor_t *prev, *next;
@ -68,11 +78,10 @@ enum {
ATTR_UNDERL = (1<<1),
};
enum {
ALIGN_L = 0,
ALIGN_C,
ALIGN_R
};
enum { ALIGN_L = 0,
ALIGN_C,
ALIGN_R
};
enum {
GC_DRAW = 0,
@ -83,15 +92,26 @@ enum {
#define MAX_FONT_COUNT 5
static Display *dpy;
static xcb_connection_t *c;
static xcb_screen_t *scr;
static int scr_nbr = 0;
static xcb_gcontext_t gc[GC_MAX];
static xcb_visualid_t visual;
static Visual *visual_ptr;
static xcb_colormap_t colormap;
static monitor_t *monhead, *montail;
static font_t *font_list[MAX_FONT_COUNT];
static int font_count = 0;
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 bool dock = false;
static bool topbar = true;
@ -101,12 +121,31 @@ static rgba_t fgc, bgc, ugc;
static rgba_t dfgc, dbgc, dugc;
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
update_gc (void)
{
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_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
@ -188,6 +227,36 @@ xcb_void_cookie_t xcb_poly_text_16_simple(xcb_connection_t * c,
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
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;
break;
}
// Draw the background first
/* Draw the background first */
fill_rect(mon->pixmap, gc[GC_CLEAR], x, 0, ch_width, bh);
return x;
}
@ -233,19 +302,30 @@ draw_shift (monitor_t *mon, int x, int align, int w)
int
draw_char (monitor_t *mon, font_t *cur_font, int x, int align, uint16_t ch)
{
int ch_width = (cur_font->width_lut) ?
cur_font->width_lut[ch - cur_font->char_min].character_width:
cur_font->width;
int ch_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);
// xcb accepts string in UCS-2 BE, so swap
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, bh / 2 + cur_font->height / 2 - cur_font->descent,
int y = bh / 2 + cur_font->height / 2- cur_font->descent + offsets_y[offset_y_index];
if (cur_font->xft_ft) {
XftDrawString16 (xft_draw, &sel_fg, cur_font->xft_ft, x,y, &ch, 1);
} else {
/* xcb accepts string in UCS-2 BE, so swap */
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);
}
draw_lines(mon, x, ch_width);
@ -336,9 +416,15 @@ set_attribute (const char modifier, const char attribute)
}
switch (modifier) {
case '+': attrs |= (1<<pos); break;
case '-': attrs &=~(1<<pos); break;
case '!': attrs ^= (1<<pos); break;
case '+':
attrs |= (1u<<pos);
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
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)
return false;
@ -468,23 +563,27 @@ font_has_glyph (font_t *font, const uint16_t c)
return true;
}
// returns NULL if character cannot be printed
font_t *
select_drawable_font (const uint16_t c)
{
// 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];
}
// If the end is reached without finding an appropriate font, return NULL.
// If the font can draw the character, return it.
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 NULL;
}
void
parse (char *text)
{
@ -501,12 +600,14 @@ parse (char *text)
// Reset the stack position
area_stack.at = 0;
for (monitor_t *m = monhead; m != NULL; m = m->next)
fill_rect(m->pixmap, gc[GC_CLEAR], 0, 0, m->width, bh);
/* Create xft drawable */
if (!(xft_draw = XftDrawCreate (dpy, cur_mon->pixmap, visual_ptr , colormap))) {
fprintf(stderr, "Couldn't create xft drawable\n");
}
for (;;) {
if (*p == '\0' || *p == '\n')
return;
break;
if (p[0] == '%' && p[1] == '{' && (block_end = strchr(p++, '}'))) {
p++;
@ -553,6 +654,15 @@ parse (char *text)
{ cur_mon = monhead; }
else if (*p == 'l')
{ 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))
{ cur_mon = monhead;
for (int i = 0; i != *p-'0' && cur_mon->next; i++)
@ -560,9 +670,14 @@ parse (char *text)
}
else
{ 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++;
pos_x = 0;
fill_rect(cur_mon->pixmap, gc[GC_CLEAR], 0, 0, cur_mon->width, bh);
break;
case 'O':
errno = 0;
@ -649,14 +764,17 @@ parse (char *text)
if (!cur_font)
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);
pos_x += w;
area_shift(cur_mon->window, align, w);
}
}
XftDrawDestroy (xft_draw);
}
void
@ -674,39 +792,60 @@ font_load (const char *pattern)
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));
if (!ret)
return;
queryreq = xcb_query_font(c, font);
font_info = xcb_query_font_reply(c, queryreq, NULL);
cookie = xcb_open_font_checked(c, font, strlen(pattern), pattern);
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->descent = 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_min = font_info->min_byte1 << 8 | font_info->min_char_or_byte2;
// 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);
if (lut_size) {
ret->width_lut = malloc(lut_size);
memcpy(ret->width_lut, xcb_query_font_char_infos(font_info), lut_size);
ret->xft_ft = NULL;
ret->ptr = font;
ret->descent = 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_min = font_info->min_byte1 << 8 | font_info->min_char_or_byte2;
// 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);
if (lut_size) {
ret->width_lut = malloc(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;
}
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 {
NET_WM_WINDOW_TYPE,
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_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], 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_CLASS, XCB_ATOM_STRING, 8, 12, "lemonbar\0Bar");
}
}
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;
@ -783,18 +925,20 @@ monitor_new (int x, int y, int width, int height)
exit(EXIT_FAILURE);
}
ret->name = name;
ret->x = x;
ret->y = (topbar ? by : height - bh - by) + y;
ret->width = width;
ret->next = ret->prev = NULL;
ret->window = xcb_generate_id(c);
int depth = (visual == scr->root_visual) ? XCB_COPY_FROM_PARENT : 32;
xcb_create_window(c, depth, ret->window, scr->root,
ret->x, ret->y, width, bh, 0,
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,
(const uint32_t []){ bgc.v, bgc.v, dock, XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_PRESS, colormap });
ret->x, ret->y, width, bh, 0,
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,
(const uint32_t []) {
bgc.v, bgc.v, dock, XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_PRESS, colormap
});
ret->pixmap = xcb_generate_id(c);
xcb_create_pixmap(c, depth, ret->pixmap, ret->window, width, bh);
@ -819,17 +963,17 @@ monitor_add (monitor_t *mon)
}
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 xcb_rectangle_t *r2 = (xcb_rectangle_t *)p2;
const monitor_t *m1 = (monitor_t *)p1;
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;
}
if (r1->x > r2->x || r1->y + r1->height > r2->y)
if (m1->x > m2->x || m1->y + m1->height > m2->y)
{
return 1;
}
@ -838,22 +982,23 @@ rect_sort_cb (const void *p1, const void *p2)
}
void
monitor_create_chain (xcb_rectangle_t *rects, const int num)
monitor_create_chain (monitor_t *mons, const int num)
{
int i;
int width = 0, height = 0;
int left = bx;
// 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++) {
int h = rects[i].y + rects[i].height;
int h = mons[i].y + mons[i].height;
// Accumulated width of all monitors
width += rects[i].width;
width += mons[i].width;
// Get height of screen from y_offset + height of lowest monitor
if (h >= height)
height = h;
height = h;
}
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
width = bw;
for (i = 0; i < num; i++) {
if (rects[i].y + rects[i].height < by)
if (mons[i].y + mons[i].height < by)
continue;
if (rects[i].width > left) {
if (mons[i].width > left) {
monitor_t *mon = monitor_new(
rects[i].x + left,
rects[i].y,
min(width, rects[i].width - left),
rects[i].height);
mons[i].x + left,
mons[i].y,
min(width, mons[i].width - left),
mons[i].height,
mons[i].name);
if (!mon)
break;
monitor_add(mon);
width -= rects[i].width - left;
width -= mons[i].width - left;
// No need to check for other monitors
if (width <= 0)
break;
}
left -= rects[i].width;
left -= mons[i].width;
if (left < 0)
left = 0;
@ -908,7 +1054,7 @@ get_randr_monitors (void)
int i, j, num, valid = 0;
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) {
fprintf(stderr, "Failed to get current randr screen resources\n");
@ -925,26 +1071,27 @@ get_randr_monitors (void)
return;
}
xcb_rectangle_t rects[num];
monitor_t mons[num];
// Get all outputs
for (i = 0; i < num; i++) {
xcb_randr_get_output_info_reply_t *oi_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);
// Output disconnected or not attached to any CRTC ?
if (!oi_reply || oi_reply->crtc == XCB_NONE || oi_reply->connection != XCB_RANDR_CONNECTION_CONNECTED) {
free(oi_reply);
rects[i].width = 0;
mons[i].width = 0;
continue;
}
ci_reply = xcb_randr_get_crtc_info_reply(c,
xcb_randr_get_crtc_info(c, oi_reply->crtc, XCB_CURRENT_TIME), NULL);
free(oi_reply);
xcb_randr_get_crtc_info(c, oi_reply->crtc, XCB_CURRENT_TIME), NULL);
if (!ci_reply) {
fprintf(stderr, "Failed to get RandR ctrc info\n");
@ -952,9 +1099,45 @@ get_randr_monitors (void)
return;
}
// There's no need to handle rotated screens here (see #69)
rects[i] = (xcb_rectangle_t){ ci_reply->x, ci_reply->y, ci_reply->width, ci_reply->height };
namelen = xcb_randr_get_output_info_name_length(oi_reply);
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);
valid++;
@ -962,36 +1145,15 @@ get_randr_monitors (void)
free(rres_reply);
// Check for clones and inactive outputs
for (i = 0; i < num; i++) {
if (rects[i].width == 0)
continue;
if (num_outputs)
free(output_names);
for (j = 0; j < num; j++) {
// 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) {
if (valid == 0) {
fprintf(stderr, "No usable RandR output found\n");
return;
}
xcb_rectangle_t r[valid];
for (i = j = 0; i < num && j < valid; i++)
if (rects[i].width != 0)
r[j++] = rects[i];
monitor_create_chain(r, valid);
monitor_create_chain(mons, valid);
}
#ifdef WITH_XINERAMA
@ -1002,48 +1164,53 @@ get_xinerama_monitors (void)
xcb_xinerama_screen_info_iterator_t iter;
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,
xcb_xinerama_query_screens_unchecked(c), NULL);
xcb_xinerama_query_screens_unchecked(c), NULL);
iter = xcb_xinerama_query_screens_screen_info_iterator(xqs_reply);
screens = iter.rem;
xcb_rectangle_t rects[screens];
monitor_t mons[screens];
// Fetch all the screens first
for (int i = 0; iter.rem; i++) {
rects[i].x = iter.data->x_org;
rects[i].y = iter.data->y_org;
rects[i].width = iter.data->width;
rects[i].height = iter.data->height;
mons[i].name = NULL;
mons[i].x = iter.data->x_org;
mons[i].y = iter.data->y_org;
mons[i].width = iter.data->width;
mons[i].height = iter.data->height;
xcb_xinerama_screen_info_next(&iter);
}
free(xqs_reply);
monitor_create_chain(rects, screens);
monitor_create_chain(mons, screens);
}
#endif
xcb_visualid_t
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
while (iter.rem) {
xcb_visualtype_t *vis = xcb_depth_visuals(iter.data);
if (iter.data->depth == 32)
return vis->visual_id;
xcb_depth_next(&iter);
if (result > 0) {
visual_ptr = result_ptr->visual;
return result_ptr->visualid;
}
// Fallback to the default one
return scr->root_visual;
//Fallback
visual_ptr = DefaultVisual(dpy, scr_nbr);
return scr->root_visual;
}
// 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;
}
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
xconn (void)
{
// Connect to X
c = xcb_connect (NULL, NULL);
if ((dpy = XOpenDisplay(0)) == 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)) {
fprintf(stderr, "Couldn't connect to X\n");
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;
// Try to get a RGBA visual and build the colormap for that
visual = get_visual();
/* Try to get a RGBA visual and build the colormap for that */
visual = get_visual();
colormap = xcb_generate_id(c);
xcb_create_colormap(c, XCB_COLORMAP_ALLOC_NONE, colormap, scr->root, visual);
}
void
init (char *wm_name)
init (char *wm_name, char *wm_instance)
{
// Try to load a default font
if (!font_count)
@ -1167,6 +1356,10 @@ init (char *wm_name)
#endif
if (!monhead) {
if (num_outputs) {
fprintf(stderr, "Failed to find any specified outputs\n");
exit(EXIT_FAILURE);
}
// If I fits I sits
if (bw < 0)
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
monhead = monitor_new(0, 0, bw, scr->height_in_pixels);
monhead = monitor_new(0, 0, bw, scr->height_in_pixels, NULL);
}
if (!monhead)
@ -1213,8 +1406,33 @@ init (char *wm_name)
// 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);
// 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);
}
@ -1222,10 +1440,14 @@ void
cleanup (void)
{
free(area_stack.area);
for (int i = 0; i < font_count; i++) {
xcb_close_font(c, font_list[i]->ptr);
free(font_list[i]->width_lut);
for (int i = 0; font_list[i]; i++) {
if (font_list[i]->xft_ft) {
XftFontClose (dpy, font_list[i]->xft_ft);
}
else {
xcb_close_font(c, font_list[i]->ptr);
free(font_list[i]->width_lut);
}
free(font_list[i]);
}
@ -1237,7 +1459,7 @@ cleanup (void)
monhead = next;
}
xcb_free_colormap(c, colormap);
XftColorFree(dpy, visual_ptr, colormap, &sel_fg);
if (gc[GC_DRAW])
xcb_free_gc(c, gc[GC_DRAW]);
@ -1249,6 +1471,21 @@ cleanup (void)
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
sighandle (int signal)
{
@ -1256,6 +1493,7 @@ sighandle (int signal)
exit(EXIT_SUCCESS);
}
int
main (int argc, char **argv)
{
@ -1271,6 +1509,7 @@ main (int argc, char **argv)
int geom_v[4] = { -1, -1, 0, 0 };
int ch, areas;
char *wm_name;
char *instance_name;
// Install the parachute!
atexit(cleanup);
@ -1280,20 +1519,23 @@ main (int argc, char **argv)
// B/W combo
dbgc = bgc = (rgba_t)0x00000000U;
dfgc = fgc = (rgba_t)0xffffffffU;
dugc = ugc = fgc;
// A safe default
areas = 10;
wm_name = NULL;
instance_name = strip_path(argv[0]);
// Connect to the Xserver and initialize scr
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) {
case 'h':
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-g Set the bar geometry {width}x{height}+{xoffset}+{yoffset}\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-u Set the underline/overline height in pixels\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);
case 'g': (void)parse_geometry_string(optarg, geom_v); break;
case 'p': permanent = true; break;
@ -1313,6 +1556,7 @@ main (int argc, char **argv)
case 'd': dock = true; break;
case 'f': font_load(optarg); 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 'F': dfgc = fgc = parse_color(optarg, NULL, (rgba_t)0xffffffffU); break;
case 'U': dugc = ugc = parse_color(optarg, NULL, fgc); break;
@ -1342,12 +1586,13 @@ main (int argc, char **argv)
by = geom_v[3];
// Do the heavy lifting
init(wm_name);
init(wm_name, instance_name);
// The string is strdup'd when the command line arguments are parsed
free(wm_name);
// The string is strdup'd when stripping argv[0]
free(instance_name);
// Get the fd to Xserver
pollin[1].fd = xcb_get_file_descriptor(c);
for (;;) {
bool redraw = false;
@ -1386,7 +1631,7 @@ main (int argc, char **argv)
(void)write(STDOUT_FILENO, "\n", 1);
}
}
break;
break;
}
free(ev);