diff --git a/bar.c b/bar.c index 2affcff..44a9686 100644 --- a/bar.c +++ b/bar.c @@ -12,176 +12,219 @@ // Here be dragons -static xcb_connection_t *c; -static xcb_window_t root, win; -static xcb_gcontext_t gc; -static xcb_drawable_t canvas; -static int font_height, font_width, font_descent; -static int bar_width; -static const unsigned pal[] = {COLOR0,COLOR1,COLOR2,COLOR3,COLOR4,COLOR5,COLOR6,COLOR7,COLOR8,COLOR9}; +#define MAX(a,b) ((a > b) ? a : b) -#define MIN(a,b) ((a > b ? b : a)) +typedef struct fontset_item_t { + xcb_font_t xcb_ft; + int height; + int width; + int descent; + unsigned short char_max; + unsigned short char_min; +} fontset_item_t; + +enum { + FONT_MAIN, + FONT_FALLBACK, + FONT_MAX +}; + +static xcb_connection_t *c; +static xcb_window_t win; +static xcb_drawable_t canvas; +static xcb_gcontext_t draw_gc; +static xcb_gcontext_t clear_gc; +static xcb_gcontext_t underl_gc; +static int bar_width; +static fontset_item_t fontset[FONT_MAX]; +static fontset_item_t *sel_font = &fontset[FONT_MAIN]; + +static const unsigned palette[] = {COLOR0,COLOR1,COLOR2,COLOR3,COLOR4,COLOR5,COLOR6,COLOR7,COLOR8,COLOR9}; void -fill_rect (int color, int x, int y, int width, int height) +xcb_set_bg (int i) +{ + xcb_change_gc (c, draw_gc , XCB_GC_BACKGROUND, (const unsigned []){ palette[i] }); + xcb_change_gc (c, clear_gc , XCB_GC_FOREGROUND, (const unsigned []){ palette[i] }); + xcb_change_gc (c, underl_gc, XCB_GC_FOREGROUND, (const unsigned []){ palette[i] }); +} + +void +xcb_set_fg (int i) +{ + xcb_change_gc (c, draw_gc , XCB_GC_FOREGROUND, (const unsigned []){ palette[i] }); +} + +void +xcb_set_ud (int i) +{ + xcb_change_gc (c, underl_gc, XCB_GC_FOREGROUND, (const unsigned []){ palette[i] }); +} + +void +xcb_fill_rect (xcb_gcontext_t gc, int x, int y, int width, int height) { - xcb_change_gc (c, gc, XCB_GC_FOREGROUND, (const unsigned []){ pal[color] }); xcb_poly_fill_rectangle (c, canvas, gc, 1, (const xcb_rectangle_t []){ { x, y, width, height } }); } -/* libc doesn't give a shit to -fshort-wchar, so here's our wcslen */ -size_t -wcslen_ (wchar_t *s) { - size_t len; - - for (len = 0; *s; s++, len++); - - return len; +void +xcb_set_fontset (int i) +{ + sel_font = &fontset[i&1]; + xcb_change_gc (c, draw_gc , XCB_GC_FONT, (const unsigned []){ sel_font->xcb_ft }); } int -draw_string (int x, int align, int fgcol, int bgcol, int udcol, wchar_t *text) +draw_char (int x, int align, wchar_t ch) { - int chunk_len; - int chars_done; - int pos_x; - int str_lenght; - int str_width; - - pos_x = x; - chars_done = 0; - - str_lenght = MIN(bar_width / font_width, wcslen_ (text)); - str_width = str_lenght * font_width; - - if (str_width == 0) - return 0; - switch (align) { case 1: - xcb_copy_area (c, canvas, canvas, gc, bar_width / 2 - pos_x / 2, 0, - bar_width / 2 - (pos_x + str_width) / 2, 0, pos_x, BAR_HEIGHT); - pos_x = bar_width / 2 - (pos_x + str_width) / 2 + pos_x; + xcb_copy_area (c, canvas, canvas, draw_gc, bar_width / 2 - x / 2, 0, + bar_width / 2 - (x + sel_font->width) / 2, 0, x, BAR_HEIGHT); + x = bar_width / 2 - (x + sel_font->width) / 2 + x; break; case 2: - xcb_copy_area (c, canvas, canvas, gc, bar_width - pos_x, 0, - bar_width - pos_x - str_width, 0, pos_x, BAR_HEIGHT); - pos_x = bar_width - str_width; + xcb_copy_area (c, canvas, canvas, draw_gc, bar_width - x, 0, + bar_width - x - sel_font->width, 0, x, BAR_HEIGHT); + x = bar_width - sel_font->width; break; } /* Draw the background first */ - fill_rect (bgcol, pos_x, 0, str_width, BAR_HEIGHT); + xcb_fill_rect (clear_gc, x, 0, sel_font->width, BAR_HEIGHT); /* Draw the underline */ if (BAR_UNDERLINE_HEIGHT) - fill_rect (udcol, pos_x, BAR_HEIGHT-BAR_UNDERLINE_HEIGHT, str_width, BAR_UNDERLINE_HEIGHT); + xcb_fill_rect (underl_gc, x, BAR_HEIGHT-BAR_UNDERLINE_HEIGHT, sel_font->width, BAR_UNDERLINE_HEIGHT); - /* Setup the colors */ - xcb_change_gc (c, gc, XCB_GC_FOREGROUND, (const unsigned []){ pal[fgcol] }); - xcb_change_gc (c, gc, XCB_GC_BACKGROUND, (const unsigned []){ pal[bgcol] }); + /* xcb accepts string in UCS-2 BE, so swap */ + ch = (ch >> 8) | (ch << 8); - do { - chunk_len = MIN(str_lenght - chars_done, 255); - /* String baseline coordinates */ - xcb_image_text_16 (c, chunk_len, canvas, gc, pos_x, - BAR_HEIGHT / 2 + font_height / 2 - font_descent, (xcb_char2b_t *)text + chars_done); - chars_done += chunk_len; - pos_x = chars_done * font_width; - } while (chars_done < str_lenght); + /* String baseline coordinates */ + xcb_image_text_16 (c, 1, canvas, draw_gc, x, BAR_HEIGHT / 2 + sel_font->height / 2 - sel_font->descent, + (xcb_char2b_t *)&ch); - return pos_x; + return sel_font->width; } void parse (char *text) { - wchar_t parsed_text[2048] = {0, }; - - wchar_t *q = parsed_text; - char *p = text; + unsigned char *p = text; int pos_x = 0; int align = 0; - int fgcol = 1; - int bgcol = 0; - int udcol = 0; - - fill_rect (0, 0, 0, bar_width, BAR_HEIGHT); + xcb_fill_rect (clear_gc, 0, 0, bar_width, BAR_HEIGHT); for (;;) { - if (*p == 0x0 || *p == 0xA || (*p == '\\' && p++ && *p != '\\' && strchr ("fbulcr", *p))) { - pos_x += draw_string (pos_x, align, fgcol, bgcol, udcol, parsed_text); - switch (*p++) { - case 0x0: /* EOL */ - case 0xA: /* NL */ - return; + if (*p == '\0') + return; + if (*p == '\n') + return; - case 'f': - if (*p == 'r') *p = '1'; - if (isdigit (*p)) fgcol = (*p++)-'0'; - break; - case 'b': - if (*p == 'r') *p = '0'; - if (isdigit (*p)) bgcol = (*p++)-'0'; - break; - case 'u': - if (*p == 'r') *p = '0'; - if (isdigit (*p)) udcol = (*p++)-'0'; - break; + if (*p == '\\' && p++ && *p != '\\' && strchr ("fbulcr", *p)) { + switch (*p++) { + case 'f': + if (!isdigit (*p)) *p = '1'; + xcb_set_fg ((*p++)-'0'); + break; + case 'b': + if (!isdigit (*p)) *p = '0'; + xcb_set_bg ((*p++)-'0'); + break; + case 'u': + if (!isdigit (*p)) *p = '0'; + xcb_set_ud ((*p++)-'0'); + break; - case 'l': - align = 0; - pos_x = 0; - break; - case 'c': - align = 1; - pos_x = 0; - break; - case 'r': - align = 2; - pos_x = 0; - break; - } - q = parsed_text; + case 'l': + align = 0; + pos_x = 0; + break; + case 'c': + align = 1; + pos_x = 0; + break; + case 'r': + align = 2; + pos_x = 0; + break; + } } else { /* utf-8 -> ucs-2 */ + wchar_t t; + if (!(p[0] & 0x80)) { - *q++ = p[0] << 8; - p += 1; + t = p[0]; + p += 1; } else if ((p[0] & 0xe0) == 0xc0 && (p[1] & 0xc0) == 0x80) { - wchar_t t = (p[0] & 0x1f) << 6 | p[1] & 0x3f; - *q++ = (t >> 8) | (t << 8); - p += 2; + t = (p[0] & 0x1f) << 6 | p[1] & 0x3f; + p += 2; } else if ((p[0] & 0xf0) == 0xe0 && (p[1] & 0xc0) == 0x80 && (p[2] & 0xc0) == 0x80) { - wchar_t t = (p[0] & 0xf) << 12 | (p[1] & 0x3f) << 6 | p[2] & 0x3f; - *q++ = (t >> 8) | (t << 8); - p += 3; + t = (p[0] & 0xf) << 12 | (p[1] & 0x3f) << 6 | p[2] & 0x3f; + p += 3; } + else { /* ASCII chars > 127 go in the extended latin range */ + t = 0xc200 + p[0]; + p += 1; + } + + /* The character is outside the main font charset, use the fallback */ + if (t < fontset[FONT_MAIN].char_min || t > fontset[FONT_MAIN].char_max) + xcb_set_fontset (FONT_FALLBACK); + else + xcb_set_fontset (FONT_MAIN); + + pos_x += draw_char (pos_x, align, t); } - *q = 0; } } -void -cleanup (void) +int +font_load (const char **font_list) { - xcb_free_pixmap (c, canvas); - xcb_destroy_window (c, win); - xcb_free_gc (c, gc); - xcb_disconnect (c); -} + xcb_query_font_cookie_t queryreq; + xcb_query_font_reply_t *font_info; + xcb_void_cookie_t cookie; + xcb_font_t font; + int max_height; -void -sighandle (int signal) -{ - if (signal == SIGINT || signal == SIGTERM) exit (0); + max_height = -1; + + for (int i = 0; i < FONT_MAX; i++) { + font = xcb_generate_id (c); + + cookie = xcb_open_font_checked (c, font, strlen (font_list[i]), font_list[i]); + if (xcb_request_check (c, cookie)) { + fprintf (stderr, "Could not load font %s\n", font_list[i]); + return 1; + } + + queryreq = xcb_query_font (c, font); + font_info = xcb_query_font_reply (c, queryreq, NULL); + + fontset[i].xcb_ft = font; + fontset[i].width = font_info->max_bounds.character_width; + fontset[i].descent = font_info->font_descent; + fontset[i].char_max= font_info->max_byte1 << 8 | font_info->max_char_or_byte2; + fontset[i].char_min= font_info->min_byte1 << 8 | font_info->min_char_or_byte2; + + max_height = MAX(font_info->font_ascent + font_info->font_descent, max_height); + + free (font_info); + } + + /* To have an uniform alignment */ + for (int i = 0; i < FONT_MAX; i++) + fontset[i].height = max_height; + + return 0; } int -set_ewmh_atoms (void) +set_ewmh_atoms (xcb_window_t root) { xcb_intern_atom_cookie_t cookies[5]; xcb_intern_atom_reply_t *reply; @@ -206,7 +249,6 @@ set_ewmh_atoms (void) reply = xcb_intern_atom_reply (c, cookies[4], NULL); atoms[4] = reply->atom; free (reply); - /* Wait until the wm is spawned */ do { reply1 = xcb_get_property_reply (c, xcb_get_property (c, 0, root, atoms[4], XCB_ATOM_ATOM, 0, -1), NULL); } while (!xcb_get_property_value_length (reply1)); @@ -214,7 +256,7 @@ set_ewmh_atoms (void) compliance_lvl = 0; for (xcb_atom_t *a = xcb_get_property_value (reply1); - a != xcb_get_property_value_end (reply1).data; + a && a != xcb_get_property_value_end (reply1).data; a++) { /* Set the _NET_WM_WINDOW_TYPE_DOCK state */ @@ -239,6 +281,7 @@ set_ewmh_atoms (void) compliance_lvl++; } } + free (reply1); /* If the wm supports at least 2 NET atoms then mark as compliant */ return (compliance_lvl > 1); @@ -247,9 +290,8 @@ set_ewmh_atoms (void) void init (void) { - xcb_font_t font; xcb_screen_t *scr; - xcb_query_font_reply_t *font_info; + xcb_window_t root; /* Connect to X */ c = xcb_connect (NULL, NULL); @@ -257,22 +299,16 @@ init (void) fprintf (stderr, "Couldn't connect to X\n"); exit (1); } + /* Grab infos from the first screen */ scr = xcb_setup_roots_iterator (xcb_get_setup (c)).data; bar_width = scr->width_in_pixels; root = scr->root; + /* Load the font */ - font = xcb_generate_id (c); - if (xcb_request_check (c, xcb_open_font_checked (c, font, strlen(BAR_FONT), BAR_FONT))) { - fprintf (stderr, "Couldn't load the font\n"); + if (font_load ((const char* []){ BAR_MAIN_FONT, BAR_FALLBACK_FONT })) exit (1); - } - /* Grab infos from the font */ - font_info = xcb_query_font_reply (c, xcb_query_font (c, font), NULL); - font_height = font_info->font_ascent + font_info->font_descent; - font_width = font_info->max_bounds.character_width; - font_descent = font_info->font_descent; - free (font_info); + /* Create the main window */ win = xcb_generate_id (c); xcb_create_window (c, XCB_COPY_FROM_PARENT, win, root, 0, @@ -282,29 +318,57 @@ init (void) 0, #endif bar_width, BAR_HEIGHT, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, scr->root_visual, - XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK, (const unsigned []){ pal[0], XCB_EVENT_MASK_EXPOSURE }); + XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK, (const unsigned []){ palette[0], XCB_EVENT_MASK_EXPOSURE }); + /* Set EWMH hints */ - int ewmh_docking = set_ewmh_atoms (); + int ewmh_docking = set_ewmh_atoms (root); + /* Quirk for wm not supporting the EWMH docking method */ xcb_change_window_attributes (c, win, XCB_CW_OVERRIDE_REDIRECT, (const unsigned []){ !ewmh_docking }); + /* Create a temporary canvas */ canvas = xcb_generate_id (c); xcb_create_pixmap (c, scr->root_depth, canvas, root, bar_width, BAR_HEIGHT); + /* Create the gc for drawing */ - gc = xcb_generate_id (c); - xcb_create_gc (c, gc, root, XCB_GC_FOREGROUND | XCB_GC_BACKGROUND | XCB_GC_FONT, - (const unsigned []){ pal[1], pal[0], font }); - /* Get rid of the font */ - xcb_close_font (c, font); + draw_gc = xcb_generate_id (c); + xcb_create_gc (c, draw_gc, root, XCB_GC_FOREGROUND | XCB_GC_BACKGROUND, (const unsigned []){ palette[1], palette[0] }); + + clear_gc = xcb_generate_id (c); + xcb_create_gc (c, clear_gc, root, XCB_GC_FOREGROUND, (const unsigned []){ palette[0] }); + + underl_gc = xcb_generate_id (c); + xcb_create_gc (c, underl_gc, root, XCB_GC_FOREGROUND, (const unsigned []){ palette[0] }); + /* Make the bar visible */ xcb_map_window (c, win); xcb_flush (c); } +void +cleanup (void) +{ + xcb_close_font (c, fontset[FONT_MAIN].xcb_ft); + xcb_close_font (c, fontset[FONT_FALLBACK].xcb_ft); + xcb_free_pixmap (c, canvas); + xcb_destroy_window (c, win); + xcb_free_gc (c, draw_gc); + xcb_free_gc (c, clear_gc); + xcb_free_gc (c, underl_gc); + xcb_disconnect (c); +} + +void +sighandle (int signal) +{ + if (signal == SIGINT || signal == SIGTERM) + exit (0); +} + int main (int argc, char **argv) { - static char input[1024] = {0, }; + char input[1024] = {0, }; struct pollfd pollin[2] = { { .fd = STDIN_FILENO, .events = POLLIN }, { .fd = -1 , .events = POLLIN }, @@ -335,7 +399,7 @@ main (int argc, char **argv) /* Get the fd to Xserver */ pollin[1].fd = xcb_get_file_descriptor (c); - fill_rect (0, 0, 0, bar_width, BAR_HEIGHT); + xcb_fill_rect (clear_gc, 0, 0, bar_width, BAR_HEIGHT); for (;;) { int redraw = 0; @@ -366,7 +430,8 @@ main (int argc, char **argv) } if (redraw) /* Copy our temporary pixmap onto the window */ - xcb_copy_area (c, canvas, win, gc, 0, 0, 0, 0, bar_width, BAR_HEIGHT); + xcb_copy_area (c, canvas, win, draw_gc, 0, 0, 0, 0, bar_width, BAR_HEIGHT); + xcb_flush (c); } diff --git a/config.def.h b/config.def.h index 45def9d..44a719b 100644 --- a/config.def.h +++ b/config.def.h @@ -5,7 +5,8 @@ /* Whether to put the bar at the screen bottom or not */ #define BAR_BOTTOM 0 /* The font used for the bar */ -#define BAR_FONT "-*-terminus-medium-r-normal-*-12-*-*-*-c-*-*-1" +#define BAR_MAIN_FONT "-*-terminus-medium-r-normal-*-12-*-*-*-c-*-*-1" +#define BAR_FALLBACK_FONT "fixed" /* Color palette */ #define COLOR0 0x1A1A1A /* background */ #define COLOR1 0xA9A9A9 /* foreground */