#include #include #include #ifdef XINERAMA #include #endif #include #include #define default_bar_height 18 #define bar_padding 6 #define max_status_len 512 #define max_title_len 512 #define max_tags 9 #define max_monitors 16 struct bar_monitor { Window window; int x; int y; int width; int height; int bar_width; }; static Display *bar_display; static Window bar_root; static GC bar_gc; static XFontStruct *bar_font; static int bar_screen; static int bar_selected_tag; static int bar_selected_tags[max_monitors]; static int bar_tag_count; static int bar_monitor_count; static int bar_height; static int bar_show_empty = 1; static int bar_workspace_usage[max_monitors][max_tags]; static int bar_margin_x; static int bar_margin_y; static int bar_is_bottom; static unsigned long bar_fg_color; static unsigned long bar_bg_color; static unsigned long bar_sel_fg_color; static unsigned long bar_sel_bg_color; static unsigned long bar_sel_accent_color; static int bar_tags[max_tags]; static char bar_status[max_status_len]; static char bar_title[max_title_len]; static struct bar_monitor bar_monitors[max_monitors]; void bar_reconfigure(void); void bar_draw(void); static unsigned long bar_alloc_color(const char *color_name, unsigned long fallback); static XFontStruct * bar_load_font(const char *font_name) { XFontStruct *font; char family[256]; int i; if (bar_display == NULL || font_name == NULL || font_name[0] == '\0') return NULL; font = XLoadQueryFont(bar_display, font_name); if (font != NULL) return font; for (i = 0; font_name[i] != '\0' && font_name[i] != ':' && i < (int)sizeof(family) - 1; i++) family[i] = font_name[i]; family[i] = '\0'; if (i > 0) return XLoadQueryFont(bar_display, family); return NULL; } static int bar_text_width(const char *text) { if (bar_font == NULL || text == NULL) return 0; return XTextWidth(bar_font, text, (int)strlen(text)); } static int bar_text_baseline(void) { int text_height; int top_pad; if (bar_font == NULL) return bar_height - 5; text_height = bar_font->ascent + bar_font->descent; if (text_height < 1) return bar_height - 5; top_pad = (bar_height - text_height) / 2; if (top_pad < 0) top_pad = 0; return top_pad + bar_font->ascent; } static void bar_draw_rect(Window window, unsigned long color, int x, int y, int width, int height) { XSetForeground(bar_display, bar_gc, color); XFillRectangle(bar_display, window, bar_gc, x, y, (unsigned int)width, (unsigned int)height); } static void bar_draw_text(Window window, unsigned long fg, int x, int y, const char *text) { XSetForeground(bar_display, bar_gc, fg); XDrawString(bar_display, window, bar_gc, x, y, text, (int)strlen(text)); } static void bar_destroy_windows(void) { int i; for (i = 0; i < bar_monitor_count; i++) { if (bar_monitors[i].window != None) XDestroyWindow(bar_display, bar_monitors[i].window); bar_monitors[i].window = None; } bar_monitor_count = 0; } static int bar_load_monitors(void) { XWindowAttributes root_attrs; if (!XGetWindowAttributes(bar_display, bar_root, &root_attrs)) return 0; bar_monitor_count = 1; bar_monitors[0].x = 0; bar_monitors[0].y = 0; bar_monitors[0].width = root_attrs.width; bar_monitors[0].height = root_attrs.height; #ifdef XINERAMA { XineramaScreenInfo *screen_info; int j, major, minor; int i; if (XineramaQueryExtension(bar_display, &major, &minor) && XineramaIsActive(bar_display)) { screen_info = XineramaQueryScreens(bar_display, &j); if (screen_info != None && j > 0) { bar_monitor_count = j; if (bar_monitor_count > max_monitors) bar_monitor_count = max_monitors; for (i = 0; i < bar_monitor_count; i++) { bar_monitors[i].x = screen_info[i].x_org; bar_monitors[i].y = screen_info[i].y_org; bar_monitors[i].width = screen_info[i].width; bar_monitors[i].height = screen_info[i].height; } XFree(screen_info); } } } #endif return bar_monitor_count; } static void bar_create_windows(void) { Atom net_wm_window_type; Atom net_wm_window_type_dock; XSetWindowAttributes attrs; int i, window_x, window_y, window_width; XClassHint class_hint; char class_name[] = "wmbar"; char class_class[] = "wmbar"; net_wm_window_type = XInternAtom(bar_display, "_NET_WM_WINDOW_TYPE", False); net_wm_window_type_dock = XInternAtom(bar_display, "_NET_WM_WINDOW_TYPE_DOCK", False); class_hint.res_name = class_name; class_hint.res_class = class_class; attrs.override_redirect = True; attrs.background_pixel = bar_bg_color; attrs.event_mask = ExposureMask; for (i = 0; i < bar_monitor_count; i++) { window_x = bar_monitors[i].x + bar_margin_x; window_y = bar_monitors[i].y + bar_margin_y; window_width = bar_monitors[i].width - (bar_margin_x * 2); if (window_width < 1) window_width = 1; if (bar_is_bottom) window_y = bar_monitors[i].y + bar_monitors[i].height - bar_height - bar_margin_y; if (window_y < bar_monitors[i].y) window_y = bar_monitors[i].y; bar_monitors[i].bar_width = window_width; bar_monitors[i].window = XCreateWindow(bar_display, bar_root, window_x, window_y, (unsigned int)window_width, bar_height, 0, DefaultDepth(bar_display, bar_screen), CopyFromParent, DefaultVisual(bar_display, bar_screen), CWOverrideRedirect | CWBackPixel | CWEventMask, &attrs); XSetClassHint(bar_display, bar_monitors[i].window, &class_hint); XChangeProperty(bar_display, bar_monitors[i].window, net_wm_window_type, XA_ATOM, 32, PropModeReplace, (unsigned char *)&net_wm_window_type_dock, 1); XMapRaised(bar_display, bar_monitors[i].window); } } int bar_init(Display *display, Window root_window) { bar_display = display; bar_root = root_window; bar_screen = DefaultScreen(display); bar_height = default_bar_height; bar_font = XLoadQueryFont(display, "fixed"); if (bar_font == NULL) return 0; bar_gc = XCreateGC(display, root_window, 0, NULL); XSetFont(display, bar_gc, bar_font->fid); bar_fg_color = bar_alloc_color("#222222", BlackPixel(display, bar_screen)); bar_bg_color = bar_alloc_color("#ffffff", WhitePixel(display, bar_screen)); bar_sel_fg_color = bar_alloc_color("#111111", BlackPixel(display, bar_screen)); bar_sel_bg_color = bar_alloc_color("#dfe8ff", WhitePixel(display, bar_screen)); bar_sel_accent_color = bar_alloc_color("#005fcc", BlackPixel(display, bar_screen)); if (!bar_load_monitors()) return 0; bar_create_windows(); XFlush(display); return 1; } void bar_set_status(const char *status) { if (status == NULL) status = ""; snprintf(bar_status, sizeof(bar_status), "%s", status); } void bar_set_tags(const int *tags, int count, int selected_tag) { int i; bar_tag_count = count > max_tags ? max_tags : count; bar_selected_tag = selected_tag; for (i = 0; i < max_monitors; i++) bar_selected_tags[i] = selected_tag; for (i = 0; i < bar_tag_count; i++) bar_tags[i] = tags[i]; } void bar_set_selected_tags(const int *selected_tags, int count) { int i; int max_count; if (selected_tags == NULL || count < 1) return; max_count = count; if (max_count > max_monitors) max_count = max_monitors; for (i = 0; i < max_count; i++) bar_selected_tags[i] = selected_tags[i]; for (; i < max_monitors; i++) bar_selected_tags[i] = bar_selected_tags[0]; } void bar_set_show_empty(int show_empty) { bar_show_empty = show_empty ? 1 : 0; bar_draw(); } void bar_set_workspace_usage(const int *usage, int monitor_count, int workspace_count) { int m, w; int mc, wc; if (usage == NULL) return; mc = monitor_count; wc = workspace_count; if (mc < 1) mc = 1; if (mc > max_monitors) mc = max_monitors; if (wc < 1) wc = 1; if (wc > max_tags) wc = max_tags; for (m = 0; m < max_monitors; m++) for (w = 0; w < max_tags; w++) bar_workspace_usage[m][w] = 0; for (m = 0; m < mc; m++) for (w = 0; w < wc; w++) bar_workspace_usage[m][w] = usage[m * wc + w] ? 1 : 0; } void bar_set_title(const char *title) { if (title == NULL) title = ""; snprintf(bar_title, sizeof(bar_title), "%s", title); } void bar_draw(void) { int m, x, i, tw, tagw, title_x, status_x, bar_width; char tag_text[8]; int baseline; if (bar_display == NULL || bar_monitor_count < 1) return; baseline = bar_text_baseline(); for (m = 0; m < bar_monitor_count; m++) { int selected_tag; if (bar_monitors[m].window == None) continue; selected_tag = bar_selected_tags[m]; bar_width = bar_monitors[m].bar_width; bar_draw_rect(bar_monitors[m].window, bar_bg_color, 0, 0, bar_width, bar_height); x = 0; for (i = 0; i < bar_tag_count; i++) { int show_tag; show_tag = bar_show_empty || i == selected_tag || bar_workspace_usage[m][i]; if (!show_tag) continue; snprintf(tag_text, sizeof(tag_text), "%d", bar_tags[i]); tw = bar_text_width(tag_text); tagw = tw + (bar_padding * 2); if (i == selected_tag) { bar_draw_rect(bar_monitors[m].window, bar_sel_bg_color, x, 0, tagw, bar_height); bar_draw_text(bar_monitors[m].window, bar_sel_accent_color, x + bar_padding, baseline, tag_text); } else { bar_draw_text(bar_monitors[m].window, bar_fg_color, x + bar_padding, baseline, tag_text); } x += tagw + bar_padding; } status_x = bar_width - bar_padding - bar_text_width(bar_status); if (status_x < x + bar_padding) status_x = x + bar_padding; bar_draw_text(bar_monitors[m].window, bar_fg_color, status_x, baseline, bar_status); title_x = x + bar_padding; tw = bar_text_width(bar_title); if (title_x + tw < status_x - bar_padding) bar_draw_text(bar_monitors[m].window, bar_sel_fg_color, title_x, baseline, bar_title); } XFlush(bar_display); } void bar_set_margins(int margin_x, int margin_y) { if (margin_x < 0) margin_x = bar_margin_x; if (margin_y < 0) margin_y = bar_margin_y; if (margin_x < 0) margin_x = 0; if (margin_y < 0) margin_y = 0; bar_margin_x = margin_x; bar_margin_y = margin_y; bar_reconfigure(); } void bar_set_bottom(int is_bottom) { bar_is_bottom = is_bottom ? 1 : 0; bar_reconfigure(); } void bar_set_height(int height) { if (height < 8) height = 8; bar_height = height; bar_reconfigure(); } void bar_set_font(const char *font_name) { XFontStruct *new_font; new_font = bar_load_font(font_name); if (new_font == NULL) return; if (bar_font != NULL) XFreeFont(bar_display, bar_font); bar_font = new_font; if (bar_gc != None) XSetFont(bar_display, bar_gc, bar_font->fid); bar_draw(); } static unsigned long bar_alloc_color(const char *color_name, unsigned long fallback) { Colormap cmap; XColor color; if (bar_display == NULL || color_name == NULL || color_name[0] == '\0') return fallback; cmap = DefaultColormap(bar_display, bar_screen); if (!XParseColor(bar_display, cmap, color_name, &color)) return fallback; if (!XAllocColor(bar_display, cmap, &color)) return fallback; return color.pixel; } void bar_set_colors(const char *fg, const char *bg, const char *sel_fg, const char *sel_bg, const char *sel_accent) { if (fg != NULL) bar_fg_color = bar_alloc_color(fg, bar_fg_color); if (bg != NULL) bar_bg_color = bar_alloc_color(bg, bar_bg_color); if (sel_fg != NULL) bar_sel_fg_color = bar_alloc_color(sel_fg, bar_sel_fg_color); if (sel_bg != NULL) bar_sel_bg_color = bar_alloc_color(sel_bg, bar_sel_bg_color); if (sel_accent != NULL) bar_sel_accent_color = bar_alloc_color(sel_accent, bar_sel_accent_color); bar_draw(); } void bar_reconfigure(void) { if (bar_display == NULL) return; bar_destroy_windows(); if (!bar_load_monitors()) return; bar_create_windows(); bar_draw(); } void bar_cleanup(void) { if (bar_display == NULL) return; bar_destroy_windows(); if (bar_gc != None) XFreeGC(bar_display, bar_gc); if (bar_font != NULL) XFreeFont(bar_display, bar_font); bar_gc = None; bar_font = NULL; bar_display = NULL; bar_root = None; }